This is a big, rather ugly patch. Whilst investigating the files not truncated
authorJeremy Allison <jra@samba.org>
Fri, 30 Mar 2001 08:57:24 +0000 (08:57 +0000)
committerJeremy Allison <jra@samba.org>
Fri, 30 Mar 2001 08:57:24 +0000 (08:57 +0000)
when copying to a full disk problem, I discovered that we were not allowing
the delete on close flag to be set properly, this led to other things, and
after investigation of the proper delete on close semantics and their relationship
to the file_share_delete flag I discovered there were some cases where we
weren't doing the deny modes properly. And this after only 5 years working
on them..... :-) :-).
So here's the latest attempt. I realised the delete on close flag needs to
be set across all smbds with a dev/ino pair open - in addition, the delete
on close flag, allow share delete and delete access requested all need to
be stored in the share mode tdb.
The "delete_on_close" entry in the fsp struct is now redundant and should
really be removed. This may also mean we can get rid of the "iterate_fsp"
calls that I didn't like adding in the first place. Whilst doing this patch,
I also discovered we needed to do the se_map_generic() call for file opens
and POSIX ACL mapping, so I added that also.
This code, although ugly, now passes the deny mode torture tests plus the
delete on close tests I added. I do need to add one more multiple connection
delete on close test to make sure I got the semantics exactly right, plus we
should also (as Andrew suggested) move to random testing here.

The good news is that NT should now correctly delete the file on disk
full error when copying to a disk :-).

Jeremy.
(This used to be commit 51987684bd231c744da2e5f3705fd236d5616173)

source3/include/proto.h
source3/include/smb.h
source3/libsmb/clifile.c
source3/locking/locking.c
source3/smbd/close.c
source3/smbd/nttrans.c
source3/smbd/open.c
source3/smbd/posix_acls.c
source3/smbd/trans2.c

index 9b6aab6645ca6c34eaab31a6334dec8d54b0e310..12a7478e0abd35b68146f781e2ceeaf405a2c177 100644 (file)
@@ -1155,11 +1155,11 @@ void unlock_share_entry_fsp(files_struct *fsp);
 int get_share_modes(connection_struct *conn, 
                    SMB_DEV_T dev, SMB_INO_T inode, 
                    share_mode_entry **shares);
-void del_share_mode(files_struct *fsp);
+size_t del_share_mode(files_struct *fsp, share_mode_entry **ppse);
 BOOL set_share_mode(files_struct *fsp, uint16 port, uint16 op_type);
 BOOL remove_share_oplock(files_struct *fsp);
 BOOL downgrade_share_oplock(files_struct *fsp);
-BOOL modify_share_mode(files_struct *fsp, int new_mode, uint16 new_oplock);
+BOOL modify_delete_flag( SMB_DEV_T dev, SMB_INO_T inode, BOOL delete_on_close);
 int share_mode_forall(SHAREMODE_FN(fn));
 
 /*The following definitions come from  locking/posix.c  */
index ba0a02e950703d66a8985771a92f0d8917cb8477..e154c1eda2ad338075ab9980588c65b7ba930650 100644 (file)
@@ -123,6 +123,11 @@ implemented */
 #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)
 
+/* was delete access requested in NT open ? */
+#define DELETE_ACCESS_REQUESTED (1<<17)
+#define GET_DELETE_ACCESS_REQUESTED(x) (((x) & DELETE_ACCESS_REQUESTED) ? True : False)
+#define SET_DELETE_ACCESS_REQUESTED(x) ((x) ? DELETE_ACCESS_REQUESTED : 0)
+
 /* open disposition values */
 #define FILE_EXISTS_FAIL 0
 #define FILE_EXISTS_OPEN 1
