Reasonably large change to give us *exactly* correct NT delete on close semantics.
authorJeremy Allison <jra@samba.org>
Fri, 23 Oct 1998 03:34:50 +0000 (03:34 +0000)
committerJeremy Allison <jra@samba.org>
Fri, 23 Oct 1998 03:34:50 +0000 (03:34 +0000)
This was trickier than it looks :-). Check out the new DELETE_ON_CLOSE
flag in the share modes and the new code that iterates through all open
files on the same device and inode in files.c and trans2.c

Also changed the code that modifies share mode entries to take
generic function pointers rather than doing a specific thing so
this sort of change should be easier in the future.

Jeremy.

source/include/proto.h
source/include/smb.h
source/locking/locking.c
source/locking/locking_shm.c
source/locking/locking_slow.c
source/smbd/close.c
source/smbd/files.c
source/smbd/open.c
source/smbd/reply.c
source/smbd/trans2.c

index 9b707adeefbc6304a709fe6d64d2fc0b94e4c263..9c78e84017360a9ebaa0844d8f096a39fbc1b5c8 100644 (file)
@@ -538,7 +538,8 @@ int get_share_modes(connection_struct *conn,
                    share_mode_entry **shares);
 void del_share_mode(int token, files_struct *fsp);
 BOOL set_share_mode(int token, files_struct *fsp, uint16 port, uint16 op_type);
-BOOL remove_share_oplock(files_struct *fsp, int token);
+BOOL remove_share_oplock(int token, files_struct *fsp);
+BOOL modify_share_mode(int token, files_struct *fsp, int new_mode);
 int share_mode_forall(void (*fn)(share_mode_entry *, char *));
 void share_status(FILE *f);
 
@@ -2147,6 +2148,8 @@ void file_close_conn(connection_struct *conn);
 void file_init(void);
 void file_close_user(int vuid);
 files_struct *file_find_dit(SMB_DEV_T dev, SMB_INO_T inode, struct timeval *tval);
+files_struct *file_find_di_first(SMB_DEV_T dev, SMB_INO_T inode);
+files_struct *file_find_di_next(files_struct *start_fsp);
 files_struct *file_find_print(void);
 void file_sync_all(connection_struct *conn);
 void fd_ptr_free(file_fd_struct *fd_ptr);
index d71d1cd1eb05343c1a98a36f4744fdd96190192b..2e27fa195f793b9f59fb29a62230da9914606f11 100644 (file)
@@ -201,14 +201,19 @@ implemented */
 #define GET_DENY_MODE(x) (((x)>>SHARE_MODE_SHIFT) & SHARE_MODE_MASK)
 #define SET_DENY_MODE(x) ((x)<<SHARE_MODE_SHIFT)
 
+/* Sync on open file (not sure if used anymore... ?) */
+#define FILE_SYNC_OPENMODE (1<<14)
+#define GET_FILE_SYNC_OPENMODE(x) (((x) & FILE_SYNC_OPENMODE) ? True : False)
+
 /* allow delete on open file mode (used by NT SMB's). */
 #define ALLOW_SHARE_DELETE (1<<15)
-#define GET_ALLOW_SHARE_DELETE(x) (((x) & ALLOW_SHARE_DELETE) ? 1 : 0)
+#define GET_ALLOW_SHARE_DELETE(x) (((x) & ALLOW_SHARE_DELETE) ? True : False)
 #define SET_ALLOW_SHARE_DELETE(x) ((x) ? ALLOW_SHARE_DELETE : 0)
 
-/* Sync on open file (not sure if used anymore... ?) */
-#define FILE_SYNC_OPENMODE (1<<14)
-#define GET_FILE_SYNC_OPENMODE(x) (((x) & FILE_SYNC_OPENMODE) ? 1 : 0)
+/* delete on close flag (used by NT SMB's). */
+#define DELETE_ON_CLOSE_FLAG (1<<16)
+#define GET_DELETE_ON_CLOSE_FLAG(x) (((x) & DELETE_ON_CLOSE_FLAG) ? True : False)
+#define SET_DELETE_ON_CLOSE_FLAG(x) ((x) ? DELETE_ON_CLOSE_FLAG : 0)
 
 /* open disposition values */
 #define FILE_EXISTS_FAIL 0
