Bug 5820 - make rsync update symlinks/devices/specials atomically
Summary: make rsync update symlinks/devices/specials atomically
Status: RESOLVED FIXED
Alias: None
Product: rsync
Classification: Unclassified
Component: core (show other bugs)
Version: 3.0.4
Hardware: All All
: P3 enhancement (vote)
Target Milestone: ---
Assignee: Wayne Davison
QA Contact: Rsync QA Contact
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2008-10-09 02:25 UTC by Igor Sysoev
Modified: 2020-07-28 00:25 UTC (History)
0 users

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Igor Sysoev 2008-10-09 02:25:52 UTC
rsync does not replace symlink atomically, i.e, first it unlinks an old symlink and then creates a new symlink. There is short time when symlink does not exist at all. The attached patch against 3.0.4 fixes the issue. However, it has been tested on FreeBSD only. The patch is also available on
http://sysoev.ru/tmp/rsync.symlink.patch

--- generator.c 2008-08-31 20:51:29.000000000 +0400
+++ generator.c 2008-10-08 23:10:17.000000000 +0400
@@ -1556,9 +1556,40 @@
                                if (remove_source_files == 1)
                                        goto return_with_success;
                                goto cleanup;
+                       } else {
+                               char fnametmp[MAXPATHLEN];
+
+                               /* Replace the symlink atomically. */
+                               if (!get_tmpname(fnametmp, fname))
+                                       goto cleanup;
+                               if (!mktemp(fnametmp))
+                                       goto cleanup;
+                               if (do_symlink(sl, fnametmp) != 0) {
+                                       rsyserr(FERROR_XFER, errno, "symlink %s -> \"%s\" failed",
+                                               full_fname(fnametmp), sl);
+                                       goto cleanup;
+                               }
+                               set_file_attrs(fnametmp, file, NULL, NULL, 0);
+                               if (do_rename(fnametmp, fname) != 0) {
+                                       rsyserr(FERROR_XFER, errno, "rename %s -> %s failed",
+                                               full_fname(fnametmp), full_fname(fname));
+                                       goto cleanup;
+                               }
+                               if (itemizing) {
+                                       itemize(fname, file, ndx, statret, &sx,
+                                               ITEM_LOCAL_CHANGE|ITEM_REPORT_CHANGE, 0, NULL);
+                               }
+                               if (verbose)
+                                       rprintf(code, "%s -> %s\n", fname, sl);
+#ifdef SUPPORT_HARD_LINKS
+                               if (preserve_hard_links && F_IS_HLINKED(file))
+                                       finish_hard_link(file, fname, ndx, NULL, itemizing, code, -1);
+#endif
+                               if (remove_source_files)
+                                       goto return_with_success;
+                               goto cleanup;
                        }
-                       /* Not the right symlink (or not a symlink), so
-                        * delete it. */
+                       /* Not not a symlink, so delete it. */
                        if (delete_item(fname, sx.st.st_mode, del_opts | DEL_FOR_SYMLINK) != 0)
                                goto cleanup;
                } else if (basis_dir[0] != NULL) {
Comment 1 Matt McCutchen 2008-10-09 12:14:23 UTC
The same issue exists with device/special files and also when replacing a file with one of a different type (e.g., a regular file with a symlink).  Let's find a way to solve this without duplicating a lot of code.  See also the --stage-all option of bug 4561 comment 6.
Comment 2 Wayne Davison 2020-07-28 00:25:12 UTC
The code got an atomic_create() function a while back so that the generator's updates get renamed into place in an atomic operation.