@@ -1060,7 +1065,7 @@ struct bitmap {
 #define FILE_READ_ATTRIBUTES  0x080
 #define FILE_WRITE_ATTRIBUTES 0x100
 
-#define FILE_ALL_ATTRIBUTES   0x1FF
+#define FILE_ALL_ACCESS       0x1FF
 
 /* the desired access to use when opening a pipe */
 #define DESIRED_ACCESS_PIPE 0x2019f
@@ -1088,6 +1093,19 @@ struct bitmap {
 #define GENERIC_WRITE_ACCESS   (1<<30)
 #define GENERIC_READ_ACCESS   (((unsigned)1)<<31)
 
+/* Mapping of generic access rights for files to specific rights. */
+
+#define FILE_GENERIC_ALL (STANDARD_RIGHTS_REQUIRED_ACCESS| SYNCHRONIZE_ACCESS|FILE_ALL_ACCESS)
+
+#define FILE_GENERIC_READ (STANDARD_RIGHTS_READ_ACCESS|FILE_READ_DATA|FILE_READ_ATTRIBUTES|\
+                                                       FILE_READ_EA|SYNCHRONIZE_ACCESS)
+
+#define FILE_GENERIC_WRITE (STANDARD_RIGHTS_WRITE_ACCESS|FILE_WRITE_DATA|FILE_WRITE_ATTRIBUTES|\
+                                                       FILE_WRITE_EA|FILE_APPEND_DATA|SYNCHRONIZE_ACCESS)
+
+#define FILE_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE_ACCESS|FILE_READ_ATTRIBUTES|\
+                                                               FILE_EXECUTE|SYNCHRONIZE_ACCESS)
+
 /* Mapping of access rights to UNIX perms. */
 #define UNIX_ACCESS_RWX (UNIX_ACCESS_R|UNIX_ACCESS_W|UNIX_ACCESS_X)
 #define UNIX_ACCESS_R (READ_CONTROL_ACCESS|SYNCHRONIZE_ACCESS|\
index e90bd7c41fae2cf7db598c07bc80450a55f5cd8d..d62a0a417a68ee6531b75c78a0605a4bea51570b 100644 (file)
@@ -164,6 +164,9 @@ BOOL cli_rmdir(struct cli_state *cli, char *dname)
        }
 
        return True;
+}
+
+/****************************************************************************
  Set or clear the delete on close flag.
 ****************************************************************************/
 