@@ -669,7 +674,7 @@ struct share_ops {
        int (*get_entries)(connection_struct *, int , SMB_DEV_T , SMB_INO_T , share_mode_entry **);
        void (*del_entry)(int , files_struct *);
        BOOL (*set_entry)(int, files_struct *, uint16 , uint16 );
-       BOOL (*remove_oplock)(files_struct *, int);
+    BOOL (*mod_entry)(int, files_struct *, void (*)(share_mode_entry *, SMB_DEV_T, SMB_INO_T, void *), void *);
        int (*forall)(void (*)(share_mode_entry *, char *));
        void (*status)(FILE *);
 };
index f088720e0a08e46c5238273c110345081cf52f3d..b71b7755248192724967f7a3d17531191f4bfef4 100644 (file)
@@ -229,12 +229,53 @@ BOOL set_share_mode(int token, files_struct *fsp, uint16 port, uint16 op_type)
        return share_ops->set_entry(token, fsp, port, op_type);
 }
 
+/*******************************************************************
+ Static function that actually does the work for the generic function
+ below.
+********************************************************************/
+
+static void remove_share_oplock_fn(share_mode_entry *entry, SMB_DEV_T dev, SMB_INO_T inode, 
+                                   void *param)
+{
+  DEBUG(10,("remove_share_oplock_fn: removing oplock info for entry dev=%x ino=%.0f\n",
+        (unsigned int)dev, (double)inode ));
+  /* Delete the oplock info. */
+  entry->op_port = 0;
+  entry->op_type = 0;
+}
+
 /*******************************************************************
  Remove an oplock port and mode entry from a share mode.
 ********************************************************************/
-BOOL remove_share_oplock(files_struct *fsp, int token)
+
+BOOL remove_share_oplock(int token, files_struct *fsp)
+{
+       return share_ops->mod_entry(token, fsp, remove_share_oplock_fn, NULL);
+}
+
+/*******************************************************************
+ Static function that actually does the work for the generic function
+ below.
+********************************************************************/
+
+static void modify_share_mode_fn(share_mode_entry *entry, SMB_DEV_T dev, SMB_INO_T inode, 
+                                   void *param)
+{
+  int new_share_mode = *(int *)param;
+  DEBUG(10,("modify_share_mode_fn: changing share mode info from %x to %x for entry dev=%x ino=%.0f\n",
+        entry->share_mode, new_share_mode, (unsigned int)dev, (double)inode ));
+  /* Change the share mode info. */
+  entry->share_mode = new_share_mode;
+}
+
+/*******************************************************************
+ Modify a share mode on a file. Used by the delete open file code.
+ Return False on fail, True on success.
+********************************************************************/
+
+BOOL modify_share_mode(int token, files_struct *fsp, int new_mode)
 {
-       return share_ops->remove_oplock(fsp, token);
+       return share_ops->mod_entry(token, fsp, modify_share_mode_fn, (void *)&new_mode);
 }
 
 /*******************************************************************
index 1077cf23eb6cdb91311029ceaf55fe49eb391365..375a8b7f109edbc5df849eb42b0702ef39b9f889 100644 (file)
@@ -495,9 +495,12 @@ static BOOL shm_set_share_mode(int token, files_struct *fsp, uint16 port, uint16
 }
 
 /*******************************************************************
-Remove an oplock port and mode entry from a share mode.
+ Call a generic modify function for a share mode entry.
 ********************************************************************/
