summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeremy Allison <jra@samba.org>1999-01-21 02:41:42 +0000
committerJeremy Allison <jra@samba.org>1999-01-21 02:41:42 +0000
commit1cea3dc96fb09a6016f46aad86b79af4f1600c85 (patch)
tree342a8ed11cb56e4ddd52fb5bb8790cd177bbc537
parent9a274b4e760c974f438616dbceb44420ea3ef19d (diff)
downloadsamba-1cea3dc96fb09a6016f46aad86b79af4f1600c85.tar.gz
samba-1cea3dc96fb09a6016f46aad86b79af4f1600c85.tar.xz
samba-1cea3dc96fb09a6016f46aad86b79af4f1600c85.zip
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.
-rw-r--r--source/include/proto.h4
-rw-r--r--source/include/smb.h1
-rw-r--r--source/smbd/close.c21
-rw-r--r--source/smbd/files.c4
-rw-r--r--source/smbd/nttrans.c29
-rw-r--r--source/smbd/open.c2
-rw-r--r--source/smbd/reply.c176
-rw-r--r--source/smbd/trans2.c176
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,11 +177,30 @@ 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.
*/
close_filestruct(fsp);
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));
}
@@ -1385,6 +1385,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, &current_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, &current_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;