@@ -183,7 +186,7 @@ int cli_nt_delete_on_close(struct cli_state *cli, int fnum, BOOL flag)
        data = flag ? 1 : 0;
 
        if (!cli_send_trans(cli, SMBtrans2,
-                                               NULL, 0,                        /* name, length */
+                                               NULL,                        /* name */
                                                -1, 0,                          /* fid, flags */
                                                &setup, 1, 0,                   /* setup, length, max */
                                                param, param_len, 2,            /* param, length, max */
@@ -204,9 +207,6 @@ int cli_nt_delete_on_close(struct cli_state *cli, int fnum, BOOL flag)
        return True;
 }
 
-/****************************************************************************
-}
-
 /****************************************************************************
  open a file - exposing the full horror of the NT API :-).
  Used in smbtorture.
index 118d59cc10c1115b9f5e3bb19326d058b8059c40..5824287e91372c1eb5af6839452bc24bbf578af8 100644 (file)
@@ -331,19 +331,24 @@ int get_share_modes(connection_struct *conn,
 }
 
 /*******************************************************************
- Del the share mode of a file for this process
+ Del the share mode of a file for this process. Return the number
+ of entries left, and a memdup'ed copy of the entry deleted.
 ********************************************************************/
-void del_share_mode(files_struct *fsp)
+
+size_t del_share_mode(files_struct *fsp, share_mode_entry **ppse)
 {
        TDB_DATA dbuf;
        struct locking_data *data;
        int i, del_count=0;
        share_mode_entry *shares;
        pid_t pid = sys_getpid();
+       size_t count;
+
+       *ppse = NULL;
 
        /* read in the existing share modes */
        dbuf = tdb_fetch(tdb, locking_key_fsp(fsp));
-       if (!dbuf.dptr) return;
+       if (!dbuf.dptr) return 0;
 
        data = (struct locking_data *)dbuf.dptr;
        shares = (share_mode_entry *)(dbuf.dptr + sizeof(*data));
@@ -354,6 +359,7 @@ void del_share_mode(files_struct *fsp)
                if (shares[i].pid == pid &&
                    memcmp(&shares[i].time, 
                           &fsp->open_time,sizeof(struct timeval)) == 0) {
+                       *ppse = memdup(&shares[i], sizeof(*shares));
                        data->num_share_mode_entries--;
                        memmove(&shares[i], &shares[i+1], 
                                dbuf.dsize - (sizeof(*data) + (i+1)*sizeof(*shares)));
@@ -366,6 +372,8 @@ void del_share_mode(files_struct *fsp)
        /* the record has shrunk a bit */
        dbuf.dsize -= del_count * sizeof(*shares);
 
+       count = data->num_share_mode_entries;
+
        /* store it back in the database */
        if (data->num_share_mode_entries == 0) {
                tdb_delete(tdb, locking_key_fsp(fsp));
@@ -374,6 +382,7 @@ void del_share_mode(files_struct *fsp)
        }
 
        free(dbuf.dptr);
+       return count;
 }
 
 /*******************************************************************
@@ -529,42 +538,40 @@ BOOL downgrade_share_oplock(files_struct *fsp)
        return mod_share_mode(fsp, downgrade_share_oplock_fn, NULL);
 }
 
-
 /*******************************************************************
- Static function that actually does the work for the generic function
below.
+ Get/Set the delete on close flag in a set of share modes.
Return False on fail, True on success.
 ********************************************************************/
-struct mod_val {
-       int new_share_mode;
-       uint16 new_oplock;
-};
 
-static void modify_share_mode_fn(share_mode_entry *entry, SMB_DEV_T dev, SMB_INO_T inode, 
-                                   void *param)
+BOOL modify_delete_flag( SMB_DEV_T dev, SMB_INO_T inode, BOOL delete_on_close)
 {
-       struct mod_val *mvp = (struct mod_val *)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, mvp->new_share_mode, (unsigned int)dev, (double)inode ));
-       DEBUG(10,("modify_share_mode_fn: changing oplock state from %x to %x for entry dev=%x ino=%.0f\n",
-        entry->op_type, (int)mvp->new_oplock, (unsigned int)dev, (double)inode ));
-       /* Change the share mode info. */
-       entry->share_mode = mvp->new_share_mode;
-       entry->op_type = mvp->new_oplock;
-}
+       TDB_DATA dbuf;
+       struct locking_data *data;
+       int i;
+       share_mode_entry *shares;
 
-/*******************************************************************
- 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(files_struct *fsp, int new_mode, uint16 new_oplock)
-{
-       struct mod_val mv;
+       /* read in the existing share modes */
+       dbuf = tdb_fetch(tdb, locking_key(dev, inode));
+       if (!dbuf.dptr) return False;
+
+       data = (struct locking_data *)dbuf.dptr;
+       shares = (share_mode_entry *)(dbuf.dptr + sizeof(*data));
 
-       mv.new_share_mode = new_mode;
-       mv.new_oplock = new_oplock;
+       /* Set/Unset the delete on close element. */
+       for (i=0;i<data->num_share_mode_entries;i++,shares++) {
+               shares->share_mode = (delete_on_close ?
+                            (shares->share_mode | DELETE_ON_CLOSE_FLAG) :
+                            (shares->share_mode & ~DELETE_ON_CLOSE_FLAG) );
+       }
 
-       return mod_share_mode(fsp, modify_share_mode_fn, (void *)&mv);
+       /* store it back */
+       if (data->num_share_mode_entries) {
+               if (tdb_store(tdb, locking_key(dev,inode), dbuf, TDB_REPLACE)==-1)
+                       return False;
+       }
+
+       free(dbuf.dptr);
+       return True;
 }
 
 
