Add new & improved `--copy-devices` option.
authorWayne Davison <wayne@opencoder.net>
Sun, 27 Mar 2022 20:36:31 +0000 (13:36 -0700)
committerWayne Davison <wayne@opencoder.net>
Sun, 27 Mar 2022 21:04:59 +0000 (14:04 -0700)
NEWS.md
flist.c
options.c
rsync.1.md
rsyncd.conf.5.md
sender.c

diff --git a/NEWS.md b/NEWS.md
index 6dfed8d0261984c807f34539c2f558f2febbe887..2b08dfa8cb4455ee5f30c0fe191291d604ac27a9 100644 (file)
--- a/NEWS.md
+++ b/NEWS.md
 
  - Added the [`--fsync`](rsync.1#opt) option (promoted from the patches repo).
 
+ - Added the [`--copy-devices`](rsync.1#opt) option.  Compared to the
+   historical version from the rsync-patches repo, this version: properly
+   handles `--checksum`; fixes a truncation bug when doing an `--inplace` copy
+   onto a longer file; fixes several bugs in the `--itemize` output; and only
+   the sending side needs the enhanced rsync for the copy to work.
+
  - Reduced memory usage for an incremental transfer that has a bunch of small
    directories.
 
diff --git a/flist.c b/flist.c
index d72ba60979986e2229297842a565396d1abe5b3c..1ba306bcdcf43e2772eb00cefcb868093e4cae4e 100644 (file)
--- a/flist.c
+++ b/flist.c
@@ -43,6 +43,7 @@ extern int use_qsort;
 extern int xfer_dirs;
 extern int filesfrom_fd;
 extern int one_file_system;
+extern int copy_devices;
 extern int copy_dirlinks;
 extern int preserve_uid;
 extern int preserve_gid;
@@ -700,6 +701,7 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
        int alloc_len, basename_len, linkname_len;
        int extra_len = file_extra_cnt * EXTRA_LEN;
        int first_hlink_ndx = -1;
+       char real_ISREG_entry;
        int64 file_length;
 #ifdef CAN_SET_NSEC
        uint32 modtime_nsec;
@@ -814,6 +816,7 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
                                linkname_len = strlen(F_SYMLINK(first)) + 1;
                        else
                                linkname_len = 0;
+                       real_ISREG_entry = S_ISREG(mode) ? 1 : 0;
                        goto create_object;
                }
        }
@@ -941,10 +944,20 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
 #endif
                linkname_len = 0;
 
+       if (copy_devices && IS_DEVICE(mode)) {
+               /* This is impossible in the official release, but some pre-release patches
+                * didn't convert the device into a file before sending, so we'll do it here
+                * (even though the length is typically 0 and any checksum data is zeros). */
+               mode = S_IFREG | (mode & ACCESSPERMS);
+               modtime = time(NULL); /* The mtime on the device is not up-to-date, so set it to "now". */
+               real_ISREG_entry = 0;
+       } else
+               real_ISREG_entry = S_ISREG(mode) ? 1 : 0;
+
 #ifdef SUPPORT_HARD_LINKS
   create_object:
        if (preserve_hard_links) {
-               if (protocol_version < 28 && S_ISREG(mode))
+               if (protocol_version < 28 && real_ISREG_entry)
                        xflags |= XMIT_HLINKED;
                if (xflags & XMIT_HLINKED)
                        extra_len += (inc_recurse+1) * EXTRA_LEN;
@@ -1160,8 +1173,8 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
        }
 #endif
 