-static BOOL shm_remove_share_oplock(files_struct *fsp, int token)
+
+static BOOL shm_mod_share_entry(int token, files_struct *fsp,
+                                void (*mod_fn)(share_mode_entry *, SMB_DEV_T, SMB_INO_T, void *),
+                                void *param)
 {
   SMB_DEV_T dev;
   SMB_INO_T inode;
@@ -518,7 +521,7 @@ static BOOL shm_remove_share_oplock(files_struct *fsp, int token)
 
   if(mode_array[hash_entry] == 0)
   {
-    DEBUG(0,("PANIC ERROR:remove_share_oplock: hash bucket %d empty\n",
+    DEBUG(0,("PANIC ERROR:modify_share_entry: hash bucket %d empty\n",
                   hash_entry));
     return False;
   } 
@@ -543,14 +546,14 @@ static BOOL shm_remove_share_oplock(files_struct *fsp, int token)
    
   if(!found)
   { 
-     DEBUG(0,("ERROR:remove_share_oplock: no entry found for dev=%x ino=%.0f\n", 
+     DEBUG(0,("ERROR:modify_share_entry: no entry found for dev=%x ino=%.0f\n", 
              (unsigned int)dev, (double)inode));
      return False;
   } 
 
   if(file_scanner_p->locking_version != LOCKING_VERSION)
   {
-    DEBUG(0,("ERROR: remove_share_oplock: Deleting old share mode v1=%d dev=%x ino=%.0f\n",
+    DEBUG(0,("ERROR: modify_share_entry: Deleting old share mode v1=%d dev=%x ino=%.0f\n",
             file_scanner_p->locking_version, (unsigned int)dev, (double)inode));
 
     if(file_prev_p == file_scanner_p)
@@ -571,9 +574,14 @@ static BOOL shm_remove_share_oplock(files_struct *fsp, int token)
         (memcmp(&entry_scanner_p->e.time, 
                 &fsp->open_time,sizeof(struct timeval)) == 0) )
     {
-      /* Delete the oplock info. */
-      entry_scanner_p->e.op_port = 0;
-      entry_scanner_p->e.op_type = 0;
+      /*
+       * Call the generic function with the given parameter.
+       */
+
+      DEBUG(5,("modify_share_entry: Calling generic function to modify entry for dev=%x ino=%.0f\n",
+            (unsigned int)dev, (double)inode));
+
+      (*mod_fn)( &entry_scanner_p->e, dev, inode, param);
       found = True;
       break;
     }
@@ -586,7 +594,7 @@ static BOOL shm_remove_share_oplock(files_struct *fsp, int token)
 
   if(!found)
   {
-    DEBUG(0,("ERROR: remove_share_oplock: No oplock granted. dev=%x ino=%.0f\n", 
+    DEBUG(0,("ERROR: modify_share_entry: No entry found for dev=%x ino=%.0f\n", 
             (unsigned int)dev, (double)inode));
     return False;
   }
@@ -594,7 +602,6 @@ static BOOL shm_remove_share_oplock(files_struct *fsp, int token)
   return True;
 }
 
-
 /*******************************************************************
 call the specified function on each entry under management by the
 share mode system
@@ -670,7 +677,7 @@ static struct share_ops share_ops = {
        shm_get_share_modes,
        shm_del_share_mode,
        shm_set_share_mode,
-       shm_remove_share_oplock,
+       shm_mod_share_entry,
        shm_share_forall,
        shm_share_status,
 };
index f2c2d3e9d9a8201b59c111157ae6fd59ed8d3ba7..f1e0fa214965bcaac6ebe5582a6868556e35e4ee 100644 (file)
@@ -827,9 +827,12 @@ mode 0x%X pid=%d\n",fname,fsp->share_mode,pid));
 }
 
 /*******************************************************************
-Remove an oplock port and mode entry from a share mode.
+ Call a generic modify function for a share mode entry.
 ********************************************************************/
-static BOOL slow_remove_share_oplock(files_struct *fsp, int token)
+
+static BOOL slow_mod_share_entry(int token, files_struct *fsp,
+                                void (*mod_fn)(share_mode_entry *, SMB_DEV_T, SMB_INO_T, void *),
+                                void *param)
 {
   pstring fname;
   int fd = (int)token;
@@ -841,20 +844,21 @@ static BOOL slow_remove_share_oplock(files_struct *fsp, int token)
   int pid;
   BOOL found = False;
   BOOL new_file;
+  share_mode_entry entry;
 
   share_name(fsp->conn, fsp->fd_ptr->dev, 
                        fsp->fd_ptr->inode, fname);
 
   if(read_share_file( fsp->conn, fd, fname, &buf, &new_file) != 0)
   {
-    DEBUG(0,("ERROR: remove_share_oplock: Failed to read share file %s\n",
+    DEBUG(0,("ERROR: slow_mod_share_entry: Failed to read share file %s\n",
                   fname));
     return False;
   }
 
   if(new_file == True)
   {
-    DEBUG(0,("ERROR: remove_share_oplock: share file %s is new (size zero), \
+    DEBUG(0,("ERROR: slow_mod_share_entry: share file %s is new (size zero), \
 deleting it.\n", fname));
     delete_share_file(fsp->conn, fname);
     return False;
@@ -862,13 +866,13 @@ deleting it.\n", fname));
 
   num_entries = IVAL(buf,SMF_NUM_ENTRIES_OFFSET);
 
-  DEBUG(5,("remove_share_oplock: share file %s has %d share mode entries.\n",
+  DEBUG(5,("slow_mod_share_entry: share file %s has %d share mode entries.\n",
             fname, num_entries));
 
   /* PARANOIA TEST */
   if(num_entries < 0)
   {
-    DEBUG(0,("PANIC ERROR:remove_share_oplock: num_share_mode_entries < 0 (%d) \
+    DEBUG(0,("PANIC ERROR:slow_mod_share_entry: num_share_mode_entries < 0 (%d) \
 for share file %s\n", num_entries, fname));
     return False;
   }
@@ -876,7 +880,7 @@ for share file %s\n", num_entries, fname));
   if(num_entries == 0)
   {
     /* No entries - just delete the file. */
-    DEBUG(0,("remove_share_oplock: share file %s has no share mode entries - deleting.\n",
+    DEBUG(0,("slow_mod_share_entry: share file %s has no share mode entries - deleting.\n",
               fname));
     if(buf)
       free(buf);
@@ -886,10 +890,6 @@ for share file %s\n", num_entries, fname));
 
   pid = getpid();
 
-  /* Go through the entries looking for the particular one
-     we have set - remove the oplock settings on it.
-  */
-
   base = buf + SMF_HEADER_LENGTH + SVAL(buf,SMF_FILENAME_LEN_OFFSET);
 
   for(i = 0; i < num_entries; i++)
@@ -902,18 +902,41 @@ for share file %s\n", num_entries, fname));
        (IVAL(p,SME_PID_OFFSET) != pid))
       continue;
 
-    DEBUG(5,("remove_share_oplock: clearing oplock on entry number %d (of %d) \
+    DEBUG(5,("slow_mod_share_entry: Calling generic function to modify entry number %d (of %d) \
 from the share file %s\n", i, num_entries, fname));
 
-    SSVAL(p,SME_PORT_OFFSET,0);
-    SSVAL(p,SME_OPLOCK_TYPE_OFFSET,0);
+    /*
+     * Copy into the share_mode_entry structure and then call 
+     * the generic function with the given parameter.
+     */
+
+    entry.pid = IVAL(p,SME_PID_OFFSET);
+    entry.op_port = SVAL(p,SME_PORT_OFFSET);
+    entry.op_type = SVAL(p,SME_OPLOCK_TYPE_OFFSET);
+    entry.share_mode = IVAL(p,SME_SHAREMODE_OFFSET);
+    entry.time.tv_sec = IVAL(p,SME_SEC_OFFSET)
+    entry.time.tv_sec = IVAL(p,SME_USEC_OFFSET);
+
+    (*mod_fn)( &entry, fsp->fd_ptr->dev, fsp->fd_ptr->inode, param);
+
+    /*
+     * Now copy any changes the function made back into the buffer.
+     */
+
+    SIVAL(p,SME_PID_OFFSET, entry.pid)
+    SSVAL(p,SME_PORT_OFFSET,entry.op_port);
+    SSVAL(p,SME_OPLOCK_TYPE_OFFSET,entry.op_type);
+    SIVAL(p,SME_SHAREMODE_OFFSET,entry.share_mode);
+    SIVAL(p,SME_SEC_OFFSET,entry.time.tv_sec)
+    SIVAL(p,SME_USEC_OFFSET,entry.time.tv_sec);
+
     found = True;
     break;
   }
 
   if(!found)
   {
-    DEBUG(0,("remove_share_oplock: entry not found in share file %s\n", fname));
+    DEBUG(0,("slow_mod_share_entry: entry not found in share file %s\n", fname));
     if(buf)
       free(buf);
     return False;
@@ -922,7 +945,7 @@ from the share file %s\n", i, num_entries, fname));
   /* Re-write the file - and truncate it at the correct point. */
   if(sys_lseek(fd, (SMB_OFF_T)0, SEEK_SET) != 0)
   {
-    DEBUG(0,("ERROR: remove_share_oplock: lseek failed to reset to \
+    DEBUG(0,("ERROR: slow_mod_share_entry: lseek failed to reset to \
 position 0 for share mode file %s (%s)\n", fname, strerror(errno)));
     if(buf)
       free(buf);
@@ -932,7 +955,7 @@ position 0 for share mode file %s (%s)\n", fname, strerror(errno)));
   fsize = (base - buf) + (SMF_ENTRY_LENGTH*num_entries);
   if(write(fd, buf, fsize) != fsize)
   {
-    DEBUG(0,("ERROR: remove_share_oplock: failed to re-write share \
+    DEBUG(0,("ERROR: slow_mod_share_entry: failed to re-write share \
 mode file %s (%s)\n", fname, strerror(errno)));
     if(buf)
       free(buf);
@@ -1048,7 +1071,7 @@ static struct share_ops share_ops = {
        slow_get_share_modes,
        slow_del_share_mode,
        slow_set_share_mode,
-       slow_remove_share_oplock,
+       slow_mod_share_entry,
        slow_share_forall,
        slow_share_status,
 };
index 2dba691a1c239b9d65bfc3aff787f079a48af248..50ad01f5755d56f33ce4c13d6d0d61429ad223b4 100644 (file)
@@ -142,6 +142,7 @@ void close_file(files_struct *fsp, BOOL normal_close)
         */
 
     if (normal_close && last_reference && delete_on_close) {
+        DEBUG(5,("close_file: file %s. Delete on close was set - deleting file.\n"));
                if(dos_unlink(fsp->fsp_name) != 0)
           DEBUG(0,("close_file: file %s. Delete on close was set and unlink failed \
 with error %s\n", fsp->fsp_name, strerror(errno) ));
index c369a45bab3e3168ab0cb1381cc838bb9732f55d..e58c3834a00f02c3178aeac9574cfc7b9fa80c2d 100644 (file)
@@ -277,6 +277,41 @@ files_struct *file_find_dit(SMB_DEV_T dev, SMB_INO_T inode, struct timeval *tval
        return NULL;
 }
 
+/****************************************************************************
+ Find the first fsp given a device and inode.
+****************************************************************************/
+
+files_struct *file_find_di_first(SMB_DEV_T dev, SMB_INO_T inode)
+{
+    files_struct *fsp;
+
+    for (fsp=Files;fsp;fsp=fsp->next) {
+        if (fsp->open &&
+            fsp->fd_ptr->dev == dev &&
+            fsp->fd_ptr->inode == inode )
+            return fsp;
+    }
+
+    return NULL;
+}
+
+/****************************************************************************
+ Find the next fsp having the same device and inode.
+****************************************************************************/
+
+files_struct *file_find_di_next(files_struct *start_fsp)
+{
+    files_struct *fsp;
+
+    for (fsp = start_fsp->next;fsp;fsp=fsp->next) {
+        if (fsp->open &&
+            fsp->fd_ptr->dev == start_fsp->fd_ptr->dev &&
+            fsp->fd_ptr->inode == start_fsp->fd_ptr->inode )
+            return fsp;
+    }
+
+    return NULL;
+}
 
 /****************************************************************************
 find a fsp that is open for printing
index b6b2ef5bb8a4400e1ce407adf3b5c058e9cf688b..aac4b02fba336758587da40a453ae5b825fa1b56 100644 (file)
@@ -659,6 +659,26 @@ static int check_share_mode( share_mode_entry *share, int deny_mode,
   int old_open_mode = GET_OPEN_MODE(share->share_mode);
   int old_deny_mode = GET_DENY_MODE(share->share_mode);
 
+  /*
+   * Setup the potential error to return.
+   */
+
+  unix_ERR_class = ERRDOS;
+  unix_ERR_code = ERRbadshare;
+
+  /*
+   * Don't allow any open once the delete on close flag has been
+   * set.
+   */
+
+  if(GET_DELETE_ON_CLOSE_FLAG(share->share_mode))
+  {
+    DEBUG(5,("check_share_mode: Failing open on file %s as delete on close flag is set.\n",
+          fname ));
+    unix_ERR_code = ERRnoaccess;
+    return False;
+  }
+
   if (old_deny_mode > 4 || old_open_mode > 2)
   {
     DEBUG(0,("Invalid share mode found (%d,%d,%d) on file %s\n",
@@ -688,6 +708,7 @@ static int check_share_mode( share_mode_entry *share, int deny_mode,
       *flags = O_WRONLY;
 
   }
+
   return True;
 }
 
@@ -865,8 +886,6 @@ dev = %x, inode = %.0f\n", old_shares[i].op_type, fname, (unsigned int)dev, (dou
             free((char *)old_shares);
             unlock_share_entry(conn, dev, inode, token);
             errno = EACCES;
-            unix_ERR_class = ERRDOS;
-            unix_ERR_code = ERRbadshare;
             return;
           }
 
index bc19f1a93131d30c2ddec42e4d50a8faa0610d3d..babdd2056c4dc68a99b324adfea66f2f1597b5fd 100644 (file)
@@ -3615,7 +3615,7 @@ no oplock granted on this file.\n", fsp->fnum));
 
     /* Remove the oplock flag from the sharemode. */
     lock_share_entry(fsp->conn, dev, inode, &token);
-    if(remove_share_oplock(fsp, token)==False) {
+    if(remove_share_oplock(token, fsp)==False) {
 
            DEBUG(0,("reply_lockingX: failed to remove share oplock for fnum %d, \
 dev = %x, inode = %.0f\n", fsp->fnum, (unsigned int)dev, (double)inode));
index f8d90cd87182df38610f017fb0671f5ff67455b9..1c2bdb1ddfac91d2e5e3509557293b05b1dade4d 100644 (file)
@@ -1655,14 +1655,64 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
     {
       if (tran_call == TRANSACT2_SETFILEINFO) {
         files_struct *fsp = file_fsp(params,0);
+        BOOL delete_on_close = (CVAL(pdata,0) ? True : False);
+
         if(fsp->is_directory)
           return(ERROR(ERRDOS,ERRnoaccess));
+
+        /*
+         * We can only set the delete on close flag if
+         * the share mode contained ALLOW_SHARE_DELETE
+         */
+
+        if(!GET_ALLOW_SHARE_DELETE(fsp->share_mode))
+          return(ERROR(ERRDOS,ERRnoaccess));
+
+        /*
+         * If the flag has changed from its previous value then
+         * modify the share mode entry for all files we have open
+         * on this device and inode to tell other smbds we have 
+         * changed the delete on close flag.
+         */
+
+        if(GET_DELETE_ON_CLOSE_FLAG(fsp->share_mode) != delete_on_close)
+        {
+          int token;
+          files_struct *iterate_fsp;
+          SMB_DEV_T dev = fsp->fd_ptr->dev;
+          SMB_INO_T inode = fsp->fd_ptr->inode;
+          int new_share_mode = (delete_on_close ? 
+                                  (fsp->share_mode | DELETE_ON_CLOSE_FLAG) :
+                                  (fsp->share_mode & ~DELETE_ON_CLOSE_FLAG) );
+
+          DEBUG(10,("call_trans2setfilepathinfo: %s delete on close flag for fnum = %d, file %s\n",
+               delete_on_close ? "Adding" : "Removing", fsp->fnum, fsp->fsp_name ));
+
+          if(lock_share_entry(fsp->conn, dev, inode, &token) == False)
+            return(ERROR(ERRDOS,ERRnoaccess));
+
+          /*
+           * Go through all files we have open on the same device and
+           * inode (hanging off the same hash bucket) and set the DELETE_ON_CLOSE_FLAG.
+           */
+
+          for(iterate_fsp = file_find_di_first(dev, inode); iterate_fsp;
+                                iterate_fsp = file_find_di_next(iterate_fsp))
+          {
+            if(modify_share_mode(token, iterate_fsp, new_share_mode)==False)
+              DEBUG(0,("call_trans2setfilepathinfo: failed to change delete on close for fnum %d, \
+dev = %x, inode = %.0f\n", fsp->fnum, (unsigned int)dev, (double)inode));
+          }
+
+          unlock_share_entry(fsp->conn, dev, inode, token);
+        }
+
         /*
          * Set the delete on close flag in the reference
          * counted struct. Delete when the last reference
          * goes away.
          */
-        fsp->fd_ptr->delete_on_close = CVAL(pdata,0);
+        fsp->fd_ptr->delete_on_close = delete_on_close;
       } else
         return(ERROR(ERRDOS,ERRunknownlevel));
       break;