summaryrefslogtreecommitdiff
path: root/source3
diff options
context:
space:
mode:
authorJeremy Allison <jra@samba.org>2005-12-13 18:11:50 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 11:05:49 -0500
commitab7a4f7e8e4b946a8acd0a205c16dbf6a3afecad (patch)
treed91dcd5c2a731925cec24e67c5a4e78dd3b7dac0 /source3
parent7d2771e758d4e8ef0adb45e55775b524de4dba9a (diff)
downloadsamba-ab7a4f7e8e4b946a8acd0a205c16dbf6a3afecad.tar.gz
samba-ab7a4f7e8e4b946a8acd0a205c16dbf6a3afecad.tar.bz2
samba-ab7a4f7e8e4b946a8acd0a205c16dbf6a3afecad.zip
r12213: Final fix for #3303 - send rename messages to smbd's
that have open file handles to allow them to correctly implement delete on close. There is a further correctness fix I'm intending to add to this to cope with different share paths, but not right now... Jeremy. (This used to be commit 932e337db8788e75344e1c7cf1ef009d090cb039)
Diffstat (limited to 'source3')
-rw-r--r--source3/include/messages.h1
-rw-r--r--source3/include/smb.h6
-rw-r--r--source3/locking/locking.c56
-rw-r--r--source3/smbd/close.c45
-rw-r--r--source3/smbd/open.c47
-rw-r--r--source3/smbd/reply.c28
-rw-r--r--source3/smbd/server.c1
-rw-r--r--source3/smbd/service.c5
8 files changed, 169 insertions, 20 deletions
diff --git a/source3/include/messages.h b/source3/include/messages.h
index abe219374e..4b1732d42d 100644
--- a/source3/include/messages.h
+++ b/source3/include/messages.h
@@ -68,6 +68,7 @@
#define MSG_SMB_ASYNC_LEVEL2_BREAK 3008
#define MSG_SMB_OPEN_RETRY 3009
#define MSG_SMB_KERNEL_BREAK 3010
+#define MSG_SMB_FILE_RENAME 3011
/* winbind messages */
#define MSG_WINBIND_FINISHED 4001
diff --git a/source3/include/smb.h b/source3/include/smb.h
index 6e995b8198..fc256c6f69 100644
--- a/source3/include/smb.h
+++ b/source3/include/smb.h
@@ -1459,6 +1459,12 @@ struct kernel_oplock_message {
unsigned long file_id;
};
+struct file_renamed_message {
+ SMB_DEV_T dev;
+ SMB_INO_T inode;
+ char names[1]; /* A variable area containing sharepath and filename. */
+};
+
/*
* On the wire return values for oplock types.
*/
diff --git a/source3/locking/locking.c b/source3/locking/locking.c
index 2debc2c23e..d89fe931ef 100644
--- a/source3/locking/locking.c
+++ b/source3/locking/locking.c
@@ -642,14 +642,24 @@ struct share_mode_lock *get_share_mode_lock(TALLOC_CTX *mem_ctx,
/*******************************************************************
Sets the service name and filename for rename.
- At this point we should emit "rename" smbd messages to all
- interested process id's.
+ At this point we emit "file renamed" messages to all
+ process id's that have this file open.
+ Based on an initial code idea from SATOH Fumiyasu <fumiya@samba.gr.jp>
********************************************************************/
BOOL rename_share_filename(struct share_mode_lock *lck,
const char *servicepath,
const char *newname)
{
+ struct file_renamed_message *frm = NULL;
+ size_t sp_len;
+ size_t fn_len;
+ size_t msg_len;
+ int i;
+
+ DEBUG(10, ("rename_share_filename: servicepath %s newname %s\n",
+ servicepath, newname));
+
/*
* rename_internal_fsp() and rename_internals() add './' to
* head of newname if newname does not contain a '/'.
@@ -658,13 +668,53 @@ BOOL rename_share_filename(struct share_mode_lock *lck,
newname += 2;
}
- lck->filename = talloc_strdup(lck, newname);
lck->servicepath = talloc_strdup(lck, servicepath);
+ lck->filename = talloc_strdup(lck, newname);
if (lck->filename == NULL || lck->servicepath == NULL) {
DEBUG(0, ("rename_share_filename: talloc failed\n"));
return False;
}
lck->modified = True;
+
+ sp_len = strlen(lck->servicepath);
+ fn_len = strlen(lck->filename);
+
+ msg_len = sizeof(*frm) + sp_len + 1 + fn_len + 1;
+
+ /* Set up the name changed message. */
+ frm = TALLOC(lck, msg_len);
+ if (!frm) {
+ return False;
+ }
+ frm->dev = lck->dev;
+ frm->inode = lck->ino;
+
+ DEBUG(10,("rename_share_filename: msg_len = %d\n", msg_len ));
+
+ safe_strcpy(&frm->names[0], lck->servicepath, sp_len);
+ safe_strcpy(&frm->names[sp_len + 1], lck->filename, fn_len);
+
+ /* Send the messages. */
+ for (i=0; i<lck->num_share_modes; i++) {
+ struct share_mode_entry *se = &lck->share_modes[i];
+ if (!is_valid_share_mode_entry(se)) {
+ continue;
+ }
+ /* But not to ourselves... */
+ if (procid_is_me(&se->pid)) {
+ continue;
+ }
+
+ DEBUG(10,("rename_share_filename: sending rename message to pid %u "
+ "dev %x, inode %.0f sharepath %s newname %s\n",
+ (unsigned int)procid_to_pid(&se->pid),
+ (unsigned int)frm->dev, (double)frm->inode,
+ lck->servicepath, lck->filename ));
+
+ message_send_pid(se->pid, MSG_SMB_FILE_RENAME,
+ frm, msg_len, True);
+ }
+
return True;
}
diff --git a/source3/smbd/close.c b/source3/smbd/close.c
index d84b9f925b..407c607838 100644
--- a/source3/smbd/close.c
+++ b/source3/smbd/close.c
@@ -227,20 +227,43 @@ static int close_normal_file(files_struct *fsp, BOOL normal_close)
*/
if (normal_close && delete_file) {
+ SMB_STRUCT_STAT sbuf;
+
DEBUG(5,("close_file: file %s. Delete on close was set - deleting file.\n",
fsp->fsp_name));
- if(SMB_VFS_UNLINK(conn,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) ));
+
+ /* We can only delete the file if the name we have
+ is still valid and hasn't been renamed. */
+
+ if(SMB_VFS_STAT(conn,fsp->fsp_name,&sbuf) != 0) {
+ DEBUG(5,("close_file: file %s. Delete on close was set "
+ "and stat failed with error %s\n",
+ fsp->fsp_name, strerror(errno) ));
+ } else {
+ if(sbuf.st_dev != fsp->dev || sbuf.st_ino != fsp->inode) {
+ DEBUG(5,("close_file: file %s. Delete on close was set and "
+ "dev and/or inode does not match\n",
+ fsp->fsp_name ));
+ DEBUG(5,("close_file: file %s. stored dev = %x, inode = %.0f "
+ "stat dev = %x, inode = %.0f\n",
+ fsp->fsp_name,
+ (unsigned int)fsp->dev, (double)fsp->inode,
+ (unsigned int)sbuf.st_dev, (double)sbuf.st_ino ));
+
+ } else if(SMB_VFS_UNLINK(conn,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) ));
+ }
+ process_pending_change_notify_queue((time_t)0);
}
- process_pending_change_notify_queue((time_t)0);
}
talloc_free(lck);
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index b3f0589dc7..7621ee001d 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -2042,3 +2042,50 @@ files_struct *open_file_stat(connection_struct *conn, char *fname,
return fsp;
}
+
+/****************************************************************************
+ Receive notification that one of our open files has been renamed by another
+ smbd process.
+****************************************************************************/
+
+void msg_file_was_renamed(int msg_type, struct process_id src, void *buf, size_t len)
+{
+ files_struct *fsp;
+ struct file_renamed_message *frm = (struct file_renamed_message *)buf;
+ const char *sharepath;
+ const char *newname;
+ size_t sp_len;
+
+ if (buf == NULL || len < sizeof(*frm)) {
+ DEBUG(0, ("msg_file_was_renamed: Got invalid msg len %d\n", (int)len));
+ return;
+ }
+
+ sharepath = &frm->names[0];
+ newname = sharepath + strlen(sharepath) + 1;
+ sp_len = strlen(sharepath);
+
+ DEBUG(10,("msg_file_was_renamed: Got rename message for sharepath %s, new name %s, "
+ "dev %x, inode %.0f\n",
+ sharepath, newname, (unsigned int)frm->dev, (double)frm->inode ));
+
+ for(fsp = file_find_di_first(frm->dev, frm->inode); fsp; fsp = file_find_di_next(fsp)) {
+ if (memcmp(fsp->conn->connectpath, sharepath, sp_len) == 0) {
+ DEBUG(10,("msg_file_was_renamed: renaming file fnum %d from %s -> %s\n",
+ fsp->fnum, fsp->fsp_name, newname ));
+ string_set(&fsp->fsp_name, newname);
+ } else {
+ /* TODO. JRA. */
+ /* Now we have the complete path we can work out if this is
+ actually within this share and adjust newname accordingly. */
+ DEBUG(10,("msg_file_was_renamed: share mismatch (sharepath %s "
+ "not sharepath %s) "
+ "fnum %d from %s -> %s\n",
+ fsp->conn->connectpath,
+ sharepath,
+ fsp->fnum,
+ fsp->fsp_name,
+ newname ));
+ }
+ }
+}
diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c
index d3739c8847..5ddba4c2bf 100644
--- a/source3/smbd/reply.c
+++ b/source3/smbd/reply.c
@@ -4082,13 +4082,15 @@ static BOOL resolve_wildcards(const char *name1, char *name2)
}
/****************************************************************************
- Ensure open files have their names updates.
+ Ensure open files have their names updated. Updated to notify other smbd's
+ asynchronously.
****************************************************************************/
-static void rename_open_files(connection_struct *conn, SMB_DEV_T dev, SMB_INO_T inode, char *newname)
+static void rename_open_files(connection_struct *conn, SMB_DEV_T dev, SMB_INO_T inode, const char *newname)
{
files_struct *fsp;
BOOL did_rename = False;
+ struct share_mode_lock *lck = NULL;
for(fsp = file_find_di_first(dev, inode); fsp; fsp = file_find_di_next(fsp)) {
DEBUG(10,("rename_open_files: renaming file fnum %d (dev = %x, inode = %.0f) from %s -> %s\n",
@@ -4098,9 +4100,24 @@ static void rename_open_files(connection_struct *conn, SMB_DEV_T dev, SMB_INO_T
did_rename = True;
}
- if (!did_rename)
+ if (!did_rename) {
DEBUG(10,("rename_open_files: no open files on dev %x, inode %.0f for %s\n",
(unsigned int)dev, (double)inode, newname ));
+ }
+
+ /* Notify all remote smbd's. */
+ lck = get_share_mode_lock(NULL, dev, inode, NULL, NULL);
+ if (lck == NULL) {
+ DEBUG(5,("rename_open_files: Could not get share mode lock for file %s\n",
+ fsp->fsp_name));
+ return;
+ }
+
+ /* Change the stored filename. */
+ rename_share_filename(lck, conn->connectpath, newname);
+
+ /* Send messages to all smbd's (not ourself) that the name has changed. */
+ talloc_free(lck);
}
/****************************************************************************
@@ -4238,10 +4255,11 @@ NTSTATUS rename_internals_fsp(connection_struct *conn, files_struct *fsp, char *
return NT_STATUS_OK;
}
- if (errno == ENOTDIR || errno == EISDIR)
+ if (errno == ENOTDIR || errno == EISDIR) {
error = NT_STATUS_OBJECT_NAME_COLLISION;
- else
+ } else {
error = map_nt_error_from_unix(errno);
+ }
DEBUG(3,("rename_internals_fsp: Error %s rename %s -> %s\n",
nt_errstr(error), fsp->fsp_name,newname));
diff --git a/source3/smbd/server.c b/source3/smbd/server.c
index 1bf9dbc374..304f1b588e 100644
--- a/source3/smbd/server.c
+++ b/source3/smbd/server.c
@@ -330,6 +330,7 @@ static BOOL open_sockets_smbd(BOOL is_daemon, BOOL interactive, const char *smb_
message_register(MSG_SMB_SAM_SYNC, msg_sam_sync);
message_register(MSG_SMB_SAM_REPL, msg_sam_repl);
message_register(MSG_SHUTDOWN, msg_exit_server);
+ message_register(MSG_SMB_FILE_RENAME, msg_file_was_renamed);
/* now accept incoming connections - forking a new process
for each incoming connection */
diff --git a/source3/smbd/service.c b/source3/smbd/service.c
index 210edde5d8..c9e2cdcf50 100644
--- a/source3/smbd/service.c
+++ b/source3/smbd/service.c
@@ -44,7 +44,7 @@ void set_conn_connectpath(connection_struct *conn, const pstring connectpath)
while (*s == '/') {
s++;
}
- if ((d != destname) && (*s != '\0')) {
+ if ((d > destname + 1) && (*s != '\0')) {
*d++ = '/';
}
start_of_name_component = True;
@@ -119,6 +119,9 @@ void set_conn_connectpath(connection_struct *conn, const pstring connectpath)
*(d-1) = '\0';
}
+ DEBUG(10,("set_conn_connectpath: service %s, connectpath = %s\n",
+ lp_servicename(SNUM(conn)), destname ));
+
string_set(&conn->connectpath, destname);
}