r23101: grab remainder of rename fixes
authorGerald Carter <jerry@samba.org>
Wed, 23 May 2007 22:46:30 +0000 (22:46 +0000)
committerGerald Carter <jerry@samba.org>
Wed, 23 May 2007 22:46:30 +0000 (22:46 +0000)
source/include/smb.h
source/locking/locking.c
source/smbd/open.c
source/smbd/reply.c
source/smbd/trans2.c

index 29fb1e133d8fef22ebe3c7d343fc93045cbcfdb3..3f2f2235f66e92e28ffacbb9601af108e4fe0643 100644 (file)
@@ -748,6 +748,7 @@ struct pending_message_list {
 };
 
 #define SHARE_MODE_FLAG_POSIX_OPEN     0x1
+#define SHARE_MODE_ALLOW_INITIAL_DELETE_ON_CLOSE      0x2
 
 /* struct returned by get_share_modes */
 struct share_mode_entry {
@@ -765,7 +766,7 @@ struct share_mode_entry {
        SMB_INO_T inode;
        unsigned long share_file_id;
        uint32 uid;             /* uid of file opener. */
-       uint16 flags;           /* POSIX_OPEN only defined so far... */
+       uint16 flags;           /* See SHARE_MODE_XX above. */
 };
 
 /* oplock break message definition - linearization of share_mode_entry.
index f304037bcce8dbb6c8dc0b10132067581e2819ac..56d8ff206fe731f631b20ffe27c5aa6ed846ae00 100644 (file)
@@ -993,10 +993,13 @@ static void add_share_mode_entry(struct share_mode_lock *lck,
 }
 
 void set_share_mode(struct share_mode_lock *lck, files_struct *fsp,
-                       uid_t uid, uint16 mid, uint16 op_type)
+                       uid_t uid, uint16 mid, uint16 op_type, BOOL initial_delete_on_close_allowed)
 {
        struct share_mode_entry entry;
        fill_share_mode_entry(&entry, fsp, uid, mid, op_type);
+       if (initial_delete_on_close_allowed) {
+               entry.flags |= SHARE_MODE_ALLOW_INITIAL_DELETE_ON_CLOSE;
+       }
        add_share_mode_entry(lck, &entry);
 }
 
@@ -1196,6 +1199,22 @@ NTSTATUS can_set_delete_on_close(files_struct *fsp, BOOL delete_on_close,
        return NT_STATUS_OK;
 }
 
+/****************************************************************************
+ Do we have an open file handle that created this entry ?
+****************************************************************************/
+
+BOOL can_set_initial_delete_on_close(const struct share_mode_lock *lck)
+{
+       int i;
+
+       for (i=0; i<lck->num_share_modes; i++) {
+               if (lck->share_modes[i].flags & SHARE_MODE_ALLOW_INITIAL_DELETE_ON_CLOSE) {
+                       return True;
+               }
+       }
+       return False;
+}
+
 /*************************************************************************
  Return a talloced copy of a UNIX_USER_TOKEN. NULL on fail.
  (Should this be in locking.c.... ?).
@@ -1296,6 +1315,31 @@ BOOL set_delete_on_close(files_struct *fsp, BOOL delete_on_close, UNIX_USER_TOKE
        return True;
 }
 
+/****************************************************************************
+ Sets the allow initial delete on close flag for this share mode.
+****************************************************************************/
+
+BOOL set_allow_initial_delete_on_close(struct share_mode_lock *lck, files_struct *fsp, BOOL delete_on_close)
+{
+       struct share_mode_entry entry, *e;
+
+       /* Don't care about the pid owner being correct here - just a search. */
+       fill_share_mode_entry(&entry, fsp, (uid_t)-1, 0, NO_OPLOCK);
+
+       e = find_share_mode_entry(lck, &entry);
+       if (e == NULL) {
+               return False;
+       }
+
+       if (delete_on_close) {
+               e->flags |= SHARE_MODE_ALLOW_INITIAL_DELETE_ON_CLOSE;
+       } else {
+               e->flags &= ~SHARE_MODE_ALLOW_INITIAL_DELETE_ON_CLOSE;
+       }
+       lck->modified = True;
+       return True;
+}
+
 struct forall_state {
        void (*fn)(const struct share_mode_entry *entry,
                   const char *sharepath,
index c7a7086894e6837ad269a8c591d2c3a697490884..7bfeeb8b7e837880aacfe3233f5fd613c17095e8 100644 (file)
@@ -47,7 +47,12 @@ static NTSTATUS fd_open(struct connection_struct *conn,
        NTSTATUS status = NT_STATUS_OK;
 
 #ifdef O_NOFOLLOW
-       if (!lp_symlinks(SNUM(conn))) {
+       /* 
+        * Never follow symlinks on a POSIX client. The
+        * client should be doing this.
+        */
+
+       if (fsp->posix_open || !lp_symlinks(SNUM(conn))) {
                flags |= O_NOFOLLOW;
        }
 #endif
@@ -1120,6 +1125,7 @@ NTSTATUS open_file_ntcreate(connection_struct *conn,
        BOOL file_existed = VALID_STAT(*psbuf);
        BOOL def_acl = False;
        BOOL posix_open = False;
+       BOOL new_file_created = False;
        SMB_DEV_T dev = 0;
        SMB_INO_T inode = 0;
        NTSTATUS fsp_open = NT_STATUS_ACCESS_DENIED;
@@ -1760,28 +1766,31 @@ NTSTATUS open_file_ntcreate(connection_struct *conn,
                        fsp->oplock_type = NO_OPLOCK;
                }
        }
-       set_share_mode(lck, fsp, current_user.ut.uid, 0, fsp->oplock_type);
 
-       if (info == FILE_WAS_OVERWRITTEN || info == FILE_WAS_CREATED ||
-           info == FILE_WAS_SUPERSEDED) {
+       if (info == FILE_WAS_OVERWRITTEN || info == FILE_WAS_CREATED || info == FILE_WAS_SUPERSEDED) {
+               new_file_created = True;
+       }
 
-               /* Handle strange delete on close create semantics. */
-               if (create_options & FILE_DELETE_ON_CLOSE) {
-                       status = can_set_delete_on_close(fsp, True, new_dos_attributes);
+       set_share_mode(lck, fsp, current_user.ut.uid, 0, fsp->oplock_type, new_file_created);
 
-                       if (!NT_STATUS_IS_OK(status)) {
-                               /* Remember to delete the mode we just added. */
-                               del_share_mode(lck, fsp);
-                               TALLOC_FREE(lck);
-                               fd_close(conn,fsp);
-                               file_free(fsp);
-                               return status;
-                       }
-                       /* Note that here we set the *inital* delete on close flag,
-                          not the regular one. The magic gets handled in close. */
-                       fsp->initial_delete_on_close = True;
+       /* Handle strange delete on close create semantics. */
+       if ((create_options & FILE_DELETE_ON_CLOSE) && can_set_initial_delete_on_close(lck)) {
+               status = can_set_delete_on_close(fsp, True, new_dos_attributes);
+
+               if (!NT_STATUS_IS_OK(status)) {
+                       /* Remember to delete the mode we just added. */
+                       del_share_mode(lck, fsp);
+                       TALLOC_FREE(lck);
+                       fd_close(conn,fsp);
+                       file_free(fsp);
+                       return status;
                }
+               /* Note that here we set the *inital* delete on close flag,
+                  not the regular one. The magic gets handled in close. */
+               fsp->initial_delete_on_close = True;
+       }
        
+       if (new_file_created) {
                /* Files should be initially set as archive */
                if (lp_map_archive(SNUM(conn)) ||
                    lp_store_dos_attributes(SNUM(conn))) {
@@ -2139,7 +2148,7 @@ NTSTATUS open_directory(connection_struct *conn,
                return status;
        }
 
-       set_share_mode(lck, fsp, current_user.ut.uid, 0, NO_OPLOCK);
+       set_share_mode(lck, fsp, current_user.ut.uid, 0, NO_OPLOCK, True);
 
        /* For directories the delete on close bit at open time seems
           always to be honored on close... See test 19 in Samba4 BASE-DELETE. */
index bb4965adf6aa2c0d36723072fce3beebca42d720..7fc4f0e1c46be53248fc7845b13464cef5f41d85 100644 (file)
@@ -1790,7 +1790,7 @@ int reply_ctemp(connection_struct *conn, char *inbuf,char *outbuf, int dum_size,
  Check if a user is allowed to rename a file.
 ********************************************************************/
 
-static NTSTATUS can_rename(connection_struct *conn, char *fname, uint16 dirtype, SMB_STRUCT_STAT *pst)
+static NTSTATUS can_rename(connection_struct *conn, char *fname, uint16 dirtype, SMB_STRUCT_STAT *pst, BOOL self_open)
 {
        files_struct *fsp;
        uint32 fmode;
@@ -1811,7 +1811,10 @@ static NTSTATUS can_rename(connection_struct *conn, char *fname, uint16 dirtype,
 
        status = open_file_ntcreate(conn, fname, pst,
                                DELETE_ACCESS,
-                               FILE_SHARE_READ|FILE_SHARE_WRITE,
+                               /* If we're checking our fsp don't deny for delete. */
+                               self_open ?
+                                       FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE :
+                                       FILE_SHARE_READ|FILE_SHARE_WRITE,
                                FILE_OPEN,
                                0,
                                FILE_ATTRIBUTE_NORMAL,
@@ -4192,7 +4195,9 @@ NTSTATUS rename_internals_fsp(connection_struct *conn, files_struct *fsp, pstrin
        ZERO_STRUCT(sbuf);
 
        status = unix_convert(conn, newname, False, newname_last_component, &sbuf);
-       if (!NT_STATUS_IS_OK(status)) {
+       /* We expect this to be NT_STATUS_OBJECT_PATH_NOT_FOUND */
+       if (!NT_STATUS_EQUAL(NT_STATUS_OBJECT_PATH_NOT_FOUND, status)) {
+               return NT_STATUS_OBJECT_NAME_COLLISION;
                return status;
        }
 
@@ -4260,9 +4265,20 @@ NTSTATUS rename_internals_fsp(connection_struct *conn, files_struct *fsp, pstrin
                return NT_STATUS_OBJECT_NAME_COLLISION;
        }
 
-       status = can_rename(conn,fsp->fsp_name,attrs,&sbuf);
+       /* Ensure we have a valid stat struct for the source. */
+       if (fsp->fh->fd != -1) {
+               if (SMB_VFS_FSTAT(fsp,fsp->fh->fd,&sbuf) == -1) {
+                       return map_nt_error_from_unix(errno);
+               }
+       } else {
+               if (SMB_VFS_STAT(conn,fsp->fsp_name,&sbuf) == -1) {
+                       return map_nt_error_from_unix(errno);
+               }
+       }
 
-       if (dest_exists && !NT_STATUS_IS_OK(status)) {
+       status = can_rename(conn,fsp->fsp_name,attrs,&sbuf,True);
+
+       if (!NT_STATUS_IS_OK(status)) {
                DEBUG(3,("rename_internals_fsp: Error %s rename %s -> %s\n",
                        nt_errstr(status), fsp->fsp_name,newname));
                if (NT_STATUS_EQUAL(status,NT_STATUS_SHARING_VIOLATION))
@@ -4277,9 +4293,33 @@ NTSTATUS rename_internals_fsp(connection_struct *conn, files_struct *fsp, pstrin
        lck = get_share_mode_lock(NULL, fsp->dev, fsp->inode, NULL, NULL);
 
        if(SMB_VFS_RENAME(conn,fsp->fsp_name, newname) == 0) {
+               uint32 create_options = fsp->fh->private_options;
+
                DEBUG(3,("rename_internals_fsp: succeeded doing rename on %s -> %s\n",
                        fsp->fsp_name,newname));
+
                rename_open_files(conn, lck, fsp->dev, fsp->inode, newname);
+
+               /*
+                * A rename acts as a new file create w.r.t. allowing an initial delete
+                * on close, probably because in Windows there is a new handle to the
+                * new file. If initial delete on close was requested but not
+                * originally set, we need to set it here. This is probably not 100% correct,
+                * but will work for the CIFSFS client which in non-posix mode
+                * depends on these semantics. JRA.
+                */
+
+               set_allow_initial_delete_on_close(lck, fsp, True);
+
+               if (create_options & FILE_DELETE_ON_CLOSE) {
+                       status = can_set_delete_on_close(fsp, True, 0);
+
+                       if (NT_STATUS_IS_OK(status)) {
+                               /* Note that here we set the *inital* delete on close flag,
+                                * not the regular one. The magic gets handled in close. */
+                               fsp->initial_delete_on_close = True;
+                       }
+               }
                TALLOC_FREE(lck);
                return NT_STATUS_OK;    
        }
@@ -4530,7 +4570,7 @@ NTSTATUS rename_internals(connection_struct *conn,
                        return status;
                }
 
-               status = can_rename(conn,directory,attrs,&sbuf1);
+               status = can_rename(conn,directory,attrs,&sbuf1,False);
 
                if (!NT_STATUS_IS_OK(status)) {
                        DEBUG(3,("rename_internals: Error %s rename %s -> "
@@ -4658,7 +4698,7 @@ NTSTATUS rename_internals(connection_struct *conn,
                                  fname, nt_errstr(status)));
                        continue;
                }
-               status = can_rename(conn,fname,attrs,&sbuf1);
+               status = can_rename(conn,fname,attrs,&sbuf1,False);
                if (!NT_STATUS_IS_OK(status)) {
                        DEBUG(6, ("rename %s refused\n", fname));
                        continue;
index 952aff953d206a15ba6bb32746a55238efdb2972..e5a699047fde3f17bbaf89d5eae7f1f97b9ced84 100644 (file)
@@ -4507,10 +4507,11 @@ static NTSTATUS smb_file_rename_information(connection_struct *conn,
        pstrcpy(base_name, fname);
        p = strrchr_m(base_name, '/');
        if (p) {
-               *p = '\0';
+               p[1] = '\0';
+       } else {
+               pstrcpy(base_name, "./");
        }
        /* Append the new name. */
-       pstrcat(base_name, "/");
        pstrcat(base_name, newname);
 
        if (fsp) {
@@ -5632,9 +5633,17 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char
                         * to do this call. JRA.
                         */
                        pstrcpy(fname, fsp->fsp_name);
-                       if (SMB_VFS_STAT(conn,fname,&sbuf) != 0) {
-                               DEBUG(3,("call_trans2setfilepathinfo: fileinfo of %s failed (%s)\n",fname,strerror(errno)));
-                               return UNIXERROR(ERRDOS,ERRbadpath);
+                       if (INFO_LEVEL_IS_UNIX(info_level)) {
+                               /* Always do lstat for UNIX calls. */
+                               if (SMB_VFS_LSTAT(conn,fname,&sbuf)) {
+                                       DEBUG(3,("call_trans2setfilepathinfo: SMB_VFS_LSTAT of %s failed (%s)\n",fname,strerror(errno)));
+                                       return UNIXERROR(ERRDOS,ERRbadpath);
+                               }
+                       } else {
+                               if (SMB_VFS_STAT(conn,fname,&sbuf) != 0) {
+                                       DEBUG(3,("call_trans2setfilepathinfo: fileinfo of %s failed (%s)\n",fname,strerror(errno)));
+                                       return UNIXERROR(ERRDOS,ERRbadpath);
+                               }
                        }
                } else if (fsp && fsp->print_file) {
                        /*
@@ -5693,14 +5702,18 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char
                        return ERROR_NT(status);
                }
 
-               /*
-                * For CIFS UNIX extensions the target name may not exist.
-                */
+               if (INFO_LEVEL_IS_UNIX(info_level)) {
+                       /*
+                        * For CIFS UNIX extensions the target name may not exist.
+                        */
+
+                       /* Always do lstat for UNIX calls. */
+                       SMB_VFS_LSTAT(conn,fname,&sbuf);
 
-               if(!VALID_STAT(sbuf) && !INFO_LEVEL_IS_UNIX(info_level)) {
-                       DEBUG(3,("call_trans2setfilepathinfo: stat of %s failed (%s)\n", fname, strerror(errno)));
+               } else if (!VALID_STAT(sbuf) && SMB_VFS_STAT(conn,fname,&sbuf)) {
+                       DEBUG(3,("call_trans2setfilepathinfo: SMB_VFS_STAT of %s failed (%s)\n",fname,strerror(errno)));
                        return UNIXERROR(ERRDOS,ERRbadpath);
-               }    
+               }
        }
 
        if (!CAN_WRITE(conn)) {