summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeremy Allison <jra@samba.org>2002-01-04 20:06:04 +0000
committerJeremy Allison <jra@samba.org>2002-01-04 20:06:04 +0000
commitf01357737b9cdd85732439eb153d0a0b8f26e66c (patch)
tree9a94833dd1e75c3cf08829917f5e6227ec600f5f
parentf97ce504d2cd29b09793fec4bf532a2acce44e1a (diff)
downloadsamba-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.h5
-rw-r--r--source/smbd/nttrans.c31
-rw-r--r--source/smbd/reply.c165
-rw-r--r--source/smbd/vfs.c23
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));
}