-       if (always_checksum && (S_ISREG(mode) || protocol_version < 28)) {
-               if (S_ISREG(mode))
+       if (always_checksum && (real_ISREG_entry || protocol_version < 28)) {
+               if (real_ISREG_entry)
                        bp = F_SUM(file);
                else {
                        /* Prior to 28, we get a useless set of nulls. */
@@ -1360,6 +1373,18 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
        linkname_len = 0;
 #endif
 
+       if (copy_devices && am_sender && IS_DEVICE(st.st_mode)) {
+               if (st.st_size == 0) {
+                       int fd = do_open(fname, O_RDONLY, 0);
+                       if (fd >= 0) {
+                               st.st_size = get_device_size(fd, fname);
+                               close(fd);
+                       }
+               }
+               st.st_mode = S_IFREG | (st.st_mode & ACCESSPERMS);
+               st.st_mtime = time(NULL); /* The mtime on the device is not up-to-date, so set it to "now". */
+       }
+
 #ifdef ST_MTIME_NSEC
        if (st.ST_MTIME_NSEC && protocol_version >= 31)
                extra_len += EXTRA_LEN;
index d08f00038fa482d217201ae44d5a7fbf532ea0cc..93bdb2372236a8c6cf501700275f97cea870bc32 100644 (file)
--- a/options.c
+++ b/options.c
@@ -47,6 +47,7 @@ int append_mode = 0;
 int keep_dirlinks = 0;
 int copy_dirlinks = 0;
 int copy_links = 0;
+int copy_devices = 0;
 int write_devices = 0;
 int preserve_links = 0;
 int preserve_hard_links = 0;
@@ -656,6 +657,7 @@ static struct poptOption long_options[] = {
   {"no-D",             0,  POPT_ARG_NONE,   0, OPT_NO_D, 0, 0 },
   {"devices",          0,  POPT_ARG_VAL,    &preserve_devices, 1, 0, 0 },
   {"no-devices",       0,  POPT_ARG_VAL,    &preserve_devices, 0, 0, 0 },
+  {"copy-devices",     0,  POPT_ARG_NONE,   &copy_devices, 0, 0, 0 },
   {"write-devices",    0,  POPT_ARG_VAL,    &write_devices, 1, 0, 0 },
   {"no-write-devices", 0,  POPT_ARG_VAL,    &write_devices, 0, 0, 0 },
   {"specials",         0,  POPT_ARG_VAL,    &preserve_specials, 1, 0, 0 },
@@ -949,6 +951,7 @@ static void set_refuse_options(void)
                 || strcmp("iconv", longName) == 0
                 || strcmp("no-iconv", longName) == 0
                 || strcmp("checksum-seed", longName) == 0
+                || strcmp("copy-devices", longName) == 0 /* disable wild-match (it gets refused below) */
                 || strcmp("write-devices", longName) == 0 /* disable wild-match (it gets refused below) */
                 || strcmp("log-format", longName) == 0 /* aka out-format (NOT log-file-format) */
                 || strcmp("sender", longName) == 0
@@ -960,6 +963,7 @@ static void set_refuse_options(void)
        assert(list_end != NULL);
 
        if (am_daemon) { /* Refused by default, but can be accepted via a negated exact match. */
+               parse_one_refuse_match(0, "copy-devices", list_end);
                parse_one_refuse_match(0, "write-devices", list_end);
        }
 
@@ -2917,6 +2921,9 @@ void server_options(char **args, int *argc_p)
        else if (remove_source_files)
                args[ac++] = "--remove-sent-files";
 
+       if (copy_devices && !am_sender)
+               args[ac++] = "--copy-devices";
+
        if (preallocate_files && am_sender)
                args[ac++] = "--preallocate";
 
index ca0ed7f7c97003cd608b19ed7832cfedb182ac64..313025df872f430e148633d79394f3b52f6ec819 100644 (file)
@@ -373,6 +373,8 @@ has its own detailed description later in this manpage.
 --owner, -o              preserve owner (super-user only)
 --group, -g              preserve group
 --devices                preserve device files (super-user only)
+--copy-devices           copy device contents as a regular file
+--write-devices          write to devices as files (implies --inplace)
 --specials               preserve special files
 -D                       same as --devices --specials
 --times, -t              preserve modification times
@@ -385,7 +387,6 @@ has its own detailed description later in this manpage.
 --fake-super             store/recover privileged attrs using xattrs
 --sparse, -S             turn sequences of nulls into sparse blocks
 --preallocate            allocate dest files before writing them
---write-devices          write to devices as files (implies --inplace)
 --dry-run, -n            perform a trial run with no changes made
 --whole-file, -W         copy files whole (w/o delta-xfer algorithm)
 --checksum-choice=STR    choose the checksum algorithm (aka --cc)
@@ -1473,6 +1474,14 @@ your home directory (remove the '=' for that).
     The `-D` option is equivalent to "[`--devices`](#opt)
     [`--specials`](#opt)".
 
+0.  `--copy-devices`
+
+    This tells rsync to treat a device on the sending side as a regular file,
+    allowing it to be copied to a normal destination file (or another device
+    if `--write-devices` was also specifed).
+
+    This option is refused by default by an rsync daemon.
+
 0.  `--write-devices`
 
     This tells rsync to treat a device on the receiving side as a regular file,
index c386403bac0898e9af756372aade331bc3a4a0f3..c61765a76c9e2e72f77f0b027447067240e408df 100644 (file)
@@ -933,9 +933,10 @@ the values of parameters.  See the GLOBAL PARAMETERS section for more details.
     If you are un-refusing the compress option, you may want to match
     "`!compress*`" if you also want to allow the `--compress-level` option.
 
-    Note that the "write-devices" option is refused by default, but can be
-    explicitly accepted with "`!write-devices`".  The options "log-file" and
-    "log-file-format" are forcibly refused and cannot be accepted.
+    Note that the "copy-devices" & "write-devices" options are refused by
+    default, but they can be explicitly accepted with "`!copy-devices`" and/or
+    "`!write-devices`".  The options "log-file" and "log-file-format" are
+    forcibly refused and cannot be accepted.
 
     Here are all the options that are not matched by wild-cards:
 
index 92724c816dd5ec09428343bc37f47a8dfd0ecf11..9159da4db80ac598b50dda7ea76178624200b832 100644 (file)
--- a/sender.c
+++ b/sender.c
@@ -37,6 +37,7 @@ extern int io_error;
 extern int flist_eof;
 extern int whole_file;
 extern int allowed_lull;
+extern int copy_devices;
 extern int preserve_xattrs;
 extern int protocol_version;
 extern int remove_source_files;
@@ -366,6 +367,15 @@ void send_files(int f_in, int f_out)
                        exit_cleanup(RERR_FILEIO);
                }
 
+               if (IS_DEVICE(st.st_mode)) {
+                       if (!copy_devices) {
+                               rprintf(FERROR, "attempt to copy device contents without --copy-devices\n");
+                               exit_cleanup(RERR_PROTOCOL);
+                       }
+                       if (st.st_size == 0)
+                               st.st_size = get_device_size(fd, fname);
+               }
+
                if (append_mode > 0 && st.st_size < F_LENGTH(file)) {
                        rprintf(FWARNING, "skipped diminished file: %s\n",
                                full_fname(fname));