r12213: Final fix for #3303 - send rename messages to smbd's
authorJeremy Allison <jra@samba.org>
Tue, 13 Dec 2005 18:11:50 +0000 (18:11 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 16:05:49 +0000 (11:05 -0500)
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)

source3/include/messages.h
source3/include/smb.h
source3/locking/locking.c
source3/smbd/close.c
source3/smbd/open.c
source3/smbd/reply.c
source3/smbd/server.c
source3/smbd/service.c

index abe219374ed57eb59387625821a474aedc17cde1..4b1732d42d17d0f39ff4bde29db900edf49bb33b 100644 (file)
@@ -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
index 6e995b81988fdfe9ed4783f31b31a3179c2a604c..fc256c6f698d2c794d1d94c37376905f09c6fc51 100644 (file)
@@ -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.
  */
index 2debc2c23e5abe1d837525035f3bb098065a4e3c..d89fe931ef644c1fadff08a2a0aac6bb34ccb30a 100644 (file)
@@ -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;
 }
 
index d84b9f925bf672d1cff1caef263214d1fc4a8946..407c6078385c827f59eaa1fe01c62f4dc5e657bb 100644 (file)
@@ -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);
index b3f0589dc7b2f8cba2bff0c957fa9a1c0d4f15ab..7621ee001dd0b0f719759d5b734b2d3fffeeb8ac 100644 (file)
@@ -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 ));
+               }
+        }
+}
index d3739c8847f585badc985d0204cbdc3f2bcf91fc..5ddba4c2bf3c67658a9a4cd2797883cb057d3e61 100644 (file)
@@ -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));
index 1bf9dbc374a67762c8594bc5da03e05fe1a49c33..304f1b588e2d205057d6f8352b180170253495c3 100644 (file)
@@ -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 */
index 210edde5d8fd20367625b335244e5e4e30e1a17e..c9e2cdcf50ca2620543727d235ff79606aa1a768 100644 (file)
@@ -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);
 }