summaryrefslogtreecommitdiff
path: root/source3/smbd
diff options
context:
space:
mode:
authorJeremy Allison <jra@samba.org>1998-10-23 17:25:14 +0000
committerJeremy Allison <jra@samba.org>1998-10-23 17:25:14 +0000
commit6d99f5f339b572df1cd737286fbb64ad23f74fc7 (patch)
tree04cb738b2cad1f8140e88208414b7f65f1892cab /source3/smbd
parenta9d1a8996324cd5344bc5579c4556632c59ef3e2 (diff)
downloadsamba-6d99f5f339b572df1cd737286fbb64ad23f74fc7.tar.gz
samba-6d99f5f339b572df1cd737286fbb64ad23f74fc7.tar.bz2
samba-6d99f5f339b572df1cd737286fbb64ad23f74fc7.zip
Handle the case where multiple smbd have the file open, some of whom
don't have the ALLOW_DELETE_ON_CLOSE share flag enabled. Told you this was fiddly code :-). Jeremy. (This used to be commit 222d686a6cdbc2947e7201636ae80ef4c650a65d)
Diffstat (limited to 'source3/smbd')
-rw-r--r--source3/smbd/close.c13
-rw-r--r--source3/smbd/trans2.c80
2 files changed, 76 insertions, 17 deletions
diff --git a/source3/smbd/close.c b/source3/smbd/close.c
index 50ad01f575..fcb65e1331 100644
--- a/source3/smbd/close.c
+++ b/source3/smbd/close.c
@@ -143,9 +143,18 @@ void close_file(files_struct *fsp, BOOL normal_close)
if (normal_close && last_reference && delete_on_close) {
DEBUG(5,("close_file: file %s. Delete on close was set - deleting file.\n"));
- if(dos_unlink(fsp->fsp_name) != 0)
- DEBUG(0,("close_file: file %s. Delete on close was set and unlink failed \
+ if(dos_unlink(fsp->fsp_name) != 0) {
+
+ /*
+ * This call can potentially fail as another smbd may have
+ * had the file open with delete on close set and deleted
+ * it when its last reference to this file went away. Hence
+ * we log this but not at debug level zero.
+ */
+
+ DEBUG(5,("close_file: file %s. Delete on close was set and unlink failed \
with error %s\n", fsp->fsp_name, strerror(errno) ));
+ }
}
if(fsp->granted_oplock == True)
diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c
index c9765c9ba8..44e9d499e2 100644
--- a/source3/smbd/trans2.c
+++ b/source3/smbd/trans2.c
@@ -1653,7 +1653,8 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
case SMB_SET_FILE_DISPOSITION_INFO: /* Set delete on close for open file. */
{
- if (tran_call == TRANSACT2_SETFILEINFO) {
+ if (tran_call == TRANSACT2_SETFILEINFO)
+ {
files_struct *fsp = file_fsp(params,0);
BOOL delete_on_close = (CVAL(pdata,0) ? True : False);
@@ -1671,28 +1672,75 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
return(ERROR(ERRDOS,ERRnoaccess));
/*
- * If the flag has changed from its previous value then
+ * If the flag has been set then
* modify the share mode entry for all files we have open
* on this device and inode to tell other smbds we have
* changed the delete on close flag.
*/
- if(GET_DELETE_ON_CLOSE_FLAG(fsp->share_mode) != delete_on_close)
+ if(delete_on_close && !GET_DELETE_ON_CLOSE_FLAG(fsp->share_mode))
{
int token;
+ int i;
files_struct *iterate_fsp;
SMB_DEV_T dev = fsp->fd_ptr->dev;
SMB_INO_T inode = fsp->fd_ptr->inode;
-
- DEBUG(10,("call_trans2setfilepathinfo: %s delete on close flag for fnum = %d, file %s\n",
- delete_on_close ? "Adding" : "Removing", fsp->fnum, fsp->fsp_name ));
+ int num_share_modes;
+ share_mode_entry *current_shares = NULL;
if(lock_share_entry(fsp->conn, dev, inode, &token) == False)
return(ERROR(ERRDOS,ERRnoaccess));
/*
+ * Before we allow this we need to ensure that all current opens
+ * on the file have the GET_ALLOW_SHARE_DELETE flag set. If they
+ * do not then we deny this (as we are essentially deleting the
+ * file at this point.
+ */
+
+ num_share_modes = get_share_modes(conn, token, dev, inode, &current_shares);
+ for(i = 0; i < num_share_modes; i++)
+ {
+ if(!GET_ALLOW_SHARE_DELETE(current_shares[i].share_mode))
+ {
+ DEBUG(5,("call_trans2setfilepathinfo: refusing to set delete on close flag for fnum = %d, \
+file %s as a share exists that was not opened with FILE_DELETE access.\n",
+ fsp->fnum, fsp->fsp_name ));
+ /*
+ * Release the lock.
+ */
+
+ unlock_share_entry(fsp->conn, dev, inode, token);
+
+ /*
+ * current_shares was malloced by get_share_modes - free it here.
+ */
+
+ free((char *)current_shares);
+
+ /*
+ * Even though share violation would be more appropriate here,
+ * return ERRnoaccess as that's what NT does.
+ */
+
+ return(ERROR(ERRDOS,ERRnoaccess));
+ }
+ }
+
+ /*
+ * current_shares was malloced by get_share_modes - free it here.
+ */
+
+ free((char *)current_shares);
+
+ DEBUG(10,("call_trans2setfilepathinfo: %s delete on close flag for fnum = %d, file %s\n",
+ delete_on_close ? "Adding" : "Removing", fsp->fnum, fsp->fsp_name ));
+
+ /*
* Go through all files we have open on the same device and
* inode (hanging off the same hash bucket) and set the DELETE_ON_CLOSE_FLAG.
+ * Other smbd's that have this file open will have to fend for themselves. We
+ * take care of this (rare) case in close_file(). See the comment there.
*/
for(iterate_fsp = file_find_di_first(dev, inode); iterate_fsp;
@@ -1704,24 +1752,26 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
DEBUG(10,("call_trans2setfilepathinfo: Changing share mode for fnum %d, file %s\
dev = %x, inode = %.0f from %x to %x\n",
- iterate_fsp->fnum, iterate_fsp->fsp_name, (unsigned int)dev,
- (double)inode, iterate_fsp->share_mode, new_share_mode ));
+ iterate_fsp->fnum, iterate_fsp->fsp_name, (unsigned int)dev,
+ (double)inode, iterate_fsp->share_mode, new_share_mode ));
if(modify_share_mode(token, iterate_fsp, new_share_mode)==False)
DEBUG(0,("call_trans2setfilepathinfo: failed to change delete on close for fnum %d, \
dev = %x, inode = %.0f\n", iterate_fsp->fnum, (unsigned int)dev, (double)inode));
}
+ /*
+ * Set the delete on close flag in the reference
+ * counted struct. Delete when the last reference
+ * goes away.
+ */
+ fsp->fd_ptr->delete_on_close = delete_on_close;
+
unlock_share_entry(fsp->conn, dev, inode, token);
- }
+
+ } /* end if(delete_on_close && !GET_DELETE_ON_CLOSE_FLAG(fsp->share_mode)) */
} /* end if lp_share_modes() */
- /*
- * Set the delete on close flag in the reference
- * counted struct. Delete when the last reference
- * goes away.
- */
- fsp->fd_ptr->delete_on_close = delete_on_close;
} else
return(ERROR(ERRDOS,ERRunknownlevel));
break;