Re-wrote the guts of the rename_internals code to cope with a reported
authorJeremy Allison <jra@samba.org>
Fri, 4 Jan 2002 21:11:35 +0000 (21:11 +0000)
committerJeremy Allison <jra@samba.org>
Fri, 4 Jan 2002 21:11:35 +0000 (21:11 +0000)
bug (renaming name -> name was failing, on W2K it succeeds). Simplified
the common case, did a lot of work to ensure NT error codes are correctly
reported back to client.
Jeremy.

source/smbd/nttrans.c
source/smbd/reply.c
source/smbd/vfs.c

index 7a6ea52aca1025a7c75dc18027a5d672c3e8e2d3..464790d158a5209488d2f50adf832a3f73e343b4 100644 (file)
@@ -1454,7 +1454,8 @@ static int call_nt_transact_rename(connection_struct *conn,
 
        status = rename_internals(conn, fsp->fsp_name,
                                  new_name, replace_if_exists);
-       if (!NT_STATUS_IS_OK(status)) return ERROR_NT(status);
+       if (!NT_STATUS_IS_OK(status))
+               return ERROR_NT(status);
 
        /*
         * Rename was successful.
index 0e7eca3ac2f8bd7b3d90b02f0833a6d2497d07ca..9e6f8d24c217f4ab3a6192d33477816fea1a5676 100644 (file)
@@ -2952,26 +2952,26 @@ static BOOL resolve_wildcards(char *name1,char *name2)
 }
 
 /*******************************************************************
-check if a user is allowed to rename a file
+ Check if a user is allowed to rename a file.
 ********************************************************************/
-static BOOL can_rename(char *fname,connection_struct *conn)
+
+static NTSTATUS can_rename(char *fname,connection_struct *conn)
 {
-  SMB_STRUCT_STAT sbuf;
+       if (!CAN_WRITE(conn))
+               return NT_STATUS_ACCESS_DENIED;
 
-  if (!CAN_WRITE(conn)) return(False);
+       if (!check_file_sharing(conn,fname,True))
+               return NT_STATUS_SHARING_VIOLATION;
 
-  if (conn->vfs_ops.lstat(conn,fname,&sbuf) != 0) return(False);
-  if (!check_file_sharing(conn,fname,True)) return(False);
-  return(True);
+       return NT_STATUS_OK;
 }
 
 /****************************************************************************
  The guts of the rename command, split out so it may be called by the NT SMB
  code. 
 ****************************************************************************/
-NTSTATUS rename_internals(connection_struct *conn, 
-                         char *name, 
-                         char *newname, BOOL replace_if_exists)
+
+NTSTATUS rename_internals(connection_struct *conn, char *name, char *newname, BOOL replace_if_exists)
 {
        pstring directory;
        pstring mask;
@@ -2982,7 +2982,6 @@ NTSTATUS rename_internals(connection_struct *conn,
        BOOL bad_path2 = False;
        int count=0;
        NTSTATUS error = NT_STATUS_OK;
-       BOOL exists=False;
        BOOL rc = True;
        SMB_STRUCT_STAT sbuf1, sbuf2;
 
@@ -3056,7 +3055,8 @@ NTSTATUS rename_internals(connection_struct *conn,
                        pstrcpy(newname, tmpstr);
                }
                
-               DEBUG(3,("rename_internals: case_sensitive = %d, case_preserve = %d, short case preserve = %d, directory = %s, newname = %s, newname_last_component = %s, is_8_3 = %d\n", 
+               DEBUG(3,("rename_internals: case_sensitive = %d, case_preserve = %d, short case preserve = %d, \
+directory = %s, newname = %s, newname_last_component = %s, is_8_3 = %d\n", 
                         case_sensitive, case_preserve, short_case_preserve, directory, 
                         newname, newname_last_component, is_short_name));
 
@@ -3093,34 +3093,76 @@ NTSTATUS rename_internals(connection_struct *conn,
                                pstrcpy(p+1, newname_last_component);
                        }
                }
-               
-               if(replace_if_exists) {
-                       /*
-                        * NT SMB specific flag - rename can overwrite
-                        * file with the same name so don't check for
-                        * vfs_file_exist().
-                        */
+       
+               resolve_wildcards(directory,newname);
+       
+               /*
+                * The source object must exist.
+                */
 
-                       if(resolve_wildcards(directory,newname) &&
-                          can_rename(directory,conn) &&
-                          conn->vfs_ops.rename(conn,directory,newname) == 0)
-                               count++;
-               } else {
-                       if (resolve_wildcards(directory,newname) && 
-                           can_rename(directory,conn) && 
-                           !vfs_file_exist(conn,newname,NULL) &&
-                           conn->vfs_ops.rename(conn,directory,newname) == 0)
-                               count++;
+               if (!vfs_object_exist(conn, directory, NULL)) {
+                       DEBUG(3,("rename_internals: source doesn't exist doing rename %s -> %s\n",
+                               directory,newname));
+
+                       if (errno == ENOTDIR || errno == EISDIR || errno == ENOENT) {
+                               /*
+                                * Must return different errors depending on whether the parent
+                                * directory existed or not.
+                                */
+
+                               p = strrchr_m(directory, '/');
+                               if (!p)
+                                       return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+                               *p = '\0';
+                               if (vfs_object_exist(conn, directory, NULL))
+                                       return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+                               return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+                       }
+                       error = map_nt_error_from_unix(errno);
+                       DEBUG(3,("rename_internals: Error %s rename %s -> %s\n",
+                               get_nt_error_msg(error), directory,newname));
+
+                       return error;
                }
 
-               DEBUG(3,("rename_internals: %s doing rename on %s -> %s\n",(count != 0) ? "succeeded" : "failed",
-                         directory,newname));
-               
-               if (!count) exists = vfs_file_exist(conn,directory,NULL);
-               if (!count && exists && vfs_file_exist(conn,newname,NULL)) {
-                       exists = True;
-                       error = NT_STATUS_OBJECT_NAME_COLLISION;
+               error = can_rename(directory,conn);
+
+               if (!NT_STATUS_IS_OK(error)) {
+                       DEBUG(3,("rename_internals: Error %s rename %s -> %s\n",
+                               get_nt_error_msg(error), directory,newname));
+               }
+
+               /*
+                * If the src and dest names are identical - including case,
+                * don't do the rename, just return success.
+                */
+
+               if (strcsequal(directory, newname)) {
+                       DEBUG(3,("rename_internals: identical names in rename %s - returning success\n", directory));
+                       return NT_STATUS_OK;
                }
+
+               if(!replace_if_exists && vfs_object_exist(conn,newname,NULL)) {
+                       DEBUG(3,("rename_internals: dest exists doing rename %s -> %s\n",
+                               directory,newname));
+                       return NT_STATUS_OBJECT_NAME_COLLISION;
+               }
+
+               if(conn->vfs_ops.rename(conn,directory, newname) == 0) {
+                       DEBUG(3,("rename_internals: succeeded doing rename on %s -> %s\n",
+                               directory,newname));
+                       return NT_STATUS_OK;    
+               }
+
+               if (errno == ENOTDIR || errno == EISDIR)
+                       error = NT_STATUS_OBJECT_NAME_COLLISION;
+               else
+                       error = map_nt_error_from_unix(errno);
+               
+               DEBUG(3,("rename_internals: Error %s rename %s -> %s\n",
+                       get_nt_error_msg(error), directory,newname));
+
+               return error;
        } else {
                /*
                 * Wildcards - process each file that matches.
@@ -3148,7 +3190,8 @@ NTSTATUS rename_internals(connection_struct *conn,
                                
                                error = NT_STATUS_ACCESS_DENIED;
                                slprintf(fname,sizeof(fname)-1,"%s/%s",directory,dname);
-                               if (!can_rename(fname,conn)) {
+                               error = can_rename(fname,conn);
+                               if (!NT_STATUS_IS_OK(error)) {
                                        DEBUG(6,("rename %s refused\n", fname));
                                        continue;
                                }
index 6d8e9cc76c2758519c60de3bbf49ee1fb5f7c618..afdfe8c48ff9c9763811097f16f3dea17a3d3acf 100644 (file)
@@ -231,10 +231,10 @@ int vfs_mkdir(connection_struct *conn, const char *name, mode_t mode)
 }
 
 /*******************************************************************
- Check if a vfs file exists.
+ Check if an object exists in the vfs.
 ********************************************************************/
 
-BOOL vfs_file_exist(connection_struct *conn,const char *fname,SMB_STRUCT_STAT *sbuf)
+BOOL vfs_object_exist(connection_struct *conn,const char *fname,SMB_STRUCT_STAT *sbuf)
 {
        SMB_STRUCT_STAT st;
 
@@ -243,9 +243,26 @@ BOOL vfs_file_exist(connection_struct *conn,const char *fname,SMB_STRUCT_STAT *s
 
        ZERO_STRUCTP(sbuf);
 
-       if (vfs_stat(conn,fname,sbuf) != 0)
+       if (vfs_stat(conn,fname,sbuf) == -1)
                return(False);
+       return True;
+}
+
+/*******************************************************************
+ Check if a file exists in the vfs.
+********************************************************************/
+
+BOOL vfs_file_exist(connection_struct *conn,char *fname,SMB_STRUCT_STAT *sbuf)
+{
+       SMB_STRUCT_STAT st;
+
+       if (!sbuf)
+               sbuf = &st;
 
+       ZERO_STRUCTP(sbuf);
+
+       if (vfs_stat(conn,fname,sbuf) == -1)
+               return False;
        return(S_ISREG(sbuf->st_mode));
 }