r1085: Now it's had some proper user testing, merge in the deferred open fix. I'm
authorJeremy Allison <jra@samba.org>
Tue, 8 Jun 2004 16:14:31 +0000 (16:14 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 15:51:54 +0000 (10:51 -0500)
still doing more testing, but it fixes a behaviour that we've been wrong
on ever since the start of Samba.
Jeremy.

16 files changed:
source/client/client.c
source/include/local.h
source/include/smb.h
source/lib/time.c
source/locking/locking.c
source/printing/nt_printing.c
source/rpc_server/srv_srvsvc_nt.c
source/script/mkproto.awk
source/smbd/close.c
source/smbd/dir.c
source/smbd/nttrans.c
source/smbd/open.c
source/smbd/oplock.c
source/smbd/process.c
source/smbd/reply.c
source/smbd/trans2.c

index a0470315f82baf664a857ffec7dd6239eeb5b783..63d73c2d4cf3d7008fac8d8c537de7fe2fde2ad6 100644 (file)
@@ -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;
 }
index 540365047a2b281a724cbd2e8a64b315f29ec5d7..ee8d672553591a94d139ba89b7b5958de8493be5 100644 (file)
 /* 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
index 54a69d1433aa3dc5dfe93dc46007abfead97d11f..9100701e212597dee1e1362f11af17e191c6df49 100644 (file)
@@ -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.
  */
index faca2cba879b78f62782bfd2fae3d2413350dc8e..e63e0b29659c2ff425e377382c97fd8556e22d04 100644 (file)
@@ -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);
+}
index fd03a25940f8da8ef3472b84167348cad698745e..8f53b55fc54a45e93cf53bc5ba97f914dbb78c63 100644 (file)
@@ -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);
index 909aed6c86613e1820802ae94909555656e62f53..225ff20ec3eb52b8f7a1fb670308cd0f817ea8f6 100644 (file)
@@ -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));
index 77b9be99660ba64f3d66ea6831398449e5b96fc2..087c50451e5d5c42ca597df0f2c346b667d9d042 100644 (file)
@@ -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 */
index b38f405e0da854c6389cc1b08eaa473a446193fc..fbe1bddf357bef2bf6a5f8e75348e4c39eb78939 100644 (file)
@@ -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;
   }
 
index 8b3010c1b2e83c8ab48f47056f2ce61c9ac2fdb3..305377dfc63d3b76e8c6e9ada1a6e61049d23694 100644 (file)
@@ -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
@@ -104,6 +105,32 @@ static int close_filestruct(files_struct *fsp)
        return ret;
 }    
 
+/****************************************************************************
+ 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.
 
@@ -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.
index 06ef23ab8cd64e2661cf4a5ad4d1fd368154092e..b88f687766d88ba9766956024a5159f4950a51c2 100644 (file)
@@ -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;
index 26be4434fdb19b844d86352bf24f5b1fab09617e..e540db234a8d8bdb42b4c275e36462ae235b8955 100644 (file)
@@ -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);
                        }
                } 
index ab5ea236faa426a4f404eefb31cac5cff19ff894..04e074d56eb380467111781496d79df5b496e314 100644 (file)
@@ -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++;
index 19e6956d9ef8b06864a86fdc194a84f8a4149264..6739f5654f150908aafd9b0edafc136c68a10227 100644 (file)
@@ -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)
index ccebd2b86c15a761964f1e0ecd47fd415898b8df..698c8475f7984a77c451fc992f8d51684807da9c 100644 (file)
@@ -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
index fff5385171dd39fb3af755db9507c16c317a767c..bf208b0fa4e449e152ddc3ee71b510777e4ac7ea 100644 (file)
@@ -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);
index 02a6bf6e4bba616d8564b12c66ea4257ff4b855f..0ba26a91479af89f8690431463e5302463f75bb3 100644 (file)
@@ -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));