]> git.ipfire.org Git - thirdparty/rsync.git/commitdiff
Added the --write-devices option.
authorWayne Davison <wayned@samba.org>
Sun, 5 Apr 2020 18:21:02 +0000 (11:21 -0700)
committerWayne Davison <wayned@samba.org>
Sun, 5 Apr 2020 18:56:28 +0000 (11:56 -0700)
This is a fleshed out version of the old one in the patches repo with
documentation & proper handling of the implied --inplace option for a
daemon's option-rufusing considerations. I ommitted the -w short option
as I would hate for someone to turn this on accidentally.

generator.c
options.c
receiver.c
rsync.yo

index 5538a92dd57ddf8671a2404a7308ada73a710f58..0ceab185db97aaa0d1163c5cd2f139b77155203b 100644 (file)
@@ -39,6 +39,7 @@ extern int preserve_acls;
 extern int preserve_xattrs;
 extern int preserve_links;
 extern int preserve_devices;
+extern int write_devices;
 extern int preserve_specials;
 extern int preserve_hard_links;
 extern int preserve_executability;
@@ -1688,7 +1689,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
 
        fnamecmp_type = FNAMECMP_FNAME;
 
-       if (statret == 0 && !S_ISREG(sx.st.st_mode)) {
+       if (statret == 0 && !(S_ISREG(sx.st.st_mode) || (write_devices && IS_DEVICE(sx.st.st_mode)))) {
                if (delete_item(fname, sx.st.st_mode, del_opts | DEL_FOR_FILE) != 0)
                        goto cleanup;
                statret = -1;
index 3464f9dd47e1df32e046e00dee7c8e28a8e8e202..d179f4752abf557302c9c0b9f0a0967b6c9a7ee6 100644 (file)
--- a/options.c
+++ b/options.c
@@ -50,6 +50,7 @@ int append_mode = 0;
 int keep_dirlinks = 0;
 int copy_dirlinks = 0;
 int copy_links = 0;
+int write_devices = 0;
 int preserve_links = 0;
 int preserve_hard_links = 0;
 int preserve_acls = 0;
@@ -721,6 +722,7 @@ void usage(enum logcode F)
 #else
   rprintf(F,"     --preallocate           pre-allocate dest files on remote receiver\n");
 #endif
+  rprintf(F,"     --write-devices         write to devices as files (implies --inplace)\n");
   rprintf(F," -n, --dry-run               perform a trial run with no changes made\n");
   rprintf(F," -W, --whole-file            copy files whole (without delta-xfer algorithm)\n");
   rprintf(F,"     --checksum-choice=STR   choose the checksum algorithms\n");
@@ -889,6 +891,8 @@ 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 },
+  {"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 },
   {"no-specials",      0,  POPT_ARG_VAL,    &preserve_specials, 0, 0, 0 },
   {"links",           'l', POPT_ARG_VAL,    &preserve_links, 1, 0, 0 },
@@ -1330,6 +1334,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
                if (!*lp_charset(module_id))
                        set_refuse_options("iconv");
 #endif
+               set_refuse_options("write-devices");
        }
 
 #ifdef ICONV_OPTION
@@ -2288,6 +2293,14 @@ int parse_arguments(int *argc_p, const char ***argv_p)
                inplace = 1;
        }
 
+       if (write_devices) {
+               if (refused_inplace) {
+                       create_refuse_error(refused_inplace);
+                       return 0;
+               }
+               inplace = 1;
+       }
+
        if (delay_updates && !partial_dir)
                partial_dir = tmp_partialdir;
 
@@ -2806,6 +2819,9 @@ void server_options(char **args, int *argc_p)
        if (relative_paths && !implied_dirs && (!am_sender || protocol_version >= 30))
                args[ac++] = "--no-implied-dirs";
 
+       if (write_devices)
+               args[ac++] = "--write-devices";
+
        if (remove_source_files == 1)
                args[ac++] = "--remove-source-files";
        else if (remove_source_files)
index 799c3d645d5fb85de010626aab3623c3b34ba767..e7441f4d756b1f3f4111e04e6653424b611609b2 100644 (file)
@@ -39,6 +39,7 @@ extern int protocol_version;
 extern int relative_paths;
 extern int preserve_hard_links;
 extern int preserve_perms;
+extern int write_devices;
 extern int preserve_xattrs;
 extern int basis_dir_cnt;
 extern int make_backups;
@@ -231,13 +232,14 @@ int open_tmpfile(char *fnametmp, const char *fname, struct file_struct *file)
 }
 
 static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
-                       const char *fname, int fd, OFF_T total_size)
+                       const char *fname, int fd, struct file_struct *file)
 {
        static char file_sum1[MAX_DIGEST_LEN];
        struct map_struct *mapbuf;
        struct sum_struct sum;
        int sum_len;
        int32 len;
+       OFF_T total_size = F_LENGTH(file);
        OFF_T offset = 0;
        OFF_T offset2;
        char *data;
@@ -379,7 +381,7 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
        /* inplace: New data could be shorter than old data.
         * preallocate_files: total_size could have been an overestimate.
         *     Cut off any extra preallocated zeros from dest file. */
-       if ((inplace || preallocated_len > offset) && fd != -1 && do_ftruncate(fd, offset) < 0) {
+       if ((inplace || preallocated_len > offset) && fd != -1 && !IS_DEVICE(file->mode) && do_ftruncate(fd, offset) < 0) {
                rsyserr(FERROR_XFER, errno, "ftruncate failed on %s",
                        full_fname(fname));
        }
@@ -402,9 +404,9 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
 }
 
 
-static void discard_receive_data(int f_in, OFF_T length)
+static void discard_receive_data(int f_in, struct file_struct *file)
 {
-       receive_data(f_in, NULL, -1, 0, NULL, -1, length);
+       receive_data(f_in, NULL, -1, 0, NULL, -1, file);
 }
 
 static void handle_delayed_updates(char *local_name)
@@ -660,7 +662,7 @@ int recv_files(int f_in, int f_out, char *local_name)
                                        "(Skipping batched update for%s \"%s\")\n",
                                        redoing ? " resend of" : "",
                                        fname);
