Added write-devices.diff patch from Darryl Dixon.
[rsync-patches.git] / write-devices.diff
1 This patch adds the --write-devices option, which will try to write
2 data into a device when used as a destination.
3
4 To use this patch, run these commands for a successful build:
5
6     patch -p1 <patches/write-devices.diff
7     ./configure                      (optional if already run)
8     make
9
10 This patch has not yet been tested by me (Wayne), but was provided
11 Darryl Dixon.  Thanks!
12
13 based-on: 3b8f8192227b14e708bf535072485e50f4362270
14 diff --git a/generator.c b/generator.c
15 --- a/generator.c
16 +++ b/generator.c
17 @@ -39,6 +39,7 @@ extern int preserve_acls;
18  extern int preserve_xattrs;
19  extern int preserve_links;
20  extern int preserve_devices;
21 +extern int write_devices;
22  extern int preserve_specials;
23  extern int preserve_hard_links;
24  extern int preserve_executability;
25 @@ -1534,7 +1535,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
26         fnamecmp = fname;
27         fnamecmp_type = FNAMECMP_FNAME;
28  
29 -       if (statret == 0 && !S_ISREG(sx.st.st_mode)) {
30 +       if (statret == 0 && !(S_ISREG(sx.st.st_mode) || (write_devices && IS_DEVICE(sx.st.st_mode)))) {
31                 if (delete_item(fname, sx.st.st_mode, del_opts | DEL_FOR_FILE) != 0)
32                         goto cleanup;
33                 statret = -1;
34 diff --git a/options.c b/options.c
35 --- a/options.c
36 +++ b/options.c
37 @@ -48,6 +48,7 @@ int append_mode = 0;
38  int keep_dirlinks = 0;
39  int copy_dirlinks = 0;
40  int copy_links = 0;
41 +int write_devices = 0;
42  int preserve_links = 0;
43  int preserve_hard_links = 0;
44  int preserve_acls = 0;
45 @@ -695,6 +696,7 @@ void usage(enum logcode F)
46    rprintf(F," -o, --owner                 preserve owner (super-user only)\n");
47    rprintf(F," -g, --group                 preserve group\n");
48    rprintf(F,"     --devices               preserve device files (super-user only)\n");
49 +  rprintf(F," -w  --write-devices         write to devices as regular files (implies --inplace)\n");
50    rprintf(F,"     --specials              preserve special files\n");
51    rprintf(F," -D                          same as --devices --specials\n");
52    rprintf(F," -t, --times                 preserve modification times\n");
53 @@ -863,6 +865,7 @@ static struct poptOption long_options[] = {
54    {"no-D",             0,  POPT_ARG_NONE,   0, OPT_NO_D, 0, 0 },
55    {"devices",          0,  POPT_ARG_VAL,    &preserve_devices, 1, 0, 0 },
56    {"no-devices",       0,  POPT_ARG_VAL,    &preserve_devices, 0, 0, 0 },
57 +  {"write-devices",   'w', POPT_ARG_NONE,   0, 'w', 0, 0 },
58    {"specials",         0,  POPT_ARG_VAL,    &preserve_specials, 1, 0, 0 },
59    {"no-specials",      0,  POPT_ARG_VAL,    &preserve_specials, 0, 0, 0 },
60    {"links",           'l', POPT_ARG_VAL,    &preserve_links, 1, 0, 0 },
61 @@ -1744,6 +1747,11 @@ int parse_arguments(int *argc_p, const char ***argv_p)
62                         return 0;
63  #endif
64  
65 +               case 'w':
66 +                       write_devices = 1;
67 +                       inplace = 1;
68 +                       break;
69 +
70                 default:
71                         /* A large opt value means that set_refuse_options()
72                          * turned this option off. */
73 @@ -2627,6 +2635,9 @@ void server_options(char **args, int *argc_p)
74         else if (remove_source_files)
75                 args[ac++] = "--remove-sent-files";
76  
77 +       if (write_devices)
78 +               args[ac++] = "--write-devices";
79 +
80         if (ac > MAX_SERVER_ARGS) { /* Not possible... */
81                 rprintf(FERROR, "argc overflow in server_options().\n");
82                 exit_cleanup(RERR_MALLOC);
83 diff --git a/receiver.c b/receiver.c
84 --- a/receiver.c
85 +++ b/receiver.c
86 @@ -37,6 +37,7 @@ extern int protocol_version;
87  extern int relative_paths;
88  extern int preserve_hard_links;
89  extern int preserve_perms;
90 +extern int write_devices;
91  extern int preserve_xattrs;
92  extern int basis_dir_cnt;
93  extern int make_backups;
94 @@ -198,6 +199,7 @@ int open_tmpfile(char *fnametmp, const char *fname, struct file_struct *file)
95  static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
96                         const char *fname, int fd, OFF_T total_size)
97  {
98 +       STRUCT_STAT st;
99         static char file_sum1[MAX_DIGEST_LEN];
100         struct map_struct *mapbuf;
101         struct sum_struct sum;
102 @@ -317,10 +319,14 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
103                 goto report_write_error;
104  
105  #ifdef HAVE_FTRUNCATE
106 -       if (inplace && fd != -1
107 -        && ftruncate(fd, offset) < 0) {
108 -               rsyserr(FERROR_XFER, errno, "ftruncate failed on %s",
109 -                       full_fname(fname));
110 +       (void)do_fstat(fd,&st);
111 +       /* Makes no sense to attempt to ftruncate() a block device: */
112 +       if (!(IS_DEVICE(st.st_mode))) {
113 +               if (inplace && fd != -1
114 +                && ftruncate(fd, offset) < 0) {
115 +                       rsyserr(FERROR_XFER, errno, "ftruncate failed on %s",
116 +                               full_fname(fname));
117 +               }
118         }
119  #endif
120  
121 @@ -728,11 +734,25 @@ int recv_files(int f_in, int f_out, char *local_name)
122                         continue;
123                 }
124  
125 -               if (fd1 != -1 && !S_ISREG(st.st_mode)) {
126 +               if (fd1 != -1 && !(S_ISREG(st.st_mode) || (write_devices && IS_DEVICE(st.st_mode)))) {
127                         close(fd1);
128                         fd1 = -1;
129                 }
130  
131 +               /* On Linux systems (at least), st_size is typically 0 for devices.
132 +                * If so, try to determine the actual device size. */
133 +               if (fd1 != -1 && IS_DEVICE(st.st_mode) && st.st_size == 0) {
134 +                       OFF_T off = lseek(fd1, 0, SEEK_END);
135 +                       if (off == (OFF_T) -1)
136 +                               rsyserr(FERROR, errno, "failed to seek to end of %s to determine size", fname);
137 +                       else {
138 +                               st.st_size = off;
139 +                               off = lseek(fd1, 0, SEEK_SET);
140 +                               if (off != 0)
141 +                                       rsyserr(FERROR, errno, "failed to seek back to beginning of %s to read it", fname);
142 +                       }
143 +               }
144 +
145                 /* If we're not preserving permissions, change the file-list's
146                  * mode based on the local permissions and some heuristics. */
147                 if (!preserve_perms) {