diff options
author | Jeremy Allison <jra@samba.org> | 2002-01-04 20:06:04 +0000 |
---|---|---|
committer | Jeremy Allison <jra@samba.org> | 2002-01-04 20:06:04 +0000 |
commit | f01357737b9cdd85732439eb153d0a0b8f26e66c (patch) | |
tree | 9a94833dd1e75c3cf08829917f5e6227ec600f5f | |
parent | f97ce504d2cd29b09793fec4bf532a2acce44e1a (diff) | |
download | samba-f01357737b9cdd85732439eb153d0a0b8f26e66c.tar.gz samba-f01357737b9cdd85732439eb153d0a0b8f26e66c.tar.xz samba-f01357737b9cdd85732439eb153d0a0b8f26e66c.zip |
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. Now to port this to HEAD and app-head.
Jeremy.
-rw-r--r-- | source/include/proto.h | 5 | ||||
-rw-r--r-- | source/smbd/nttrans.c | 31 | ||||
-rw-r--r-- | source/smbd/reply.c | 165 | ||||
-rw-r--r-- | source/smbd/vfs.c | 23 |
4 files changed, 144 insertions, 80 deletions
diff --git a/source/include/proto.h b/source/include/proto.h index a7d2ef95d1f..08fe3c5c100 100644 --- a/source/include/proto.h +++ b/source/include/proto.h @@ -4434,9 +4434,7 @@ NTSTATUS mkdir_internal(connection_struct *conn, pstring directory); int reply_mkdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize); BOOL rmdir_internals(connection_struct *conn, char *directory); int reply_rmdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize); -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); int reply_mv(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize); int reply_copy(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize); int reply_setdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize); @@ -4543,6 +4541,7 @@ BOOL vfs_init(connection_struct *conn); BOOL vfs_directory_exist(connection_struct *conn, char *dname, SMB_STRUCT_STAT *st); int vfs_mkdir(connection_struct *conn, char *fname, mode_t mode); char *vfs_getwd(connection_struct *conn, char *unix_path); +BOOL vfs_object_exist(connection_struct *conn,char *fname,SMB_STRUCT_STAT *sbuf); BOOL vfs_file_exist(connection_struct *conn,char *fname,SMB_STRUCT_STAT *sbuf); ssize_t vfs_read_data(files_struct *fsp, char *buf, size_t byte_count); ssize_t vfs_write_data(files_struct *fsp,char *buffer,size_t N); diff --git a/source/smbd/nttrans.c b/source/smbd/nttrans.c index 46c2c21c7e0..a5208f0cbac 100644 --- a/source/smbd/nttrans.c +++ b/source/smbd/nttrans.c @@ -1553,29 +1553,30 @@ static int call_nt_transact_rename(connection_struct *conn, int bufsize, char **ppsetup, char **ppparams, char **ppdata) { - char *params = *ppparams; - pstring new_name; - files_struct *fsp = file_fsp(params, 0); - BOOL replace_if_exists = (SVAL(params,2) & RENAME_REPLACE_IF_EXISTS) ? True : False; + char *params = *ppparams; + pstring new_name; + files_struct *fsp = file_fsp(params, 0); + BOOL replace_if_exists = (SVAL(params,2) & RENAME_REPLACE_IF_EXISTS) ? True : False; NTSTATUS status; - uint32 fname_len = MIN((((uint32)IVAL(inbuf,smb_nt_TotalParameterCount)-4)), - ((uint32)sizeof(new_name)-1)); + uint32 fname_len = MIN((((uint32)IVAL(inbuf,smb_nt_TotalParameterCount)-4)), + ((uint32)sizeof(new_name)-1)); - CHECK_FSP(fsp, conn); - StrnCpy(new_name,params+4,fname_len); - new_name[fname_len] = '\0'; + CHECK_FSP(fsp, conn); + StrnCpy(new_name,params+4,fname_len); + new_name[fname_len] = '\0'; 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. - */ + /* + * Rename was successful. + */ send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, NULL, 0, NULL, 0); - DEBUG(3,("nt transact rename from = %s, to = %s succeeded.\n", - fsp->fsp_name, new_name)); + DEBUG(3,("nt transact rename from = %s, to = %s succeeded.\n", + fsp->fsp_name, new_name)); /* * Win2k needs a changenotify request response before it will diff --git a/source/smbd/reply.c b/source/smbd/reply.c index ff09535bdcc..7c4b130fc3f 100644 --- a/source/smbd/reply.c +++ b/source/smbd/reply.c @@ -3654,26 +3654,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,dos_to_unix(fname,False),&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; @@ -3684,10 +3684,8 @@ 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; - pstring zdirectory; *directory = *mask = 0; @@ -3729,6 +3727,9 @@ NTSTATUS rename_internals(connection_struct *conn, has_wild = ms_has_wild(mask); if (!has_wild) { + pstring zdirectory; + pstring znewname; + /* * No wildcards - just process the one file. */ @@ -3747,7 +3748,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)); @@ -3785,37 +3787,81 @@ NTSTATUS rename_internals(connection_struct *conn, } } - pstrcpy(zdirectory, dos_to_unix(directory, False)); - if(replace_if_exists) { - /* - * NT SMB specific flag - rename can overwrite - * file with the same name so don't check for - * vfs_file_exist(). - */ - if(resolve_wildcards(directory,newname) && - can_rename(directory,conn) && - !conn->vfs_ops.rename(conn,zdirectory, - dos_to_unix(newname,False))) - count++; - } else { - if (resolve_wildcards(directory,newname) && - can_rename(directory,conn) && - !vfs_file_exist(conn,newname,NULL) && - !conn->vfs_ops.rename(conn,zdirectory, - dos_to_unix(newname,False))) - count++; + resolve_wildcards(directory,newname); + + /* + * The source object must exist. + */ + + 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(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)); + } + + pstrcpy(zdirectory, dos_to_unix(directory, False)); + pstrcpy(znewname, dos_to_unix(newname,False)); + + /* + * If the src and dest names are identical - including case, + * don't do the rename, just return success. + */ + + if (strcsequal(zdirectory, znewname)) { + 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,zdirectory, znewname) == 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. */ @@ -3842,8 +3888,9 @@ 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)) { - DEBUG(6,("rename %s refused\n", fname)); + error = can_rename(fname,conn); + if (!NT_STATUS_IS_OK(error)) { + DEBUG(6,("rename %s failed. Error %s\n", fname, get_nt_error_msg(error))); continue; } pstrcpy(destname,newname); @@ -3855,7 +3902,7 @@ NTSTATUS rename_internals(connection_struct *conn, } if (!replace_if_exists && - vfs_file_exist(conn,destname, NULL)) { + vfs_object_exist(conn,destname, NULL)) { DEBUG(6,("file_exist %s\n", destname)); error = NT_STATUS_OBJECT_NAME_COLLISION; continue; @@ -3869,7 +3916,7 @@ NTSTATUS rename_internals(connection_struct *conn, CloseDir(dirptr); } } - + if (count == 0 && NT_STATUS_IS_OK(error)) { error = map_nt_error_from_unix(errno); } @@ -3883,19 +3930,19 @@ NTSTATUS rename_internals(connection_struct *conn, int reply_mv(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { - int outsize = 0; - pstring name; - pstring newname; + int outsize = 0; + pstring name; + pstring newname; NTSTATUS status; - START_PROFILE(SMBmv); + START_PROFILE(SMBmv); - pstrcpy(name,smb_buf(inbuf) + 1); - pstrcpy(newname,smb_buf(inbuf) + 3 + strlen(name)); + pstrcpy(name,smb_buf(inbuf) + 1); + pstrcpy(newname,smb_buf(inbuf) + 3 + strlen(name)); - RESOLVE_DFSPATH(name, conn, inbuf, outbuf); - RESOLVE_DFSPATH(newname, conn, inbuf, outbuf); + RESOLVE_DFSPATH(name, conn, inbuf, outbuf); + RESOLVE_DFSPATH(newname, conn, inbuf, outbuf); - DEBUG(3,("reply_mv : %s -> %s\n",name,newname)); + DEBUG(3,("reply_mv : %s -> %s\n",name,newname)); status = rename_internals(conn, name, newname, False); if (!NT_STATUS_IS_OK(status)) { @@ -3903,14 +3950,14 @@ int reply_mv(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, in } /* - * Win2k needs a changenotify request response before it will - * update after a rename.. - */ - process_pending_change_notify_queue((time_t)0); - outsize = set_message(outbuf,0,0,True); + * Win2k needs a changenotify request response before it will + * update after a rename.. + */ + process_pending_change_notify_queue((time_t)0); + outsize = set_message(outbuf,0,0,True); - END_PROFILE(SMBmv); - return(outsize); + END_PROFILE(SMBmv); + return(outsize); } /******************************************************************* diff --git a/source/smbd/vfs.c b/source/smbd/vfs.c index 499072dcd8f..dc22109d612 100644 --- a/source/smbd/vfs.c +++ b/source/smbd/vfs.c @@ -348,10 +348,10 @@ char *vfs_getwd(connection_struct *conn, char *unix_path) } /******************************************************************* - Check if a vfs file exists. + Check if an object exists in the vfs. ********************************************************************/ -BOOL vfs_file_exist(connection_struct *conn,char *fname,SMB_STRUCT_STAT *sbuf) +BOOL vfs_object_exist(connection_struct *conn,char *fname,SMB_STRUCT_STAT *sbuf) { SMB_STRUCT_STAT st; @@ -360,9 +360,26 @@ BOOL vfs_file_exist(connection_struct *conn,char *fname,SMB_STRUCT_STAT *sbuf) 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)); } |