-                               discard_receive_data(f_in, F_LENGTH(file));
+                               discard_receive_data(f_in, file);
                                file->flags |= FLAG_FILE_SENT;
                                continue;
                        }
@@ -671,13 +673,13 @@ int recv_files(int f_in, int f_out, char *local_name)
                if (!do_xfers) { /* log the transfer */
                        log_item(FCLIENT, file, iflags, NULL);
                        if (read_batch)
-                               discard_receive_data(f_in, F_LENGTH(file));
+                               discard_receive_data(f_in, file);
                        continue;
                }
                if (write_batch < 0) {
                        log_item(FCLIENT, file, iflags, NULL);
                        if (!am_server)
-                               discard_receive_data(f_in, F_LENGTH(file));
+                               discard_receive_data(f_in, file);
                        if (inc_recurse)
                                send_msg_int(MSG_SUCCESS, ndx);
                        continue;
@@ -767,7 +769,7 @@ int recv_files(int f_in, int f_out, char *local_name)
                } else if (do_fstat(fd1,&st) != 0) {
                        rsyserr(FERROR_XFER, errno, "fstat %s failed",
                                full_fname(fnamecmp));
-                       discard_receive_data(f_in, F_LENGTH(file));
+                       discard_receive_data(f_in, file);
                        close(fd1);
                        if (inc_recurse)
                                send_msg_int(MSG_NO_SEND, ndx);
@@ -782,18 +784,32 @@ int recv_files(int f_in, int f_out, char *local_name)
                         */
                        rprintf(FERROR_XFER, "recv_files: %s is a directory\n",
                                full_fname(fnamecmp));
-                       discard_receive_data(f_in, F_LENGTH(file));
+                       discard_receive_data(f_in, file);
                        close(fd1);
                        if (inc_recurse)
                                send_msg_int(MSG_NO_SEND, ndx);
                        continue;
                }
 
-               if (fd1 != -1 && !S_ISREG(st.st_mode)) {
+               if (fd1 != -1 && !(S_ISREG(st.st_mode) || (write_devices && IS_DEVICE(st.st_mode)))) {
                        close(fd1);
                        fd1 = -1;
                }
 
+               /* On Linux systems (at least), st_size is typically 0 for devices.
+                * If so, try to determine the actual device size. */
+               if (fd1 != -1 && IS_DEVICE(st.st_mode) && st.st_size == 0) {
+                       OFF_T off = lseek(fd1, 0, SEEK_END);
+                       if (off == (OFF_T) -1)
+                               rsyserr(FERROR, errno, "failed to seek to end of %s to determine size", fname);
+                       else {
+                               st.st_size = off;
+                               off = lseek(fd1, 0, SEEK_SET);
+                               if (off != 0)
+                                       rsyserr(FERROR, errno, "failed to seek back to beginning of %s to read it", fname);
+                       }
+               }
+
                /* If we're not preserving permissions, change the file-list's
                 * mode based on the local permissions and some heuristics. */
                if (!preserve_perms) {
@@ -825,7 +841,7 @@ int recv_files(int f_in, int f_out, char *local_name)
                }
 
                if (fd2 == -1) {
-                       discard_receive_data(f_in, F_LENGTH(file));
+                       discard_receive_data(f_in, file);
                        if (fd1 != -1)
                                close(fd1);
                        if (inc_recurse)
@@ -840,8 +856,7 @@ int recv_files(int f_in, int f_out, char *local_name)
                        rprintf(FINFO, "%s\n", fname);
 
                /* recv file data */
-               recv_ok = receive_data(f_in, fnamecmp, fd1, st.st_size,
-                                      fname, fd2, F_LENGTH(file));
+               recv_ok = receive_data(f_in, fnamecmp, fd1, st.st_size, fname, fd2, file);
 
                log_item(log_code, file, iflags, NULL);
 
index fdaf3d278e16b142c422c29005973f03facec162..40b3fe7e0cd5c849502b2bb83a8599703074a2a6 100644 (file)
--- a/rsync.yo
+++ b/rsync.yo
@@ -384,6 +384,7 @@ to the detailed description below for a complete description.  verb(
      --fake-super            store/recover privileged attrs using xattrs
  -S, --sparse                turn sequences of nulls into sparse blocks
      --preallocate           allocate dest files before writing
+     --write-devices         write to devices as files (implies --inplace)
  -n, --dry-run               perform a trial run with no changes made
  -W, --whole-file            copy files whole (w/o delta-xfer algorithm)
      --checksum-choice=STR   choose the checksum algorithms
@@ -1221,6 +1222,16 @@ such as named sockets and fifos.
 
 dit(bf(-D)) The bf(-D) option is equivalent to bf(--devices) bf(--specials).
 
+dit(bf(--write-devices)) This tells rsync to treat a device on the receiving
+side as a regular file, allowing the writing of file data into a device.
+
+This option implies the bf(--inplace) option.
+
+Be careful using this, as you should know what devices are present on the
+receiving side of the transfer, especially if running rsync as root.
+
+This option is refused by an rsync daemon.
+
 dit(bf(-t, --times)) This tells rsync to transfer modification times along
 with the files and update them on the remote system.  Note that if this
 option is not used, the optimization that excludes files that have not been