diff options
author | Jeremy Allison <jra@samba.org> | 1998-10-23 17:25:14 +0000 |
---|---|---|
committer | Jeremy Allison <jra@samba.org> | 1998-10-23 17:25:14 +0000 |
commit | 6d99f5f339b572df1cd737286fbb64ad23f74fc7 (patch) | |
tree | 04cb738b2cad1f8140e88208414b7f65f1892cab /source3 | |
parent | a9d1a8996324cd5344bc5579c4556632c59ef3e2 (diff) | |
download | samba-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')
-rw-r--r-- | source3/smbd/close.c | 13 | ||||
-rw-r--r-- | source3/smbd/trans2.c | 80 |
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, ¤t_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; |