From eb61d92ca7053bef9142a29f296aae695c47db33 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 4 Jan 2002 21:11:35 +0000 Subject: Re-wrote the guts of the rename_internals code to cope with a reported 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. (This used to be commit e6b27f3d8069ae304baaebe09341c58d46b05fe4) --- source3/smbd/nttrans.c | 3 +- source3/smbd/reply.c | 117 +++++++++++++++++++++++++++++++++---------------- source3/smbd/vfs.c | 23 ++++++++-- 3 files changed, 102 insertions(+), 41 deletions(-) diff --git a/source3/smbd/nttrans.c b/source3/smbd/nttrans.c index 7a6ea52aca..464790d158 100644 --- a/source3/smbd/nttrans.c +++ b/source3/smbd/nttrans.c @@ -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. diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c index 0e7eca3ac2..9e6f8d24c2 100644 --- a/source3/smbd/reply.c +++ b/source3/smbd/reply.c @@ -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; } diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c index 6d8e9cc76c..afdfe8c48f 100644 --- a/source3/smbd/vfs.c +++ b/source3/smbd/vfs.c @@ -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)); } -- cgit