index 56aad643914d5e7d59d43de3222bb84aec51219a..c290ee6f894fddcd636e2a84620030bbc6f7b8e2 100644 (file)
@@ -99,7 +99,9 @@ static int close_filestruct(files_struct *fsp)
 
 static int close_normal_file(files_struct *fsp, BOOL normal_close)
 {
-       BOOL delete_on_close = fsp->delete_on_close;
+       share_mode_entry *share_entry = NULL;
+       size_t share_entry_count = 0;
+       BOOL delete_on_close = False;
        connection_struct *conn = fsp->conn;
        int err = 0;
        int err1 = 0;
@@ -120,21 +122,25 @@ static int close_normal_file(files_struct *fsp, BOOL normal_close)
                return 0;
        }
 
-       lock_share_entry_fsp(fsp);
-       del_share_mode(fsp);
-       unlock_share_entry_fsp(fsp);
+       /*
+        * Lock the share entries, and determine if we should delete
+        * on close. If so delete whilst the lock is still in effect.
+        * This prevents race conditions with the file being created. JRA.
+        */
 
-       if(EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type))
-               release_file_oplock(fsp);
+       lock_share_entry_fsp(fsp);
+       share_entry_count = del_share_mode(fsp, &share_entry);
 
-       locking_close_file(fsp);
+       /*
+        * We delete on close if it's the last open, and the
+        * delete on close flag was set in the entry we just deleted.
+        */
 
-       err = fd_close(conn, fsp);
+       if ((share_entry_count == 0) && share_entry && 
+                       GET_DELETE_ON_CLOSE_FLAG(share_entry->share_mode) )
+               delete_on_close = True;
 
-       /* check for magic scripts */
-       if (normal_close) {
-               check_magic(fsp,conn);
-       }
+       safe_free(share_entry);
 
        /*
         * NT can set delete_on_close of the last open
@@ -157,6 +163,21 @@ with error %s\n", fsp->fsp_name, strerror(errno) ));
         }
     }
 
+       unlock_share_entry_fsp(fsp);
+
+       if(EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type))
+               release_file_oplock(fsp);
+
+       locking_close_file(fsp);
+
+       err = fd_close(conn, fsp);
+
+       /* check for magic scripts */
+       if (normal_close) {
+               check_magic(fsp,conn);
+       }
+
+
        DEBUG(2,("%s closed file %s (numopen=%d) %s\n",
                 conn->user,fsp->fsp_name,
                 conn->num_files_open, err ? strerror(err) : ""));
index 58b484f44d6207a3ac258427b1334c0f800d1245..b67815ff6946681ad2848318ef898bc3ead410ac 100644 (file)
@@ -47,6 +47,15 @@ static char *known_nt_pipes[] = {
   NULL
 };
 
