summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Allison <jra@samba.org>2004-06-08 16:14:31 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 10:51:54 -0500
commit2fc57c9a2ce3a266534dd20e6fed4883e052c557 (patch)
tree0b26f97c713129487ffcf34494119bc3dbe9176a
parent386a11f49eff2ec60d75b9966ecdc5627b259f0d (diff)
downloadsamba-2fc57c9a2ce3a266534dd20e6fed4883e052c557.tar.gz
samba-2fc57c9a2ce3a266534dd20e6fed4883e052c557.tar.bz2
samba-2fc57c9a2ce3a266534dd20e6fed4883e052c557.zip
r1085: Now it's had some proper user testing, merge in the deferred open fix. I'm
still doing more testing, but it fixes a behaviour that we've been wrong on ever since the start of Samba. Jeremy. (This used to be commit 894cc6d16296b934c112786eec896846156aee5d)
-rw-r--r--source3/client/client.c2
-rw-r--r--source3/include/local.h3
-rw-r--r--source3/include/smb.h62
-rw-r--r--source3/lib/time.c6
-rw-r--r--source3/locking/locking.c392
-rw-r--r--source3/printing/nt_printing.c12
-rw-r--r--source3/rpc_server/srv_srvsvc_nt.c10
-rw-r--r--source3/script/mkproto.awk2
-rw-r--r--source3/smbd/close.c30
-rw-r--r--source3/smbd/dir.c3
-rw-r--r--source3/smbd/nttrans.c8
-rw-r--r--source3/smbd/open.c200
-rw-r--r--source3/smbd/oplock.c70
-rw-r--r--source3/smbd/process.c217
-rw-r--r--source3/smbd/reply.c32
-rw-r--r--source3/smbd/trans2.c8
16 files changed, 974 insertions, 83 deletions
diff --git a/source3/client/client.c b/source3/client/client.c
index a0470315f8..63d73c2d4c 100644
--- a/source3/client/client.c
+++ b/source3/client/client.c
@@ -1571,7 +1571,7 @@ static int cmd_open(void)
}
pstrcat(mask,buf);
- cli_open(cli, mask, O_RDWR, DENY_ALL);
+ cli_nt_create(cli, mask, FILE_READ_DATA);
return 0;
}
diff --git a/source3/include/local.h b/source3/include/local.h
index 540365047a..ee8d672553 100644
--- a/source3/include/local.h
+++ b/source3/include/local.h
@@ -230,4 +230,7 @@
/* size of listen() backlog in smbd */
#define SMBD_LISTEN_BACKLOG 50
+/* Number of microseconds to wait before a sharing violation. */
+#define SHARING_VIOLATION_USEC_WAIT 950000
+
#endif
diff --git a/source3/include/smb.h b/source3/include/smb.h
index 54a69d1433..9100701e21 100644
--- a/source3/include/smb.h
+++ b/source3/include/smb.h
@@ -584,6 +584,24 @@ struct interface
struct in_addr nmask;
};
+/* struct used by share mode violation error processing */
+typedef struct {
+ pid_t pid;
+ uint16 mid;
+ struct timeval time;
+ SMB_DEV_T dev;
+ SMB_INO_T inode;
+ uint16 port;
+} deferred_open_entry;
+
+/* Internal message queue for deferred opens. */
+struct pending_message_list {
+ struct pending_message_list *next, *prev;
+ struct timeval msg_time; /* The timeout time */
+ DATA_BLOB buf;
+ DATA_BLOB private_data;
+};
+
/* struct returned by get_share_modes */
typedef struct {
pid_t pid;
@@ -663,28 +681,14 @@ struct locking_key {
SMB_INO_T inode;
};
-struct locking_data {
- union {
- int num_share_mode_entries;
- share_mode_entry dummy; /* Needed for alignment. */
- } u;
- /* the following two entries are implicit
- share_mode_entry modes[num_share_mode_entries];
- char file_name[];
- */
-};
-
-
/* the following are used by loadparm for option lists */
-typedef enum
-{
- P_BOOL,P_BOOLREV,P_CHAR,P_INTEGER,P_OCTAL,P_LIST,
- P_STRING,P_USTRING,P_GSTRING,P_UGSTRING,P_ENUM,P_SEP
+typedef enum {
+ P_BOOL,P_BOOLREV,P_CHAR,P_INTEGER,P_OCTAL,P_LIST,
+ P_STRING,P_USTRING,P_GSTRING,P_UGSTRING,P_ENUM,P_SEP
} parm_type;
-typedef enum
-{
- P_LOCAL,P_GLOBAL,P_SEPARATOR,P_NONE
+typedef enum {
+ P_LOCAL,P_GLOBAL,P_SEPARATOR,P_NONE
} parm_class;
/* passed to br lock code */
@@ -1410,6 +1414,7 @@ extern int chain_size;
#define EXCLUSIVE_OPLOCK 1
#define BATCH_OPLOCK 2
#define LEVEL_II_OPLOCK 4
+#define INTERNAL_OPEN_ONLY 8
#define EXCLUSIVE_OPLOCK_TYPE(lck) ((lck) & (EXCLUSIVE_OPLOCK|BATCH_OPLOCK))
#define BATCH_OPLOCK_TYPE(lck) ((lck) & BATCH_OPLOCK)
@@ -1461,6 +1466,25 @@ extern int chain_size;
#define LEVEL_II_OPLOCK_BREAK_CMD 0x3
#define ASYNC_LEVEL_II_OPLOCK_BREAK_CMD 0x4
+/* Add the "deferred open" message. */
+#define RETRY_DEFERRED_OPEN_CMD 0x5
+
+/*
+ * And the message format for it. Keep the same message length.
+ *
+ * 0 2 2+pid 2+pid+dev 2+pid+dev+ino
+ * +----+--------+-------+--------+---------+
+ * | cmd| pid | dev | inode | mid |
+ * +----+--------+-------+--------+---------+
+ */
+
+#define DEFERRED_OPEN_CMD_OFFSET 0
+#define DEFERRED_OPEN_PID_OFFSET 2 /* pid we're *sending* from. */
+#define DEFERRED_OPEN_DEV_OFFSET (DEFERRED_OPEN_PID_OFFSET + sizeof(pid_t))
+#define DEFERRED_OPEN_INODE_OFFSET (DEFERRED_OPEN_DEV_OFFSET + sizeof(SMB_DEV_T))
+#define DEFERRED_OPEN_MID_OFFSET (DEFERRED_OPEN_INODE_OFFSET + sizeof(SMB_INO_T))
+#define DEFERRED_OPEN_MSG_LEN OPLOCK_BREAK_MSG_LEN
+
/*
* Capabilities abstracted for different systems.
*/
diff --git a/source3/lib/time.c b/source3/lib/time.c
index faca2cba87..e63e0b2965 100644
--- a/source3/lib/time.c
+++ b/source3/lib/time.c
@@ -754,3 +754,9 @@ BOOL nt_time_is_zero(NTTIME *nt)
return True;
return False;
}
+
+SMB_BIG_INT usec_time_diff(struct timeval *larget, struct timeval *smallt)
+{
+ SMB_BIG_INT sec_diff = larget->tv_sec - smallt->tv_sec;
+ return (sec_diff * 1000000) + (SMB_BIG_INT)(larget->tv_usec - smallt->tv_usec);
+}
diff --git a/source3/locking/locking.c b/source3/locking/locking.c
index fd03a25940..8f53b55fc5 100644
--- a/source3/locking/locking.c
+++ b/source3/locking/locking.c
@@ -40,6 +40,17 @@ uint16 global_smbpid;
/* the locking database handle */
static TDB_CONTEXT *tdb;
+struct locking_data {
+ union {
+ int num_share_mode_entries;
+ share_mode_entry dummy; /* Needed for alignment. */
+ } u;
+ /* the following two entries are implicit
+ share_mode_entry modes[num_share_mode_entries];
+ char file_name[];
+ */
+};
+
/****************************************************************************
Debugging aid :-).
****************************************************************************/
@@ -432,6 +443,7 @@ int get_share_modes(connection_struct *conn,
data = (struct locking_data *)dbuf.dptr;
num_share_modes = data->u.num_share_mode_entries;
if(num_share_modes) {
+ pstring fname;
int i;
int del_count = 0;
@@ -443,6 +455,9 @@ int get_share_modes(connection_struct *conn,
return 0;
}
+ /* Save off the associated filename. */
+ pstrcpy(fname, dbuf.dptr + sizeof(*data) + num_share_modes * sizeof(share_mode_entry));
+
/*
* Ensure that each entry has a real process attached.
*/
@@ -467,17 +482,28 @@ int get_share_modes(connection_struct *conn,
if (del_count) {
data->u.num_share_mode_entries = num_share_modes;
- if (num_share_modes)
+ if (num_share_modes) {
memcpy(dbuf.dptr + sizeof(*data), shares,
num_share_modes * sizeof(share_mode_entry));
+ /* Append the filename. */
+ pstrcpy(dbuf.dptr + sizeof(*data) + num_share_modes * sizeof(share_mode_entry), fname);
+ }
/* The record has shrunk a bit */
dbuf.dsize -= del_count * sizeof(share_mode_entry);
- if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1) {
- SAFE_FREE(shares);
- SAFE_FREE(dbuf.dptr);
- return 0;
+ if (data->u.num_share_mode_entries == 0) {
+ if (tdb_delete(tdb, key) == -1) {
+ SAFE_FREE(shares);
+ SAFE_FREE(dbuf.dptr);
+ return 0;
+ }
+ } else {
+ if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1) {
+ SAFE_FREE(shares);
+ SAFE_FREE(dbuf.dptr);
+ return 0;
+ }
}
}
}
@@ -847,6 +873,358 @@ BOOL modify_delete_flag( SMB_DEV_T dev, SMB_INO_T inode, BOOL delete_on_close)
return True;
}
+/*******************************************************************
+ Print out a deferred open entry.
+********************************************************************/
+
+char *deferred_open_str(int num, deferred_open_entry *e)
+{
+ static pstring de_str;
+
+ slprintf(de_str, sizeof(de_str)-1, "deferred_open_entry[%d]: \
+pid = %lu, mid = %u, dev = 0x%x, inode = %.0f, port = %u, time = [%u.%06u]",
+ num, (unsigned long)e->pid, (unsigned int)e->mid, (unsigned int)e->dev, (double)e->inode,
+ (unsigned int)e->port,
+ (unsigned int)e->time.tv_sec, (unsigned int)e->time.tv_usec );
+
+ return de_str;
+}
+
+/* Internal data structures for deferred opens... */
+
+struct de_locking_key {
+ char name[4];
+ SMB_DEV_T dev;
+ SMB_INO_T inode;
+};
+
+struct deferred_open_data {
+ union {
+ int num_deferred_open_entries;
+ deferred_open_entry dummy; /* Needed for alignment. */
+ } u;
+ /* the following two entries are implicit
+ deferred_open_entry de_entries[num_deferred_open_entries];
+ char file_name[];
+ */
+};
+
+/*******************************************************************
+ Print out a deferred open table.
+********************************************************************/
+
+static void print_deferred_open_table(struct deferred_open_data *data)
+{
+ int num_de_entries = data->u.num_deferred_open_entries;
+ deferred_open_entry *de_entries = (deferred_open_entry *)(data + 1);
+ int i;
+
+ for (i = 0; i < num_de_entries; i++) {
+ deferred_open_entry *entry_p = &de_entries[i];
+ DEBUG(10,("print_deferred_open_table: %s\n", deferred_open_str(i, entry_p) ));
+ }
+}
+
+
+/*******************************************************************
+ Form a static deferred open locking key for a dev/inode pair.
+******************************************************************/
+
+static TDB_DATA deferred_open_locking_key(SMB_DEV_T dev, SMB_INO_T inode)
+{
+ static struct de_locking_key key;
+ TDB_DATA kbuf;
+
+ memset(&key, '\0', sizeof(key));
+ memcpy(&key.name[0], "DOE", 4);
+ key.dev = dev;
+ key.inode = inode;
+ kbuf.dptr = (char *)&key;
+ kbuf.dsize = sizeof(key);
+ return kbuf;
+}
+
+/*******************************************************************
+ Get all deferred open entries for a dev/inode pair.
+********************************************************************/
+
+int get_deferred_opens(connection_struct *conn,
+ SMB_DEV_T dev, SMB_INO_T inode,
+ deferred_open_entry **pp_de_entries)
+{
+ TDB_DATA dbuf;
+ struct deferred_open_data *data;
+ int num_de_entries;
+ deferred_open_entry *de_entries = NULL;
+ TDB_DATA key = deferred_open_locking_key(dev, inode);
+
+ *pp_de_entries = NULL;
+
+ dbuf = tdb_fetch(tdb, key);
+ if (!dbuf.dptr)
+ return 0;
+
+ data = (struct deferred_open_data *)dbuf.dptr;
+ num_de_entries = data->u.num_deferred_open_entries;
+ if(num_de_entries) {
+ pstring fname;
+ int i;
+ int del_count = 0;
+
+ de_entries = (deferred_open_entry *)memdup(dbuf.dptr + sizeof(*data),
+ num_de_entries * sizeof(deferred_open_entry));
+
+ if (!de_entries) {
+ SAFE_FREE(dbuf.dptr);
+ return 0;
+ }
+
+ /* Save off the associated filename. */
+ pstrcpy(fname, dbuf.dptr + sizeof(*data) + num_de_entries * sizeof(deferred_open_entry));
+
+ /*
+ * Ensure that each entry has a real process attached.
+ */
+
+ for (i = 0; i < num_de_entries; ) {
+ deferred_open_entry *entry_p = &de_entries[i];
+ if (process_exists(entry_p->pid)) {
+ DEBUG(10,("get_deferred_opens: %s\n", deferred_open_str(i, entry_p) ));
+ i++;
+ } else {
+ DEBUG(10,("get_deferred_opens: deleted %s\n", deferred_open_str(i, entry_p) ));
+ if (num_de_entries - i - 1 > 0) {
+ memcpy( &de_entries[i], &de_entries[i+1],
+ sizeof(deferred_open_entry) * (num_de_entries - i - 1));
+ }
+ num_de_entries--;
+ del_count++;
+ }
+ }
+
+ /* Did we delete any ? If so, re-store in tdb. */
+ if (del_count) {
+ data->u.num_deferred_open_entries = num_de_entries;
+
+ if (num_de_entries) {
+ memcpy(dbuf.dptr + sizeof(*data), de_entries,
+ num_de_entries * sizeof(deferred_open_entry));
+ /* Append the filename. */
+ pstrcpy(dbuf.dptr + sizeof(*data) + num_de_entries * sizeof(deferred_open_entry), fname);
+ }
+
+ /* The record has shrunk a bit */
+ dbuf.dsize -= del_count * sizeof(deferred_open_entry);
+
+ if (data->u.num_deferred_open_entries == 0) {
+ if (tdb_delete(tdb, key) == -1) {
+ SAFE_FREE(de_entries);
+ SAFE_FREE(dbuf.dptr);
+ return 0;
+ }
+ } else {
+ if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1) {
+ SAFE_FREE(de_entries);
+ SAFE_FREE(dbuf.dptr);
+ return 0;
+ }
+ }
+ }
+ }
+
+ SAFE_FREE(dbuf.dptr);
+ *pp_de_entries = de_entries;
+ return num_de_entries;
+}
+
+/*******************************************************************
+ Check if two deferred open entries are identical.
+********************************************************************/
+
+static BOOL deferred_open_entries_identical( deferred_open_entry *e1, deferred_open_entry *e2)
+{
+#if 1 /* JRA PARANOIA TEST - REMOVE LATER */
+ if (e1->pid == e2->pid &&
+ e1->port == e2->port &&
+ e1->dev == e2->dev &&
+ e1->inode == e2->inode &&
+ ((e1->time.tv_sec != e2->time.tv_sec) ||
+ (e1->time.tv_usec != e2->time.tv_usec) ||
+ (e1->mid != e2->mid))) {
+ smb_panic("PANIC: deferred_open_entries_identical: logic error.\n");
+ }
+#endif
+
+ return (e1->pid == e2->pid &&
+ e1->mid == e2->mid &&
+ e1->port == e2->port &&
+ e1->dev == e2->dev &&
+ e1->inode == e2->inode &&
+ e1->time.tv_sec == e2->time.tv_sec &&
+ e1->time.tv_usec == e2->time.tv_usec);
+}
+
+
+/*******************************************************************
+ Delete a specific deferred open entry.
+ Ignore if no entry deleted.
+********************************************************************/
+
+BOOL delete_deferred_open_entry(deferred_open_entry *entry)
+{
+ TDB_DATA dbuf;
+ struct deferred_open_data *data;
+ int i, del_count=0;
+ deferred_open_entry *de_entries;
+ BOOL ret = True;
+ TDB_DATA key = deferred_open_locking_key(entry->dev, entry->inode);
+
+ /* read in the existing share modes */
+ dbuf = tdb_fetch(tdb, key);
+ if (!dbuf.dptr)
+ return -1;
+
+ data = (struct deferred_open_data *)dbuf.dptr;
+ de_entries = (deferred_open_entry *)(dbuf.dptr + sizeof(*data));
+
+ /*
+ * Find any with this pid and delete it
+ * by overwriting with the rest of the data
+ * from the record.
+ */
+
+ DEBUG(10,("delete_deferred_open_entry: num_deferred_open_entries = %d\n",
+ data->u.num_deferred_open_entries ));
+
+ for (i=0;i<data->u.num_deferred_open_entries;) {
+ if (deferred_open_entries_identical(&de_entries[i], entry)) {
+ DEBUG(10,("delete_deferred_open_entry: deleted %s\n",
+ deferred_open_str(i, &de_entries[i]) ));
+
+ data->u.num_deferred_open_entries--;
+ if ((dbuf.dsize - (sizeof(*data) + (i+1)*sizeof(*de_entries))) > 0) {
+ memmove(&de_entries[i], &de_entries[i+1],
+ dbuf.dsize - (sizeof(*data) + (i+1)*sizeof(*de_entries)));
+ }
+ del_count++;
+
+ DEBUG(10,("delete_deferred_open_entry: deleting entry %d\n", i ));
+
+ } else {
+ i++;
+ }
+ }
+
+ SMB_ASSERT(del_count == 0 || del_count == 1);
+
+ if (del_count) {
+ /* the record may have shrunk a bit */
+ dbuf.dsize -= del_count * sizeof(*de_entries);
+
+ /* store it back in the database */
+ if (data->u.num_deferred_open_entries == 0) {
+ if (tdb_delete(tdb, key) == -1)
+ ret = False;
+ } else {
+ if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1)
+ ret = False;
+ }
+ }
+ DEBUG(10,("delete_deferred_open_entry: Remaining table.\n"));
+ print_deferred_open_table((struct deferred_open_data*)dbuf.dptr);
+ SAFE_FREE(dbuf.dptr);
+ return ret;
+}
+
+/*******************************************************************
+ Fill a deferred open entry.
+********************************************************************/
+
+static void fill_deferred_open(char *p, uint16 mid, struct timeval *ptv, SMB_DEV_T dev, SMB_INO_T inode, uint16 port)
+{
+ deferred_open_entry *e = (deferred_open_entry *)p;
+ void *x = &e->time; /* Needed to force alignment. p may not be aligned.... */
+
+ memset(e, '\0', sizeof(deferred_open_entry));
+ e->mid = mid;
+ e->pid = sys_getpid();
+ memcpy(x, ptv, sizeof(struct timeval));
+ e->dev = dev;
+ e->inode = inode;
+ e->port = port;
+}
+
+/*******************************************************************
+ Add a deferred open record. Return False on fail, True on success.
+********************************************************************/
+
+BOOL add_deferred_open(uint16 mid, struct timeval *ptv, SMB_DEV_T dev, SMB_INO_T inode, uint16 port, const char *fname)
+{
+ TDB_DATA dbuf;
+ struct deferred_open_data *data;
+ char *p=NULL;
+ int size;
+ TDB_DATA key = deferred_open_locking_key(dev, inode);
+ BOOL ret = True;
+
+ /* read in the existing deferred open records if any */
+ dbuf = tdb_fetch(tdb, key);
+ if (!dbuf.dptr) {
+ size_t offset;
+ /* we'll need to create a new record */
+
+ size = sizeof(*data) + sizeof(deferred_open_entry) + strlen(fname) + 1;
+ p = (char *)malloc(size);
+ if (!p)
+ return False;
+ data = (struct deferred_open_data *)p;
+ data->u.num_deferred_open_entries = 1;
+
+ DEBUG(10,("add_deferred_open: creating entry for file %s. num_deferred_open_entries = 1\n",
+ fname ));
+
+ offset = sizeof(*data) + sizeof(deferred_open_entry);
+ safe_strcpy(p + offset, fname, size - offset - 1);
+ fill_deferred_open(p + sizeof(*data), mid, ptv, dev, inode, port);
+ dbuf.dptr = p;
+ dbuf.dsize = size;
+ if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1)
+ ret = False;
+
+ print_deferred_open_table((struct deferred_open_data *)p);
+
+ SAFE_FREE(p);
+ return ret;
+ }
+
+ /* we're adding to an existing entry - this is a bit fiddly */
+ data = (struct deferred_open_data *)dbuf.dptr;
+
+ data->u.num_deferred_open_entries++;
+
+ DEBUG(10,("add_deferred_open: adding entry for file %s. new num_deferred_open_entries = %d\n",
+ fname, data->u.num_deferred_open_entries ));
+
+ size = dbuf.dsize + sizeof(deferred_open_entry);
+ p = malloc(size);
+ if (!p) {
+ SAFE_FREE(dbuf.dptr);
+ return False;
+ }
+ memcpy(p, dbuf.dptr, sizeof(*data));
+ fill_deferred_open(p + sizeof(*data), mid, ptv, dev, inode, port);
+ memcpy(p + sizeof(*data) + sizeof(deferred_open_entry), dbuf.dptr + sizeof(*data),
+ dbuf.dsize - sizeof(*data));
+ SAFE_FREE(dbuf.dptr);
+ dbuf.dptr = p;
+ dbuf.dsize = size;
+ if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1)
+ ret = False;
+ print_deferred_open_table((struct deferred_open_data *)p);
+ SAFE_FREE(p);
+ return ret;
+}
+
/****************************************************************************
Traverse the whole database with this function, calling traverse_callback
on each share mode
@@ -862,6 +1240,10 @@ static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
SHAREMODE_FN(traverse_callback) = (SHAREMODE_FN_CAST())state;
+ /* Ensure this is a locking_key record. */
+ if (kbuf.dsize != sizeof(struct locking_key))
+ return 0;
+
data = (struct locking_data *)dbuf.dptr;
shares = (share_mode_entry *)(dbuf.dptr + sizeof(*data));
name = dbuf.dptr + sizeof(*data) + data->u.num_share_mode_entries*sizeof(*shares);
diff --git a/source3/printing/nt_printing.c b/source3/printing/nt_printing.c
index 909aed6c86..225ff20ec3 100644
--- a/source3/printing/nt_printing.c
+++ b/source3/printing/nt_printing.c
@@ -1003,9 +1003,9 @@ static int file_version_is_newer(connection_struct *conn, fstring new_file, fstr
driver_unix_convert(filepath,conn,NULL,&bad_path,&stat_buf);
fsp = open_file_shared(conn, filepath, &stat_buf,
- SET_OPEN_MODE(DOS_OPEN_RDONLY),
+ SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_RDONLY),
(FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
- FILE_ATTRIBUTE_NORMAL, 0, &access_mode, &action);
+ FILE_ATTRIBUTE_NORMAL, INTERNAL_OPEN_ONLY, &access_mode, &action);
if (!fsp) {
/* Old file not found, so by definition new file is in fact newer */
DEBUG(10,("file_version_is_newer: Can't open old file [%s], errno = %d\n",
@@ -1032,9 +1032,9 @@ static int file_version_is_newer(connection_struct *conn, fstring new_file, fstr
driver_unix_convert(filepath,conn,NULL,&bad_path,&stat_buf);
fsp = open_file_shared(conn, filepath, &stat_buf,
- SET_OPEN_MODE(DOS_OPEN_RDONLY),
+ SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_RDONLY),
(FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
- FILE_ATTRIBUTE_NORMAL, 0, &access_mode, &action);
+ FILE_ATTRIBUTE_NORMAL, INTERNAL_OPEN_ONLY, &access_mode, &action);
if (!fsp) {
/* New file not found, this shouldn't occur if the caller did its job */
DEBUG(3,("file_version_is_newer: Can't open new file [%s], errno = %d\n",
@@ -1148,9 +1148,9 @@ static uint32 get_correct_cversion(const char *architecture, fstring driverpath_
driver_unix_convert(driverpath,conn,NULL,&bad_path,&st);
fsp = open_file_shared(conn, driverpath, &st,
- SET_OPEN_MODE(DOS_OPEN_RDONLY),
+ SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_RDONLY),
(FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
- FILE_ATTRIBUTE_NORMAL, 0, &access_mode, &action);
+ FILE_ATTRIBUTE_NORMAL, INTERNAL_OPEN_ONLY, &access_mode, &action);
if (!fsp) {
DEBUG(3,("get_correct_cversion: Can't open file [%s], errno = %d\n",
driverpath, errno));
diff --git a/source3/rpc_server/srv_srvsvc_nt.c b/source3/rpc_server/srv_srvsvc_nt.c
index 77b9be9966..087c50451e 100644
--- a/source3/rpc_server/srv_srvsvc_nt.c
+++ b/source3/rpc_server/srv_srvsvc_nt.c
@@ -1886,8 +1886,9 @@ WERROR _srv_net_file_query_secdesc(pipes_struct *p, SRV_Q_NET_FILE_QUERY_SECDESC
unistr2_to_ascii(filename, &q_u->uni_file_name, sizeof(filename));
unix_convert(filename, conn, NULL, &bad_path, &st);
- fsp = open_file_shared(conn, filename, &st, SET_OPEN_MODE(DOS_OPEN_RDONLY),
- (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), FILE_ATTRIBUTE_NORMAL, 0, &access_mode, &action);
+ fsp = open_file_shared(conn, filename, &st, SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_RDONLY),
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), FILE_ATTRIBUTE_NORMAL, INTERNAL_OPEN_ONLY,
+ &access_mode, &action);
if (!fsp) {
/* Perhaps it is a directory */
@@ -1990,8 +1991,9 @@ WERROR _srv_net_file_set_secdesc(pipes_struct *p, SRV_Q_NET_FILE_SET_SECDESC *q_
unistr2_to_ascii(filename, &q_u->uni_file_name, sizeof(filename));
unix_convert(filename, conn, NULL, &bad_path, &st);
- fsp = open_file_shared(conn, filename, &st, SET_OPEN_MODE(DOS_OPEN_RDWR),
- (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), FILE_ATTRIBUTE_NORMAL, 0, &access_mode, &action);
+ fsp = open_file_shared(conn, filename, &st, SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_RDWR),
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), FILE_ATTRIBUTE_NORMAL, INTERNAL_OPEN_ONLY,
+ &access_mode, &action);
if (!fsp) {
/* Perhaps it is a directory */
diff --git a/source3/script/mkproto.awk b/source3/script/mkproto.awk
index b38f405e0d..fbe1bddf35 100644
--- a/source3/script/mkproto.awk
+++ b/source3/script/mkproto.awk
@@ -124,7 +124,7 @@ END {
gotstart = 1;
}
- if( $0 ~ /^smb_iconv_t|^long|^char|^uint|^NTSTATUS|^WERROR|^CLI_POLICY_HND|^struct|^BOOL|^void|^time|^smb_shm_offset_t|^shm_offset_t|^FILE|^XFILE|^SMB_OFF_T|^size_t|^ssize_t|^SMB_BIG_UINT/ ) {
+ if( $0 ~ /^smb_iconv_t|^long|^char|^uint|^NTSTATUS|^WERROR|^CLI_POLICY_HND|^struct|^BOOL|^void|^time|^smb_shm_offset_t|^shm_offset_t|^FILE|^XFILE|^SMB_OFF_T|^size_t|^ssize_t|^SMB_BIG_UINT|^SMB_BIG_INT/ ) {
gotstart = 1;
}
diff --git a/source3/smbd/close.c b/source3/smbd/close.c
index 8b3010c1b2..305377dfc6 100644
--- a/source3/smbd/close.c
+++ b/source3/smbd/close.c
@@ -2,6 +2,7 @@
Unix SMB/CIFS implementation.
file closing
Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Jeremy Allison 1992-2004.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -105,6 +106,32 @@ static int close_filestruct(files_struct *fsp)
}
/****************************************************************************
+ If any deferred opens are waiting on this close, notify them.
+****************************************************************************/
+
+static void notify_deferred_opens(files_struct *fsp)
+{
+ deferred_open_entry *de_array = NULL;
+ int num_de_entries, i;
+ pid_t mypid = sys_getpid();
+
+ num_de_entries = get_deferred_opens(fsp->conn, fsp->dev, fsp->inode, &de_array);
+ for (i = 0; i < num_de_entries; i++) {
+ deferred_open_entry *entry = &de_array[i];
+ if (entry->pid == mypid) {
+ /*
+ * We need to notify ourself to retry the open.
+ * Do this by finding the queued SMB record, moving it
+ * to the head of the queue and changing the wait time to zero.
+ */
+ schedule_sharing_violation_open_smb_message(entry->mid);
+ } else {
+ send_deferred_open_retry_message(entry);
+ }
+ }
+}
+
+/****************************************************************************
Close a file.
If normal_close is 1 then this came from a normal SMBclose (or equivalent)
@@ -177,6 +204,9 @@ static int close_normal_file(files_struct *fsp, BOOL normal_close)
SAFE_FREE(share_entry);
+ /* Notify any deferred opens waiting on this close. */
+ notify_deferred_opens(fsp);
+
/*
* NT can set delete_on_close of the last open
* reference to a file.
diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c
index 06ef23ab8c..b88f687766 100644
--- a/source3/smbd/dir.c
+++ b/source3/smbd/dir.c
@@ -763,7 +763,8 @@ static BOOL user_can_write_file(connection_struct *conn, char *name, SMB_STRUCT_
return True;
else
fsp = open_file_shared1(conn, name, pst, FILE_WRITE_ATTRIBUTES, SET_DENY_MODE(DENY_NONE),
- (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), FILE_ATTRIBUTE_NORMAL, 0, &access_mode, &smb_action);
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), FILE_ATTRIBUTE_NORMAL, INTERNAL_OPEN_ONLY,
+ &access_mode, &smb_action);
if (!fsp)
return False;
diff --git a/source3/smbd/nttrans.c b/source3/smbd/nttrans.c
index 26be4434fd..e540db234a 100644
--- a/source3/smbd/nttrans.c
+++ b/source3/smbd/nttrans.c
@@ -863,6 +863,10 @@ create_options = 0x%x root_dir_fid = 0x%x\n", flags, desired_access, file_attrib
restore_case_semantics(conn, file_attributes);
END_PROFILE(SMBntcreateX);
+ if (open_was_deferred(SVAL(inbuf,smb_mid))) {
+ /* We have re-scheduled this call. */
+ return -1;
+ }
return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess);
}
}
@@ -1347,6 +1351,10 @@ static int call_nt_transact_create(connection_struct *conn, char *inbuf, char *o
}
} else {
restore_case_semantics(conn, file_attributes);
+ if (open_was_deferred(SVAL(inbuf,smb_mid))) {
+ /* We have re-scheduled this call. */
+ return -1;
+ }
return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess);
}
}
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index ab5ea236fa..04e074d56e 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -2,7 +2,7 @@
Unix SMB/CIFS implementation.
file opening and share modes
Copyright (C) Andrew Tridgell 1992-1998
- Copyright (C) Jeremy Allison 2001
+ Copyright (C) Jeremy Allison 2001-2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -26,6 +26,11 @@ extern uint16 global_oplock_port;
extern uint16 global_smbpid;
extern BOOL global_client_failed_oplock_break;
+struct dev_inode_bundle {
+ SMB_DEV_T dev;
+ SMB_INO_T inode;
+};
+
/****************************************************************************
fd support routines - attempt to do a dos_open.
****************************************************************************/
@@ -243,7 +248,7 @@ static BOOL open_file(files_struct *fsp,connection_struct *conn,
}
/*******************************************************************
-return True if the filename is one of the special executable types
+ Return True if the filename is one of the special executable types.
********************************************************************/
static BOOL is_executable(const char *fname)
@@ -262,12 +267,13 @@ static BOOL is_executable(const char *fname)
enum {AFAIL,AREAD,AWRITE,AALL};
/*******************************************************************
-reproduce the share mode access table
-this is horrendoously complex, and really can't be justified on any
-rational grounds except that this is _exactly_ what NT does. See
-the DENY1 and DENY2 tests in smbtorture for a comprehensive set of
-test routines.
+ Reproduce the share mode access table.
+ This is horrendoously complex, and really can't be justified on any
+ rational grounds except that this is _exactly_ what NT does. See
+ the DENY1 and DENY2 tests in smbtorture for a comprehensive set of
+ test routines.
********************************************************************/
+
static int access_table(int new_deny,int old_deny,int old_mode,
BOOL same_pid, BOOL isexe)
{
@@ -353,9 +359,8 @@ static int access_table(int new_deny,int old_deny,int old_mode,
return(AFAIL);
}
-
/****************************************************************************
-check if we can open a file with a share mode
+ Check if we can open a file with a share mode.
****************************************************************************/
static BOOL check_share_mode(connection_struct *conn, share_mode_entry *share, int share_mode, uint32 desired_access,
@@ -770,9 +775,101 @@ after break ! For file %s, dev = %x, inode = %.0f. Deleting it to continue...\n"
}
/****************************************************************************
-set a kernel flock on a file for NFS interoperability
-this requires a patch to Linux
+ Delete the record for a handled deferred open entry.
+****************************************************************************/
+
+static void delete_defered_open_entry_record(connection_struct *conn, SMB_DEV_T dev, SMB_INO_T inode)
+{
+ uint16 mid = get_current_mid();
+ pid_t mypid = sys_getpid();
+ deferred_open_entry *de_array = NULL;
+ int num_de_entries, i;
+
+ num_de_entries = get_deferred_opens(conn, dev, inode, &de_array);
+ for (i = 0; i < num_de_entries; i++) {
+ deferred_open_entry *entry = &de_array[i];
+ if (entry->pid == mypid && entry->mid == mid && entry->dev == dev &&
+ entry->inode == inode) {
+
+ /* Remove the deferred open entry from the array. */
+ delete_deferred_open_entry(entry);
+ SAFE_FREE(de_array);
+ return;
+ }
+ }
+ SAFE_FREE(de_array);
+}
+
+/****************************************************************************
+ Handle the 1 second delay in returning a SHARING_VIOLATION error.
+****************************************************************************/
+
+void defer_open_sharing_error(connection_struct *conn, struct timeval *ptv,
+ char *fname, SMB_DEV_T dev, SMB_INO_T inode)
+{
+ uint16 mid = get_current_mid();
+ pid_t mypid = sys_getpid();
+ deferred_open_entry *de_array = NULL;
+ int num_de_entries, i;
+ struct dev_inode_bundle dib;
+
+ dib.dev = dev;
+ dib.inode = inode;
+
+ num_de_entries = get_deferred_opens(conn, dev, inode, &de_array);
+ for (i = 0; i < num_de_entries; i++) {
+ deferred_open_entry *entry = &de_array[i];
+ if (entry->pid == mypid && entry->mid == mid) {
+ /*
+ * Check if a 1 second timeout has expired.
+ */
+ if (usec_time_diff(ptv, &entry->time) > SHARING_VIOLATION_USEC_WAIT) {
+ DEBUG(10,("defer_open_sharing_error: Deleting deferred open entry for mid %u, \
+file %s\n",
+ (unsigned int)mid, fname ));
+
+ /* Expired, return a real error. */
+ /* Remove the deferred open entry from the array. */
+
+ delete_deferred_open_entry(entry);
+ SAFE_FREE(de_array);
+ return;
+ }
+ /*
+ * If the timeout hasn't expired yet and we still have a sharing violation,
+ * just leave the entry in the deferred open array alone. We do need to
+ * reschedule this open call though (with the original created time).
+ */
+ DEBUG(10,("defer_open_sharing_error: time [%u.%06u] updating \
+deferred open entry for mid %u, file %s\n",
+ (unsigned int)entry->time.tv_sec,
+ (unsigned int)entry->time.tv_usec,
+ (unsigned int)mid, fname ));
+
+ push_sharing_violation_open_smb_message(&entry->time, (char *)&dib, sizeof(dib));
+ SAFE_FREE(de_array);
+ return;
+ }
+ }
+
+ DEBUG(10,("defer_open_sharing_error: time [%u.%06u] adding deferred open entry for mid %u, file %s\n",
+ (unsigned int)ptv->tv_sec, (unsigned int)ptv->tv_usec, (unsigned int)mid, fname ));
+
+ if (!push_sharing_violation_open_smb_message(ptv, (char *)&dib, sizeof(dib))) {
+ SAFE_FREE(de_array);
+ return;
+ }
+ if (!add_deferred_open(mid, ptv, dev, inode, global_oplock_port, fname)) {
+ remove_sharing_violation_open_smb_message(mid);
+ }
+ SAFE_FREE(de_array);
+}
+
+/****************************************************************************
+ Set a kernel flock on a file for NFS interoperability.
+ This requires a patch to Linux.
****************************************************************************/
+
static void kernel_flock(files_struct *fsp, int deny_mode)
{
#if HAVE_KERNEL_SHARE_MODES
@@ -847,6 +944,7 @@ files_struct *open_file_shared1(connection_struct *conn,char *fname, SMB_STRUCT_
BOOL fcbopen = False;
BOOL def_acl = False;
BOOL add_share_mode = True;
+ BOOL internal_only_open = False;
SMB_DEV_T dev = 0;
SMB_INO_T inode = 0;
int num_share_modes = 0;
@@ -858,9 +956,50 @@ files_struct *open_file_shared1(connection_struct *conn,char *fname, SMB_STRUCT_
mode_t new_mode = (mode_t)0;
int action;
uint32 existing_dos_mode = 0;
+ struct pending_message_list *pml = NULL;
+ uint16 mid = get_current_mid();
/* We add aARCH to this as this mode is only used if the file is created new. */
mode_t mode = unix_mode(conn,new_dos_mode | aARCH,fname);
+ if (oplock_request == INTERNAL_OPEN_ONLY) {
+ internal_only_open = True;
+ oplock_request = 0;
+ }
+
+ if ((pml = get_open_deferred_message(mid)) != NULL) {
+ struct dev_inode_bundle dib;
+
+ memcpy(&dib, pml->private_data.data, sizeof(dib));
+
+ /* There could be a race condition where the dev/inode pair
+ has changed since we deferred the message. If so, just
+ remove the deferred open entry and return sharing violation. */
+
+ /* If the timeout value is non-zero, we need to just
+ return sharing violation. Don't retry the open
+ as we were not notified of a close and we don't want to
+ trigger another spurious oplock break. */
+
+ if (!file_existed || dib.dev != psbuf->st_dev || dib.inode != psbuf->st_ino ||
+ pml->msg_time.tv_sec || pml->msg_time.tv_usec) {
+ /* Ensure we don't reprocess this message. */
+ remove_sharing_violation_open_smb_message(mid);
+
+ /* Now remove the deferred open entry under lock. */
+ lock_share_entry(conn, dib.dev, dib.inode);
+ delete_defered_open_entry_record(conn, dib.dev, dib.inode);
+ unlock_share_entry(conn, dib.dev, dib.inode);
+
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadshare;
+ unix_ERR_ntstatus = NT_STATUS_SHARING_VIOLATION;
+ return NULL;
+ }
+ /* Ensure we don't reprocess this message. */
+ remove_sharing_violation_open_smb_message(mid);
+
+ }
+
if (conn->printer) {
/* printers are handled completely differently. Most of the passed parameters are
ignored */
@@ -1043,17 +1182,28 @@ flags=0x%X flags2=0x%X mode=0%o returned %d\n",
unix_ERR_ntstatus = NT_STATUS_ACCESS_DENIED;
}
+ /*
+ * If we're returning a share violation, ensure we cope with
+ * the braindead 1 second delay.
+ */
+
+ if (!internal_only_open && NT_STATUS_EQUAL(unix_ERR_ntstatus,NT_STATUS_SHARING_VIOLATION)) {
+ /* The fsp->open_time here represents the current time of day. */
+ defer_open_sharing_error(conn, &fsp->open_time, fname, dev, inode);
+ }
+
unlock_share_entry(conn, dev, inode);
- if (fsp_open)
+ if (fsp_open) {
fd_close(conn, fsp);
+ /*
+ * We have detected a sharing violation here
+ * so return the correct error code
+ */
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadshare;
+ unix_ERR_ntstatus = NT_STATUS_SHARING_VIOLATION;
+ }
file_free(fsp);
- /*
- * We have detected a sharing violation here
- * so return the correct error code
- */
- unix_ERR_class = ERRDOS;
- unix_ERR_code = ERRbadshare;
- unix_ERR_ntstatus = NT_STATUS_SHARING_VIOLATION;
return NULL;
}
@@ -1118,6 +1268,16 @@ flags=0x%X flags2=0x%X mode=0%o returned %d\n",
&flags, &oplock_request, &all_current_opens_are_level_II);
if(num_share_modes == -1) {
+ /*
+ * If we're returning a share violation, ensure we cope with
+ * the braindead 1 second delay.
+ */
+
+ if (!internal_only_open && NT_STATUS_EQUAL(unix_ERR_ntstatus,NT_STATUS_SHARING_VIOLATION)) {
+ /* The fsp->open_time here represents the current time of day. */
+ defer_open_sharing_error(conn, &fsp->open_time, fname, dev, inode);
+ }
+
unlock_share_entry_fsp(fsp);
fd_close(conn,fsp);
file_free(fsp);
@@ -1286,6 +1446,8 @@ flags=0x%X flags2=0x%X mode=0%o returned %d\n",
fname, (int)new_mode));
}
+ /* If this is a successful open, we must remove any deferred open records. */
+ delete_defered_open_entry_record(conn, fsp->dev, fsp->inode);
unlock_share_entry_fsp(fsp);
conn->num_files_open++;
diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c
index 19e6956d9e..6739f5654f 100644
--- a/source3/smbd/oplock.c
+++ b/source3/smbd/oplock.c
@@ -388,6 +388,30 @@ pid %d, port %d, dev = %x, inode = %.0f, file_id = %lu\n",
(int)remotepid, from_port, (unsigned int)dev, (double)inode, file_id));
break;
+ case RETRY_DEFERRED_OPEN_CMD:
+
+ /* Request to retry and open that would return SHARING_VIOLATION. */
+ if (msg_len != DEFERRED_OPEN_MSG_LEN) {
+ DEBUG(0,("process_local_message: incorrect length for RETRY_DEFERRED_OPEN_CMD (was %d, should be %d).\n",
+ (int)msg_len, (int)DEFERRED_OPEN_MSG_LEN));
+ return False;
+ }
+ {
+ uint16 mid;
+
+ memcpy((char *)&remotepid, msg_start+DEFERRED_OPEN_PID_OFFSET,sizeof(remotepid));
+ memcpy((char *)&inode, msg_start+DEFERRED_OPEN_INODE_OFFSET,sizeof(inode));
+ memcpy((char *)&dev, msg_start+DEFERRED_OPEN_DEV_OFFSET,sizeof(dev));
+ memcpy((char *)&mid, msg_start+DEFERRED_OPEN_MID_OFFSET,sizeof(mid));
+
+ DEBUG(5,("process_local_message: RETRY_DEFERRED_OPEN from \
+pid %d, port %d, dev = %x, inode = %.0f, mid = %u\n",
+ (int)remotepid, from_port, (unsigned int)dev, (double)inode, (unsigned int)mid));
+
+ schedule_sharing_violation_open_smb_message(mid);
+ }
+ break;
+
/*
* Keep this as a debug case - eventually we can remove it.
*/
@@ -1215,7 +1239,51 @@ void release_level_2_oplocks_on_change(files_struct *fsp)
}
/****************************************************************************
-setup oplocks for this process
+ Send a 'retry your open' message to a process with a deferred open entry.
+****************************************************************************/
+
+BOOL send_deferred_open_retry_message(deferred_open_entry *entry)
+{
+ char de_msg[DEFERRED_OPEN_MSG_LEN];
+ struct sockaddr_in addr_out;
+ pid_t pid = sys_getpid();
+
+ memset(de_msg, '\0', DEFERRED_OPEN_MSG_LEN);
+ SSVAL(de_msg,DEFERRED_OPEN_CMD_OFFSET,RETRY_DEFERRED_OPEN_CMD);
+ memcpy(de_msg+DEFERRED_OPEN_PID_OFFSET,(char *)&pid,sizeof(pid));
+ memcpy(de_msg+DEFERRED_OPEN_DEV_OFFSET,(char *)&entry->dev,sizeof(entry->dev));
+ memcpy(de_msg+DEFERRED_OPEN_INODE_OFFSET,(char *)&entry->inode,sizeof(entry->inode));
+ memcpy(de_msg+DEFERRED_OPEN_MID_OFFSET,(char *)&entry->mid,sizeof(entry->mid));
+
+ /* Set the address and port. */
+ memset((char *)&addr_out,'\0',sizeof(addr_out));
+ addr_out.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ addr_out.sin_port = htons( entry->port );
+ addr_out.sin_family = AF_INET;
+
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "send_deferred_open_retry_message: sending a message to ");
+ dbgtext( "pid %d on port %d ", (int)entry->pid, entry->port );
+ dbgtext( "for dev = %x, inode = %.0f, mid = %u\n",
+ (unsigned int)entry->dev, (double)entry->inode, (unsigned int)entry->mid );
+ }
+
+ if(sys_sendto(oplock_sock,de_msg,DEFERRED_OPEN_MSG_LEN,0,
+ (struct sockaddr *)&addr_out,sizeof(addr_out)) < 0) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "send_deferred_open_retry_message: failed sending a message to ");
+ dbgtext( "pid %d on port %d ", (int)entry->pid, entry->port );
+ dbgtext( "for dev = %x, inode = %.0f, mid = %u\n",
+ (unsigned int)entry->dev, (double)entry->inode, (unsigned int)entry->mid );
+ dbgtext( "Error was %s\n", strerror(errno) );
+ }
+ return False;
+ }
+ return True;
+}
+
+/****************************************************************************
+ Setup oplocks for this process.
****************************************************************************/
BOOL init_oplocks(void)
diff --git a/source3/smbd/process.c b/source3/smbd/process.c
index ccebd2b86c..698c8475f7 100644
--- a/source3/smbd/process.c
+++ b/source3/smbd/process.c
@@ -61,20 +61,28 @@ uint16 get_current_mid(void)
for processing.
****************************************************************************/
-struct pending_message_list {
- struct pending_message_list *next, *prev;
- char *msg_buf;
- int msg_len;
-};
-
static struct pending_message_list *smb_oplock_queue;
+static struct pending_message_list *smb_sharing_violation_queue;
+
+enum q_type { OPLOCK_QUEUE, SHARE_VIOLATION_QUEUE };
+
+/****************************************************************************
+ Free up a message.
+****************************************************************************/
+
+static void free_queued_message(struct pending_message_list *msg)
+{
+ data_blob_free(&msg->buf);
+ data_blob_free(&msg->private_data);
+ SAFE_FREE(msg);
+}
/****************************************************************************
Function to push a message onto the tail of a linked list of smb messages ready
for processing.
****************************************************************************/
-static BOOL push_message(char *buf, int msg_len)
+static BOOL push_queued_message(enum q_type qt, char *buf, int msg_len, struct timeval *ptv, char *private, size_t private_len)
{
struct pending_message_list *tmp_msg;
struct pending_message_list *msg = (struct pending_message_list *)
@@ -85,32 +93,159 @@ static BOOL push_message(char *buf, int msg_len)
return False;
}
- msg->msg_buf = (char *)malloc(msg_len);
- if(msg->msg_buf == NULL) {
+ memset(msg,'\0',sizeof(*msg));
+
+ msg->buf = data_blob(buf, msg_len);
+ if(msg->buf.data == NULL) {
DEBUG(0,("push_message: malloc fail (2)\n"));
SAFE_FREE(msg);
return False;
}
- memcpy(msg->msg_buf, buf, msg_len);
- msg->msg_len = msg_len;
+ if (ptv) {
+ msg->msg_time = *ptv;
+ }
- DLIST_ADD_END(smb_oplock_queue, msg, tmp_msg);
+ if (private) {
+ msg->private_data = data_blob(private, private_len);
+ if (msg->private_data.data == NULL) {
+ DEBUG(0,("push_message: malloc fail (3)\n"));
+ data_blob_free(&msg->buf);
+ SAFE_FREE(msg);
+ }
+ }
+
+ if (qt == OPLOCK_QUEUE) {
+ DLIST_ADD_END(smb_oplock_queue, msg, tmp_msg);
+ } else {
+ DLIST_ADD_END(smb_sharing_violation_queue, msg, tmp_msg);
+ }
/* Push the MID of this packet on the signing queue. */
srv_defer_sign_response(SVAL(buf,smb_mid));
+ DEBUG(10,("push_message: pushed message length %u on queue %s\n",
+ (unsigned int)msg_len,
+ qt == OPLOCK_QUEUE ? "smb_oplock_queue" : "smb_sharing_violation_queue" ));
+
return True;
}
/****************************************************************************
- Function to push a smb message onto a linked list of local smb messages ready
+ Function to push an oplock smb message onto a linked list of local smb messages ready
for processing.
****************************************************************************/
BOOL push_oplock_pending_smb_message(char *buf, int msg_len)
{
- return push_message(buf, msg_len);
+ return push_queued_message(OPLOCK_QUEUE, buf, msg_len, NULL, NULL, 0);
+}
+
+/****************************************************************************
+ Function to delete a sharing violation open message by mid.
+****************************************************************************/
+
+void remove_sharing_violation_open_smb_message(uint16 mid)
+{
+ struct pending_message_list *pml;
+
+ for (pml = smb_sharing_violation_queue; pml; pml = pml->next) {
+ if (mid == SVAL(pml->buf.data,smb_mid)) {
+ DEBUG(10,("remove_sharing_violation_open_smb_message: deleting mid %u len %u\n",
+ (unsigned int)mid, (unsigned int)pml->buf.length ));
+ DLIST_REMOVE(smb_sharing_violation_queue, pml);
+ free_queued_message(pml);
+ return;
+ }
+ }
+}
+
+/****************************************************************************
+ Move a sharing violation open retry message to the front of the list and
+ schedule it for immediate processing.
+****************************************************************************/
+
+void schedule_sharing_violation_open_smb_message(uint16 mid)
+{
+ struct pending_message_list *pml;
+ int i = 0;
+
+ for (pml = smb_sharing_violation_queue; pml; pml = pml->next) {
+ uint16 msg_mid = SVAL(pml->buf.data,smb_mid);
+ DEBUG(10,("schedule_sharing_violation_open_smb_message: [%d] msg_mid = %u\n", i++,
+ (unsigned int)msg_mid ));
+ if (mid == msg_mid) {
+ DEBUG(10,("schedule_sharing_violation_open_smb_message: scheduling mid %u\n",
+ mid ));
+ pml->msg_time.tv_sec = 0;
+ pml->msg_time.tv_usec = 0;
+ DLIST_PROMOTE(smb_sharing_violation_queue, pml);
+ return;
+ }
+ }
+
+ DEBUG(10,("schedule_sharing_violation_open_smb_message: failed to find message mid %u\n",
+ mid ));
+}
+
+/****************************************************************************
+ Return true if this mid is on the deferred queue.
+****************************************************************************/
+
+BOOL open_was_deferred(uint16 mid)
+{
+ struct pending_message_list *pml;
+ for (pml = smb_sharing_violation_queue; pml; pml = pml->next) {
+ if (SVAL(pml->buf.data,smb_mid) == mid) {
+ return True;
+ }
+ }
+ return False;
+}
+
+/****************************************************************************
+ Return the message queued by this mid.
+****************************************************************************/
+
+struct pending_message_list *get_open_deferred_message(uint16 mid)
+{
+ struct pending_message_list *pml;
+ for (pml = smb_sharing_violation_queue; pml; pml = pml->next) {
+ if (SVAL(pml->buf.data,smb_mid) == mid) {
+ return pml;
+ }
+ }
+ return NULL;
+}
+
+/****************************************************************************
+ Function to push a sharing violation open smb message onto a linked list of local smb messages ready
+ for processing.
+****************************************************************************/
+
+BOOL push_sharing_violation_open_smb_message(struct timeval *ptv, char *private, size_t priv_len)
+{
+ uint16 mid = SVAL(InBuffer,smb_mid);
+ struct timeval tv;
+ SMB_BIG_INT tdif;
+
+ tv = *ptv;
+ tdif = tv.tv_sec;
+ tdif *= 1000000;
+ tdif += tv.tv_usec;
+
+ /* Add on the timeout. */
+ tdif += SHARING_VIOLATION_USEC_WAIT;
+
+ tv.tv_sec = tdif / 1000000;
+ tv.tv_usec = tdif % 1000000;
+
+ DEBUG(10,("push_sharing_violation_open_smb_message: pushing message len %u mid %u\
+ timeout time [%u.%06u]\n", (unsigned int) smb_len(InBuffer)+4, (unsigned int)mid,
+ (unsigned int)tv.tv_sec, (unsigned int)tv.tv_usec));
+
+ return push_queued_message(SHARE_VIOLATION_QUEUE, InBuffer,
+ smb_len(InBuffer)+4, &tv, private, priv_len);
}
/****************************************************************************
@@ -169,12 +304,17 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout)
fd_set fds;
int selrtn;
struct timeval to;
+ struct timeval *pto;
int maxfd;
smb_read_error = 0;
again:
+ to.tv_sec = timeout / 1000;
+ to.tv_usec = (timeout % 1000) * 1000;
+ pto = timeout > 0 ? &to : NULL;
+
/*
* Note that this call must be before processing any SMB
* messages as we need to synchronously process any messages
@@ -188,17 +328,55 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout)
*/
if(smb_oplock_queue != NULL) {
struct pending_message_list *msg = smb_oplock_queue;
- memcpy(buffer, msg->msg_buf, MIN(buffer_len, msg->msg_len));
+ memcpy(buffer, msg->buf.data, MIN(buffer_len, msg->buf.length));
/* Free the message we just copied. */
DLIST_REMOVE(smb_oplock_queue, msg);
- SAFE_FREE(msg->msg_buf);
- SAFE_FREE(msg);
+ free_queued_message(msg);
DEBUG(5,("receive_message_or_smb: returning queued smb message.\n"));
return True;
}
+ /*
+ * Check to see if we already have a message on the deferred open queue
+ * and it's time to schedule.
+ */
+ if(smb_sharing_violation_queue != NULL) {
+ BOOL pop_message = False;
+ struct pending_message_list *msg = smb_sharing_violation_queue;
+
+ if (msg->msg_time.tv_sec == 0 && msg->msg_time.tv_usec == 0) {
+ pop_message = True;
+ } else {
+ struct timeval tv;
+ SMB_BIG_INT tdif;
+
+ GetTimeOfDay(&tv);
+ tdif = usec_time_diff(&msg->msg_time, &tv);
+ if (tdif <= 0) {
+ /* Timed out. Schedule...*/
+ pop_message = True;
+ DEBUG(10,("receive_message_or_smb: queued message timed out.\n"));
+ } else {
+ /* Make a more accurate select timeout. */
+ to.tv_sec = tdif / 1000000;
+ to.tv_usec = tdif % 1000000;
+ pto = &to;
+ DEBUG(10,("receive_message_or_smb: select with timeout of [%u.%06u]\n",
+ (unsigned int)pto->tv_sec, (unsigned int)pto->tv_usec ));
+ }
+ }
+
+ if (pop_message) {
+ memcpy(buffer, msg->buf.data, MIN(buffer_len, msg->buf.length));
+
+ /* We leave this message on the queue so the open code can
+ know this is a retry. */
+ DEBUG(5,("receive_message_or_smb: returning deferred open smb message.\n"));
+ return True;
+ }
+ }
/*
* Setup the select read fd set.
@@ -229,10 +407,7 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout)
FD_SET(smbd_server_fd(),&fds);
maxfd = setup_oplock_select_set(&fds);
- to.tv_sec = timeout / 1000;
- to.tv_usec = (timeout % 1000) * 1000;
-
- selrtn = sys_select(MAX(maxfd,smbd_server_fd())+1,&fds,NULL,NULL,timeout>0?&to:NULL);
+ selrtn = sys_select(MAX(maxfd,smbd_server_fd())+1,&fds,NULL,NULL,pto);
/* if we get EINTR then maybe we have received an oplock
signal - treat this as select returning 1. This is ugly, but
diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c
index fff5385171..bf208b0fa4 100644
--- a/source3/smbd/reply.c
+++ b/source3/smbd/reply.c
@@ -1043,6 +1043,10 @@ int reply_open(connection_struct *conn, char *inbuf,char *outbuf, int dum_size,
if (!fsp) {
END_PROFILE(SMBopen);
+ if (open_was_deferred(SVAL(inbuf,smb_mid))) {
+ /* We have re-scheduled this call. */
+ return -1;
+ }
return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnoaccess);
}
@@ -1132,6 +1136,10 @@ int reply_open_and_X(connection_struct *conn, char *inbuf,char *outbuf,int lengt
if (!fsp) {
END_PROFILE(SMBopenX);
+ if (open_was_deferred(SVAL(inbuf,smb_mid))) {
+ /* We have re-scheduled this call. */
+ return -1;
+ }
return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnoaccess);
}
@@ -1257,6 +1265,10 @@ int reply_mknew(connection_struct *conn, char *inbuf,char *outbuf, int dum_size,
if (!fsp) {
END_PROFILE(SMBcreate);
+ if (open_was_deferred(SVAL(inbuf,smb_mid))) {
+ /* We have re-scheduled this call. */
+ return -1;
+ }
return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnoaccess);
}
@@ -1332,6 +1344,10 @@ int reply_ctemp(connection_struct *conn, char *inbuf,char *outbuf, int dum_size,
if (!fsp) {
END_PROFILE(SMBctemp);
+ if (open_was_deferred(SVAL(inbuf,smb_mid))) {
+ /* We have re-scheduled this call. */
+ return -1;
+ }
return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnoaccess);
}
@@ -1623,8 +1639,13 @@ int reply_unlink(connection_struct *conn, char *inbuf,char *outbuf, int dum_size
DEBUG(3,("reply_unlink : %s\n",name));
status = unlink_internals(conn, dirtype, name);
- if (!NT_STATUS_IS_OK(status))
+ if (!NT_STATUS_IS_OK(status)) {
+ if (open_was_deferred(SVAL(inbuf,smb_mid))) {
+ /* We have re-scheduled this call. */
+ return -1;
+ }
return ERROR_NT(status);
+ }
/*
* Win2k needs a changenotify request response before it will
@@ -3944,6 +3965,10 @@ int reply_mv(connection_struct *conn, char *inbuf,char *outbuf, int dum_size,
status = rename_internals(conn, name, newname, attrs, False);
if (!NT_STATUS_IS_OK(status)) {
END_PROFILE(SMBmv);
+ if (open_was_deferred(SVAL(inbuf,smb_mid))) {
+ /* We have re-scheduled this call. */
+ return -1;
+ }
return ERROR_NT(status);
}
@@ -3989,7 +4014,8 @@ static BOOL copy_file(char *src,char *dest1,connection_struct *conn, int ofun,
return(False);
fsp1 = open_file_shared(conn,src,&src_sbuf,SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_RDONLY),
- (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),FILE_ATTRIBUTE_NORMAL,0,&Access,&action);
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),FILE_ATTRIBUTE_NORMAL,INTERNAL_OPEN_ONLY,
+ &Access,&action);
if (!fsp1)
return(False);
@@ -4002,7 +4028,7 @@ static BOOL copy_file(char *src,char *dest1,connection_struct *conn, int ofun,
ZERO_STRUCTP(&sbuf2);
fsp2 = open_file_shared(conn,dest,&sbuf2,SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_WRONLY),
- ofun,dosattrs,0,&Access,&action);
+ ofun,dosattrs,INTERNAL_OPEN_ONLY,&Access,&action);
if (!fsp2) {
close_file(fsp1,False);
diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c
index 02a6bf6e4b..0ba26a9147 100644
--- a/source3/smbd/trans2.c
+++ b/source3/smbd/trans2.c
@@ -627,6 +627,10 @@ static int call_trans2open(connection_struct *conn, char *inbuf, char *outbuf, i
oplock_request, &rmode,&smb_action);
if (!fsp) {
+ if (open_was_deferred(SVAL(inbuf,smb_mid))) {
+ /* We have re-scheduled this call. */
+ return -1;
+ }
return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess);
}
@@ -3205,7 +3209,7 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
SET_OPEN_MODE(DOS_OPEN_RDWR),
(FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
FILE_ATTRIBUTE_NORMAL,
- 0, &access_mode, &action);
+ INTERNAL_OPEN_ONLY, &access_mode, &action);
if (new_fsp == NULL)
return(UNIXERROR(ERRDOS,ERRbadpath));
@@ -3655,7 +3659,7 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n",
SET_OPEN_MODE(DOS_OPEN_RDWR),
(FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
FILE_ATTRIBUTE_NORMAL,
- 0, &access_mode, &action);
+ INTERNAL_OPEN_ONLY, &access_mode, &action);
if (new_fsp == NULL)
return(UNIXERROR(ERRDOS,ERRbadpath));