From 1cea3dc96fb09a6016f46aad86b79af4f1600c85 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 21 Jan 1999 02:41:42 +0000 Subject: Added code to let NT do a 'delete on close' request on an open directory handle. WinCenter (multi-user NT system) is doing this. This code still needs testing.... Jeremy. --- source/include/proto.h | 4 +- source/include/smb.h | 1 + source/smbd/close.c | 21 +++++- source/smbd/files.c | 4 +- source/smbd/nttrans.c | 29 +++++++- source/smbd/open.c | 2 + source/smbd/reply.c | 176 +++++++++++++++++++++++++++---------------------- source/smbd/trans2.c | 176 ++++++++++++++++++++++++++----------------------- 8 files changed, 245 insertions(+), 168 deletions(-) diff --git a/source/include/proto.h b/source/include/proto.h index 928c7c10cf6..7c4b2433294 100644 --- a/source/include/proto.h +++ b/source/include/proto.h @@ -2285,7 +2285,7 @@ BOOL change_oem_password(struct smb_passwd *smbpw, char *new_passwd, BOOL overri /*The following definitions come from smbd/close.c */ int close_file(files_struct *fsp, BOOL normal_close); -void close_directory(files_struct *fsp); +void close_directory(files_struct *fsp, BOOL normal_close); /*The following definitions come from smbd/conn.c */ @@ -2429,6 +2429,7 @@ int reply_ntcancel(connection_struct *conn, int reply_nttranss(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize); void remove_pending_change_notify_requests_by_fid(files_struct *fsp); +void remove_pending_change_notify_requests_by_filename(files_struct *fsp); void process_pending_change_notify_queue(time_t t); int reply_nttrans(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize); @@ -2565,6 +2566,7 @@ int reply_printqueue(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize); int reply_printwrite(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize); 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); int rename_internals(connection_struct *conn, char *inbuf, char *outbuf, char *name, diff --git a/source/include/smb.h b/source/include/smb.h index 88303b57642..641d16068a7 100644 --- a/source/include/smb.h +++ b/source/include/smb.h @@ -632,6 +632,7 @@ typedef struct files_struct BOOL granted_oplock; BOOL sent_oplock_break; BOOL is_directory; + BOOL directory_delete_on_close; char *fsp_name; } files_struct; diff --git a/source/smbd/close.c b/source/smbd/close.c index 708571bae6b..52e6a4940bc 100644 --- a/source/smbd/close.c +++ b/source/smbd/close.c @@ -177,10 +177,29 @@ with error %s\n", fsp->fsp_name, strerror(errno) )); Close a directory opened by an NT SMB call. ****************************************************************************/ -void close_directory(files_struct *fsp) +void close_directory(files_struct *fsp, BOOL normal_close) { remove_pending_change_notify_requests_by_fid(fsp); + /* + * NT can set delete_on_close of the last open + * reference to a directory also. + */ + + if (normal_close && fsp->directory_delete_on_close) { + BOOL ok = rmdir_internals(fsp->conn, fsp->fsp_name); + DEBUG(5,("close_directory: %s. Delete on close was set - deleting directory %s.\n", + fsp->fsp_name, ok ? "succeeded" : "failed" )); + + /* + * Ensure we remove any change notify requests that would + * now fail as the directory has been deleted. + */ + + if(ok) + remove_pending_change_notify_requests_by_filename(fsp); + } + /* * Do the code common to files and directories. */ diff --git a/source/smbd/files.c b/source/smbd/files.c index 3a41c837669..7ba8f0f54f3 100644 --- a/source/smbd/files.c +++ b/source/smbd/files.c @@ -187,7 +187,7 @@ void file_close_conn(connection_struct *conn) next = fsp->next; if (fsp->conn == conn && fsp->open) { if (fsp->is_directory) - close_directory(fsp); + close_directory(fsp,False); else close_file(fsp,False); } @@ -245,7 +245,7 @@ void file_close_user(int vuid) if(!fsp->is_directory) close_file(fsp,False); else - close_directory(fsp); + close_directory(fsp,False); } } } diff --git a/source/smbd/nttrans.c b/source/smbd/nttrans.c index 5ea2ae0c73a..e7811c605b5 100644 --- a/source/smbd/nttrans.c +++ b/source/smbd/nttrans.c @@ -789,7 +789,7 @@ int reply_ntcreate_and_X(connection_struct *conn, if(fsp->is_directory) { if(dos_stat(fsp->fsp_name, &sbuf) != 0) { - close_directory(fsp); + close_directory(fsp,True); restore_case_semantics(file_attributes); return(ERROR(ERRDOS,ERRnoaccess)); } @@ -1384,6 +1384,33 @@ static void remove_pending_change_notify_requests_by_mid(int mid) } } +/**************************************************************************** + Delete entries by filename and cnum from the change notify pending queue. + Always send reply. +*****************************************************************************/ + +void remove_pending_change_notify_requests_by_filename(files_struct *fsp) +{ + change_notify_buf *cnbp = (change_notify_buf *)ubi_slFirst( &change_notify_queue ); + change_notify_buf *prev = NULL; + + while(cnbp != NULL) { + /* + * We know it refers to the same directory if the connection number and + * the filename are identical. + */ + if((cnbp->fsp->conn == fsp->conn) && strequal(cnbp->fsp->fsp_name,fsp->fsp_name)) { + change_notify_reply_packet(cnbp->request_buf,0,NT_STATUS_CANCELLED); + free((char *)ubi_slRemNext( &change_notify_queue, prev)); + cnbp = (change_notify_buf *)(prev ? ubi_slNext(prev) : ubi_slFirst(&change_notify_queue)); + continue; + } + + prev = cnbp; + cnbp = (change_notify_buf *)ubi_slNext(cnbp); + } +} + /**************************************************************************** Process the change notify queue. Note that this is only called as root. *****************************************************************************/ diff --git a/source/smbd/open.c b/source/smbd/open.c index 9e662e9f805..e4819881d92 100644 --- a/source/smbd/open.c +++ b/source/smbd/open.c @@ -540,6 +540,7 @@ static void open_file(files_struct *fsp,connection_struct *conn, fsp->granted_oplock = False; fsp->sent_oplock_break = False; fsp->is_directory = False; + fsp->directory_delete_on_close = False; fsp->conn = conn; /* * Note that the file name here is the *untranslated* name @@ -1101,6 +1102,7 @@ int open_directory(files_struct *fsp,connection_struct *conn, fsp->granted_oplock = False; fsp->sent_oplock_break = False; fsp->is_directory = True; + fsp->directory_delete_on_close = False; fsp->conn = conn; /* * Note that the file name here is the *untranslated* name diff --git a/source/smbd/reply.c b/source/smbd/reply.c index 293aff71f5d..d338e288ecf 100644 --- a/source/smbd/reply.c +++ b/source/smbd/reply.c @@ -2612,7 +2612,7 @@ int reply_close(connection_struct *conn, * handle. */ DEBUG(3,("close directory fnum=%d\n", fsp->fnum)); - close_directory(fsp); + close_directory(fsp,True); } else { /* * Close ordinary file. @@ -3066,6 +3066,7 @@ int reply_mkdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, Static function used by reply_rmdir to delete an entire directory tree recursively. ****************************************************************************/ + static BOOL recursive_rmdir(char *directory) { char *dname = NULL; @@ -3124,8 +3125,97 @@ static BOOL recursive_rmdir(char *directory) } /**************************************************************************** - reply to a rmdir + The internals of the rmdir code - called elsewhere. ****************************************************************************/ + +BOOL rmdir_internals(connection_struct *conn, char *directory) +{ + BOOL ok; + + ok = (dos_rmdir(directory) == 0); + if(!ok && (errno == ENOTEMPTY) && lp_veto_files(SNUM(conn))) + { + /* + * Check to see if the only thing in this directory are + * vetoed files/directories. If so then delete them and + * retry. If we fail to delete any of them (and we *don't* + * do a recursive delete) then fail the rmdir. + */ + BOOL all_veto_files = True; + char *dname; + void *dirptr = OpenDir(conn, directory, False); + + if(dirptr != NULL) + { + int dirpos = TellDir(dirptr); + while ((dname = ReadDirName(dirptr))) + { + if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0)) + continue; + if(!IS_VETO_PATH(conn, dname)) + { + all_veto_files = False; + break; + } + } + if(all_veto_files) + { + SeekDir(dirptr,dirpos); + while ((dname = ReadDirName(dirptr))) + { + pstring fullname; + SMB_STRUCT_STAT st; + + if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0)) + continue; + + /* Construct the full name. */ + if(strlen(directory) + strlen(dname) + 1 >= sizeof(fullname)) + { + errno = ENOMEM; + break; + } + pstrcpy(fullname, directory); + pstrcat(fullname, "/"); + pstrcat(fullname, dname); + + if(dos_lstat(fullname, &st) != 0) + break; + if(st.st_mode & S_IFDIR) + { + if(lp_recursive_veto_delete(SNUM(conn))) + { + if(recursive_rmdir(fullname) != 0) + break; + } + if(dos_rmdir(fullname) != 0) + break; + } + else if(dos_unlink(fullname) != 0) + break; + } + CloseDir(dirptr); + /* Retry the rmdir */ + ok = (dos_rmdir(directory) == 0); + } + else + CloseDir(dirptr); + } + else + errno = ENOTEMPTY; + } + + if (!ok) + DEBUG(3,("rmdir_internals: couldn't remove directory %s : %s\n", + directory,strerror(errno))); + + return ok; +} + +/**************************************************************************** + Reply to a rmdir. +****************************************************************************/ + int reply_rmdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { pstring directory; @@ -3137,84 +3227,10 @@ int reply_rmdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, unix_convert(directory,conn, NULL,&bad_path,NULL); if (check_name(directory,conn)) - { - - dptr_closepath(directory,SVAL(inbuf,smb_pid)); - ok = (dos_rmdir(directory) == 0); - if(!ok && (errno == ENOTEMPTY) && lp_veto_files(SNUM(conn))) - { - /* Check to see if the only thing in this directory are - vetoed files/directories. If so then delete them and - retry. If we fail to delete any of them (and we *don't* - do a recursive delete) then fail the rmdir. */ - BOOL all_veto_files = True; - char *dname; - void *dirptr = OpenDir(conn, directory, False); - - if(dirptr != NULL) - { - int dirpos = TellDir(dirptr); - while ((dname = ReadDirName(dirptr))) - { - if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0)) - continue; - if(!IS_VETO_PATH(conn, dname)) - { - all_veto_files = False; - break; - } - } - if(all_veto_files) - { - SeekDir(dirptr,dirpos); - while ((dname = ReadDirName(dirptr))) - { - pstring fullname; - SMB_STRUCT_STAT st; - - if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0)) - continue; - - /* Construct the full name. */ - if(strlen(directory) + strlen(dname) + 1 >= sizeof(fullname)) - { - errno = ENOMEM; - break; - } - pstrcpy(fullname, directory); - pstrcat(fullname, "/"); - pstrcat(fullname, dname); - - if(dos_lstat(fullname, &st) != 0) - break; - if(st.st_mode & S_IFDIR) - { - if(lp_recursive_veto_delete(SNUM(conn))) - { - if(recursive_rmdir(fullname) != 0) - break; - } - if(dos_rmdir(fullname) != 0) - break; - } - else if(dos_unlink(fullname) != 0) - break; - } - CloseDir(dirptr); - /* Retry the rmdir */ - ok = (dos_rmdir(directory) == 0); - } - else - CloseDir(dirptr); - } - else - errno = ENOTEMPTY; - } - - if (!ok) - DEBUG(3,("couldn't remove directory %s : %s\n", - directory,strerror(errno))); - } + { + dptr_closepath(directory,SVAL(inbuf,smb_pid)); + ok = rmdir_internals(conn, directory); + } if (!ok) { diff --git a/source/smbd/trans2.c b/source/smbd/trans2.c index c9ade4b6ba1..704a610726c 100644 --- a/source/smbd/trans2.c +++ b/source/smbd/trans2.c @@ -1270,6 +1270,9 @@ static int call_trans2qfilepathinfo(connection_struct *conn, } return(UNIXERROR(ERRDOS,ERRbadpath)); } + + delete_pending = fsp->directory_delete_on_close; + } else { /* * Original code - this is an open file. @@ -1674,122 +1677,129 @@ static int call_trans2setfilepathinfo(connection_struct *conn, BOOL delete_on_close = (CVAL(pdata,0) ? True : False); if(fsp->is_directory) - return(ERROR(ERRDOS,ERRnoaccess)); - - /* - * We can only set the delete on close flag if - * the share mode contained ALLOW_SHARE_DELETE - */ - - if(lp_share_modes(SNUM(conn))) { - if(!GET_ALLOW_SHARE_DELETE(fsp->share_mode)) - return(ERROR(ERRDOS,ERRnoaccess)); + fsp->directory_delete_on_close = delete_on_close; + DEBUG(10, ("call_trans2setfilepathinfo: %s delete on close flag for fnum = %d, directory %s\n", + delete_on_close ? "Added" : "Removed", fsp->fnum, fsp->fsp_name )); + + } + else + { /* - * 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. + * We can only set the delete on close flag if + * the share mode contained ALLOW_SHARE_DELETE */ - if(delete_on_close && !GET_DELETE_ON_CLOSE_FLAG(fsp->share_mode)) + if(lp_share_modes(SNUM(conn))) { - int token; - int i; - files_struct *iterate_fsp; - SMB_DEV_T dev = fsp->fd_ptr->dev; - SMB_INO_T inode = fsp->fd_ptr->inode; - int num_share_modes; - share_mode_entry *current_shares = NULL; - - if(lock_share_entry(fsp->conn, dev, inode, &token) == False) + if(!GET_ALLOW_SHARE_DELETE(fsp->share_mode)) 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. + * 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. */ - num_share_modes = get_share_modes(conn, token, dev, inode, ¤t_shares); - for(i = 0; i < num_share_modes; i++) + if(delete_on_close && !GET_DELETE_ON_CLOSE_FLAG(fsp->share_mode)) { - if(!GET_ALLOW_SHARE_DELETE(current_shares[i].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; + 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++) { - DEBUG(5,("call_trans2setfilepathinfo: refusing to set delete on close flag for fnum = %d, \ + 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. - */ + fsp->fnum, fsp->fsp_name )); + /* + * Release the lock. + */ - unlock_share_entry(fsp->conn, dev, inode, token); + unlock_share_entry(fsp->conn, dev, inode, token); - /* - * current_shares was malloced by get_share_modes - free it here. - */ + /* + * current_shares was malloced by get_share_modes - free it here. + */ - free((char *)current_shares); + free((char *)current_shares); - /* - * Even though share violation would be more appropriate here, - * return ERRnoaccess as that's what NT does. - */ + /* + * Even though share violation would be more appropriate here, + * return ERRnoaccess as that's what NT does. + */ - return(ERROR(ERRDOS,ERRnoaccess)); + return(ERROR(ERRDOS,ERRnoaccess)); + } } - } - /* - * current_shares was malloced by get_share_modes - free it here. - */ + /* + * current_shares was malloced by get_share_modes - free it here. + */ - free((char *)current_shares); + 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 )); + 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. - */ + /* + * 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; - iterate_fsp = file_find_di_next(iterate_fsp)) - { - int new_share_mode = (delete_on_close ? - (iterate_fsp->share_mode | DELETE_ON_CLOSE_FLAG) : - (iterate_fsp->share_mode & ~DELETE_ON_CLOSE_FLAG) ); + for(iterate_fsp = file_find_di_first(dev, inode); iterate_fsp; + iterate_fsp = file_find_di_next(iterate_fsp)) + { + int new_share_mode = (delete_on_close ? + (iterate_fsp->share_mode | DELETE_ON_CLOSE_FLAG) : + (iterate_fsp->share_mode & ~DELETE_ON_CLOSE_FLAG) ); - DEBUG(10,("call_trans2setfilepathinfo: Changing share mode for fnum %d, file %s \ + 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, \ + 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); + /* + * 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; - DEBUG(10, ("call_trans2setfilepathinfo: %s delete on close flag for fnum = %d, file %s\n", - delete_on_close ? "Added" : "Removed", fsp->fnum, fsp->fsp_name )); + 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() */ + DEBUG(10, ("call_trans2setfilepathinfo: %s delete on close flag for fnum = %d, file %s\n", + delete_on_close ? "Added" : "Removed", fsp->fnum, fsp->fsp_name )); + } /* end if(delete_on_close && !GET_DELETE_ON_CLOSE_FLAG(fsp->share_mode)) */ + } /* end if lp_share_modes() */ + } /* end if is_directory. */ } else return(ERROR(ERRDOS,ERRunknownlevel)); break; -- cgit