+/* Map generic permissions to file object specific permissions */
+struct generic_mapping file_generic_mapping = {
+    FILE_GENERIC_READ,
+    FILE_GENERIC_WRITE,
+    FILE_GENERIC_EXECUTE,
+    FILE_GENERIC_ALL
+};
+
 /****************************************************************************
  Send the required number of replies back.
  We assume all fields other than the data fields are
@@ -332,6 +341,12 @@ static int map_share_mode( BOOL *pstat_open_only, char *fname,
 
   *pstat_open_only = False;
 
+  /*
+   * Convert GENERIC bits to specific bits.
+   */
+
+  se_map_generic(&desired_access, &file_generic_mapping);
+
   switch( desired_access & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA) ) {
   case FILE_READ_DATA:
     smb_open_mode = DOS_OPEN_RDONLY;
@@ -395,8 +410,22 @@ static int map_share_mode( BOOL *pstat_open_only, char *fname,
    * JRA.
    */
 
-  if(share_access & FILE_SHARE_DELETE)
+  if(share_access & FILE_SHARE_DELETE) {
     smb_open_mode |= ALLOW_SHARE_DELETE;
+    DEBUG(10,("map_share_mode: FILE_SHARE_DELETE requested. open_mode = %x\n", smb_open_mode));
+  }
+
+  /*
+   * We need to store the intent to open for Delete. This
+   * is what determines if a delete on close flag can be set.
+   * This is the wrong way (and place) to store this, but for 2.2 this
+   * is the only practical way. JRA.
+   */
+
+  if(desired_access & DELETE_ACCESS) {
+    smb_open_mode |= DELETE_ACCESS_REQUESTED;
+    DEBUG(10,("map_share_mode: DELETE_ACCESS requested. open_mode = %x\n", smb_open_mode));
+  }
 
   /* Add in the requested share mode. */
   switch( share_access & (FILE_SHARE_READ|FILE_SHARE_WRITE)) {
index c60112145994d6c17ffab6ee5d6c28cb417a498e..5f8657262a20ae23bca5543c4ac1815f9f0efb3b 100644 (file)
@@ -341,18 +341,19 @@ static int access_table(int new_deny,int old_deny,int old_mode,
 check if we can open a file with a share mode
 ****************************************************************************/
 
-static int check_share_mode( share_mode_entry *share, int deny_mode, 
+static int check_share_mode( share_mode_entry *share, int share_mode, 
                             const char *fname, BOOL fcbopen, int *flags)
 {
+       int deny_mode = GET_DENY_MODE(share_mode);
        int old_open_mode = GET_OPEN_MODE(share->share_mode);
        int old_deny_mode = GET_DENY_MODE(share->share_mode);
 
        /*
-        * Don't allow any open once the delete on close flag has been
+        * Don't allow any opens once the delete on close flag has been
         * set.
         */
 
-       if(GET_DELETE_ON_CLOSE_FLAG(share->share_mode)) {
+       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_class = ERRDOS;
@@ -360,6 +361,35 @@ static int check_share_mode( share_mode_entry *share, int deny_mode,
                return False;
        }
 
+       /*
+        * If delete access was requested and the existing share mode doesn't have
+        * ALLOW_SHARE_DELETE then deny.
+        */
+
+       if (GET_DELETE_ACCESS_REQUESTED(share_mode) && !GET_ALLOW_SHARE_DELETE(share->share_mode)) {
+               DEBUG(5,("check_share_mode: Failing open on file %s as delete access requested and allow share delete not set.\n",
+                       fname ));
+               unix_ERR_class = ERRDOS;
+               unix_ERR_code = ERRbadshare;
+
+               return False;
+       }
+
+       /*
+        * The inverse of the above.
+        * If delete access was granted and the new share mode doesn't have
+        * ALLOW_SHARE_DELETE then deny.
+        */
+
+       if (GET_DELETE_ACCESS_REQUESTED(share->share_mode) && !GET_ALLOW_SHARE_DELETE(share_mode)) {
+               DEBUG(5,("check_share_mode: Failing open on file %s as delete access granted and allow share delete not requested.\n",
+                       fname ));
+               unix_ERR_class = ERRDOS;
+               unix_ERR_code = ERRbadshare;
+
+               return False;
+       }
+
        {
                int access_allowed = access_table(deny_mode,old_deny_mode,old_open_mode,
                                                                                (share->pid == sys_getpid()),is_executable(fname));
@@ -405,7 +435,6 @@ static int open_mode_check(connection_struct *conn, const char *fname, SMB_DEV_T
   int oplock_contention_count = 0;
   share_mode_entry *old_shares = 0;
   BOOL fcbopen = False;
-  int deny_mode = GET_DENY_MODE(share_mode);
   BOOL broke_oplock;   
 
   if(GET_OPEN_MODE(share_mode) == DOS_OPEN_FCB)
@@ -473,7 +502,7 @@ dev = %x, inode = %.0f\n", old_shares[i].op_type, fname, (unsigned int)dev, (dou
       /* someone else has a share lock on it, check to see 
          if we can too */
 
-      if(check_share_mode(share_entry, deny_mode, fname, fcbopen, p_flags) == False) {
+      if(check_share_mode(share_entry, share_mode, fname, fcbopen, p_flags) == False) {
         free((char *)old_shares);
         errno = EACCES;
         return -1;
@@ -532,6 +561,7 @@ files_struct *open_file_shared(connection_struct *conn,char *fname, SMB_STRUCT_S
        int flags2=0;
        int deny_mode = GET_DENY_MODE(share_mode);
        BOOL allow_share_delete = GET_ALLOW_SHARE_DELETE(share_mode);
+       BOOL delete_access_requested = GET_DELETE_ACCESS_REQUESTED(share_mode);
        BOOL file_existed = VALID_STAT(*psbuf);
        BOOL fcbopen = False;
        SMB_DEV_T dev = 0;
@@ -768,7 +798,10 @@ files_struct *open_file_shared(connection_struct *conn,char *fname, SMB_STRUCT_S
 
        fsp->share_mode = SET_DENY_MODE(deny_mode) | 
                                                SET_OPEN_MODE(open_mode) | 
-                                               SET_ALLOW_SHARE_DELETE(allow_share_delete);
+                                               SET_ALLOW_SHARE_DELETE(allow_share_delete) |
+                                               SET_DELETE_ACCESS_REQUESTED(delete_access_requested);
+
+       DEBUG(10,("open_file_shared : share_mode = %x\n", fsp->share_mode ));
 
        if (Access)
                (*Access) = open_mode;
index 5b0c22c77e5bc44d7d4f3f11030befef57d92ef1..aa8f9376c767f254e2d3ed3a7a2ffa8bb491fe0b 100644 (file)
@@ -559,6 +559,7 @@ static BOOL create_canon_ace_lists(files_struct *fsp,
                                                        SEC_ACL *dacl)
 {
        extern DOM_SID global_sid_World;
+       extern struct generic_mapping *file_generic_mapping;
        BOOL all_aces_are_inherit_only = (fsp->is_directory ? True : False);
        canon_ace *file_ace = NULL;
        canon_ace *dir_ace = NULL;
@@ -589,8 +590,13 @@ static BOOL create_canon_ace_lists(files_struct *fsp,
                 * to be so. Any other bits override the UNIX_ACCESS_NONE bit.
                 */
 
-               psa->info.mask &= (GENERIC_ALL_ACCESS|GENERIC_EXECUTE_ACCESS|GENERIC_WRITE_ACCESS|
-                                                       GENERIC_READ_ACCESS|UNIX_ACCESS_NONE|FILE_ALL_ATTRIBUTES);
+               /*
+                * Convert GENERIC bits to specific bits.
+                */
+               se_map_generic(&psa->info.mask, &file_generic_mapping);
+
+               psa->info.mask &= (UNIX_ACCESS_NONE|FILE_ALL_ACCESS);
 
                if(psa->info.mask != UNIX_ACCESS_NONE)
                        psa->info.mask &= ~UNIX_ACCESS_NONE;
index 5a7a5c72cb1ce8fc24ab66a146c493f53313a1ad..be130247c0ec35f4befc54f17953a0e76b893853 100644 (file)
@@ -1800,144 +1800,94 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
 
     case SMB_SET_FILE_DISPOSITION_INFO: /* Set delete on close for open file. */
     {
-      if ((tran_call == TRANSACT2_SETFILEINFO) && (fsp != NULL))
-      {
-        BOOL delete_on_close = (CVAL(pdata,0) ? True : False);
+               BOOL delete_on_close = (CVAL(pdata,0) ? True : False);
 
-        if(fsp->is_directory)
-        {
-          fsp->directory_delete_on_close = delete_on_close;
-          DEBUG(10, ("call_trans2setfilepathinfo: %s delete on close flag for fnum = %d, directory %s\n",
-                delete_on_close ? "Added" : "Removed", fsp->fnum, fsp->fsp_name ));
+               if (tran_call != TRANSACT2_SETFILEINFO)
+                       return(ERROR(ERRDOS,ERRunknownlevel));
 
-        }
-       else if(fsp->stat_open)
-       {
-          DEBUG(10, ("call_trans2setfilepathinfo: %s delete on close flag for fnum = %d, stat open %s\n",
-                delete_on_close ? "Added" : "Removed", fsp->fnum, fsp->fsp_name ));
-       }
-        else
-        {
+               if (fsp == NULL)
+                       return(UNIXERROR(ERRDOS,ERRbadfid));
 
-          /*
-           * 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 been set 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(delete_on_close && !GET_DELETE_ON_CLOSE_FLAG(fsp->share_mode))
-          {
-            int i;
-            files_struct *iterate_fsp;
-            SMB_DEV_T dev = fsp->dev;
-            SMB_INO_T inode = fsp->inode;
-            int num_share_modes;
-            share_mode_entry *current_shares = NULL;
-
-            if (lock_share_entry_fsp(fsp) == False)
-              return(ERROR(ERRDOS,ERRnoaccess));
-
-            /*
-             * Before we allow this we need to ensure that all current opens
-             * on the file have the GET_ALLOW_SHARE_DELETE flag set. If they
-             * do not then we deny this (as we are essentially deleting the
-             * file at this point.
-             */
-
-            num_share_modes = get_share_modes(conn, dev, inode, &current_shares);
-            for(i = 0; i < num_share_modes; i++)
-            {
-              if(!GET_ALLOW_SHARE_DELETE(current_shares[i].share_mode))
-              {
-                DEBUG(5,("call_trans2setfilepathinfo: refusing to set delete on close flag for fnum = %d, \
-file %s as a share exists that was not opened with FILE_DELETE access.\n",
-                      fsp->fnum, fsp->fsp_name ));
-                /*
-                 * Release the lock.
-                 */
-
-                unlock_share_entry_fsp(fsp);
-
-                /*
-                 * current_shares was malloced by get_share_modes - free it here.
-                 */
-
-                free((char *)current_shares);
-
-                /*
-                 * Even though share violation would be more appropriate here,
-                 * return ERRnoaccess as that's what NT does.
-                 */
-
-                return(ERROR(ERRDOS,ERRnoaccess));
-              }
-            }
-
-            /*
-             * current_shares was malloced by get_share_modes - free it here.
-             */
-
-            free((char *)current_shares);
-
-            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 ));
-
-            /*
-             * 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.
-             * Other smbd's that have this file open will have to fend for themselves. We
-             * take care of this (rare) case in close_file(). See the comment there.
-             */
-
-            for(iterate_fsp = file_find_di_first(dev, inode); iterate_fsp;
-                                  iterate_fsp = file_find_di_next(iterate_fsp))
-            {
-              int new_share_mode = (delete_on_close ? 
-                                    (iterate_fsp->share_mode | DELETE_ON_CLOSE_FLAG) :
-                                    (iterate_fsp->share_mode & ~DELETE_ON_CLOSE_FLAG) );
-
-              DEBUG(10,("call_trans2setfilepathinfo: Changing share mode for fnum %d, file %s \
-dev = %x, inode = %.0f from %x to %x\n", 
-                    iterate_fsp->fnum, iterate_fsp->fsp_name, (unsigned int)dev, 
-                    (double)inode, iterate_fsp->share_mode, new_share_mode ));
-
-              if(modify_share_mode(iterate_fsp, new_share_mode, iterate_fsp->oplock_type)==False)
-                DEBUG(0,("call_trans2setfilepathinfo: failed to change delete on close for fnum %d, \
-dev = %x, inode = %.0f\n", iterate_fsp->fnum, (unsigned int)dev, (double)inode));
-            }
-
-            /*
-             * Set the delete on close flag in the reference
-             * counted struct. Delete when the last reference
-             * goes away.
-             */
-           fsp->delete_on_close = delete_on_close;
-
-           unlock_share_entry_fsp(fsp);
-
-           DEBUG(10, ("call_trans2setfilepathinfo: %s delete on close flag for fnum = %d, file %s\n",
-                 delete_on_close ? "Added" : "Removed", fsp->fnum, fsp->fsp_name ));
-
-          } /* end if(delete_on_close && !GET_DELETE_ON_CLOSE_FLAG(fsp->share_mode)) */
-        } /* end if is_directory. */
-      } else
-        return(ERROR(ERRDOS,ERRunknownlevel));
-      break;
-    }
+               /*
+                * Only allow delete on close for files/directories opened with delete intent.
+                */
 
-    default:
-    {
-      return(ERROR(ERRDOS,ERRunknownlevel));
-    }
+               if (delete_on_close && !GET_DELETE_ACCESS_REQUESTED(fsp->share_mode)) {
+                       DEBUG(10,("call_trans2setfilepathinfo: file %s delete on close flag set but delete access denied.\n",
+                                       fsp->fsp_name ));
+                               return(ERROR(ERRDOS,ERRnoaccess));
+               }
+
+               if(fsp->is_directory) {
+                       fsp->directory_delete_on_close = delete_on_close;
+                       DEBUG(10, ("call_trans2setfilepathinfo: %s delete on close flag for fnum = %d, directory %s\n",
+                               delete_on_close ? "Added" : "Removed", fsp->fnum, fsp->fsp_name ));
+               } else if(fsp->stat_open) {
+
+                       DEBUG(10, ("call_trans2setfilepathinfo: %s delete on close flag for fnum = %d, stat open %s\n",
+                               delete_on_close ? "Added" : "Removed", fsp->fnum, fsp->fsp_name ));
+
+               } else {
+
+                       files_struct *iterate_fsp;
+
+                       /*
+                        * Modify the share mode entry for all files open
+                        * on this device and inode to tell other smbds we have 
+                        * changed the delete on close flag. This will be noticed
+                        * in the close code, the last closer will delete the file
+                        * if flag is set.
+                        */
+
+                       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(fsp) == False)
+                               return(ERROR(ERRDOS,ERRnoaccess));
+
+                       if (!modify_delete_flag(fsp->dev, fsp->inode, delete_on_close)) {
+                               DEBUG(0,("call_trans2setfilepathinfo: failed to change delete on close flag for file %s\n",
+                                               fsp->fsp_name ));
+                               unlock_share_entry_fsp(fsp);
+                               return(ERROR(ERRDOS,ERRnoaccess));
+                       }
+
+                       /*
+                        * Release the lock.
+                        */
+
+                       unlock_share_entry_fsp(fsp);
+
+                       /*
+                        * 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.
+                        * Other smbd's that have this file open will look in the share_mode on close.
+                        * take care of this (rare) case in close_file(). See the comment there.
+                        * NB. JRA. We don't really need to do this anymore - all should be taken
+                        * care of in the share_mode changes in the tdb.
+                        */
+
+                       for(iterate_fsp = file_find_di_first(fsp->dev, fsp->inode);
+                                       iterate_fsp; iterate_fsp = file_find_di_next(iterate_fsp))
+                                                       fsp->delete_on_close = delete_on_close;
+
+                       /*
+                        * Set the delete on close flag in the fsp.
+                        */
+                       fsp->delete_on_close = delete_on_close;
+
+                       DEBUG(10, ("call_trans2setfilepathinfo: %s delete on close flag for fnum = %d, file %s\n",
+                               delete_on_close ? "Added" : "Removed", fsp->fnum, fsp->fsp_name ));
+
+               }
+
+               break;
+       }
+
+       default:
+       {
+               return(ERROR(ERRDOS,ERRunknownlevel));
+       }
   }
 
   /* get some defaults (no modifications) if any info is zero or -1. */