summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGerald Carter <jerry@samba.org>2007-05-23 22:46:30 +0000
committerGerald Carter <jerry@samba.org>2007-05-23 22:46:30 +0000
commitb9220bd6ab2dfe7ffdfc4b287ca2e4965cf13e3a (patch)
tree86f3f5891658b35f5e7e8183ae5eae9546669185
parenta7385f33bf7d7efc9bea78caeca94c43e5670749 (diff)
downloadsamba-b9220bd6ab2dfe7ffdfc4b287ca2e4965cf13e3a.tar.gz
r23101: grab remainder of rename fixes
-rw-r--r--source/include/smb.h3
-rw-r--r--source/locking/locking.c46
-rw-r--r--source/smbd/open.c47
-rw-r--r--source/smbd/reply.c54
-rw-r--r--source/smbd/trans2.c35
5 files changed, 146 insertions, 39 deletions
diff --git a/source/include/smb.h b/source/include/smb.h
index 29fb1e133d8..3f2f2235f66 100644
--- a/source/include/smb.h
+++ b/source/include/smb.h
@@ -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.
diff --git a/source/locking/locking.c b/source/locking/locking.c
index f304037bcce..56d8ff206fe 100644
--- a/source/locking/locking.c
+++ b/source/locking/locking.c
@@ -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,
diff --git a/source/smbd/open.c b/source/smbd/open.c
index c7a7086894e..7bfeeb8b7e8 100644
--- a/source/smbd/open.c
+++ b/source/smbd/open.c
@@ -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. */
diff --git a/source/smbd/reply.c b/source/smbd/reply.c
index bb4965adf6a..7fc4f0e1c46 100644
--- a/source/smbd/reply.c
+++ b/source/smbd/reply.c
@@ -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;
diff --git a/source/smbd/trans2.c b/source/smbd/trans2.c
index 952aff953d2..e5a699047fd 100644
--- a/source/smbd/trans2.c
+++ b/source/smbd/trans2.c
@@ -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)) {