The patches for 3.3.0.
[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: 16b49716d55a50f2e985b879b720b2c53c892a3a
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 @@ -1687,7 +1688,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
26  
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 @@ -50,6 +50,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 @@ -704,6 +705,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 @@ -885,6 +887,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 @@ -1785,6 +1788,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 @@ -2757,6 +2765,9 @@ void server_options(char **args, int *argc_p)
74         if (relative_paths && !implied_dirs && (!am_sender || protocol_version >= 30))
75                 args[ac++] = "--no-implied-dirs";
76  
77 +       if (write_devices)
78 +               args[ac++] = "--write-devices";
79 +
80         if (remove_source_files == 1)
81                 args[ac++] = "--remove-source-files";
82         else if (remove_source_files)
83 diff --git a/receiver.c b/receiver.c
84 --- a/receiver.c
85 +++ b/receiver.c
86 @@ -39,6 +39,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 @@ -229,7 +230,7 @@ int open_tmpfile(char *fnametmp, const char *fname, struct file_struct *file)
95  }
96  
97  static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
98 -                       const char *fname, int fd, OFF_T total_size)
99 +                       const char *fname, int fd, struct file_struct *file)
100  {
101         static char file_sum1[MAX_DIGEST_LEN];
102         struct map_struct *mapbuf;
103 @@ -245,12 +246,12 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
104         OFF_T preallocated_len = 0;
105  #endif
106  
107 -       if (preallocate_files && fd != -1 && total_size > 0 && (!inplace || total_size > size_r)) {
108 +       if (preallocate_files && fd != -1 && F_LENGTH(file) > 0 && (!inplace || F_LENGTH(file) > size_r)) {
109                 /* Try to preallocate enough space for file's eventual length.  Can
110                  * reduce fragmentation on filesystems like ext4, xfs, and NTFS. */
111 -               if (do_fallocate(fd, 0, total_size) == 0) {
112 +               if (do_fallocate(fd, 0, F_LENGTH(file)) == 0) {
113  #ifdef PREALLOCATE_NEEDS_TRUNCATE
114 -                       preallocated_len = total_size;
115 +                       preallocated_len = F_LENGTH(file);
116  #endif
117                 } else
118                         rsyserr(FWARNING, errno, "do_fallocate %s", full_fname(fname));
119 @@ -279,7 +280,7 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
120                 if (append_mode == 2 && mapbuf) {
121                         for (j = CHUNK_SIZE; j < sum.flength; j += CHUNK_SIZE) {
122                                 if (INFO_GTE(PROGRESS, 1))
123 -                                       show_progress(offset, total_size);
124 +                                       show_progress(offset, F_LENGTH(file));
125                                 sum_update(map_ptr(mapbuf, offset, CHUNK_SIZE),
126                                            CHUNK_SIZE);
127                                 offset = j;
128 @@ -287,7 +288,7 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
129                         if (offset < sum.flength) {
130                                 int32 len = (int32)(sum.flength - offset);
131                                 if (INFO_GTE(PROGRESS, 1))
132 -                                       show_progress(offset, total_size);
133 +                                       show_progress(offset, F_LENGTH(file));
134                                 sum_update(map_ptr(mapbuf, offset, len), len);
135                         }
136                 }
137 @@ -301,7 +302,7 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
138  
139         while ((i = recv_token(f_in, &data)) != 0) {
140                 if (INFO_GTE(PROGRESS, 1))
141 -                       show_progress(offset, total_size);
142 +                       show_progress(offset, F_LENGTH(file));
143  
144                 if (allowed_lull)
145                         maybe_send_keepalive(time(NULL), MSK_ALLOW_FLUSH | MSK_ACTIVE_RECEIVER);
146 @@ -371,20 +372,20 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
147  
148  #ifdef HAVE_FTRUNCATE
149         /* inplace: New data could be shorter than old data.
150 -        * preallocate_files: total_size could have been an overestimate.
151 +        * preallocate_files: F_LENGTH(file) could have been an overestimate.
152          *     Cut off any extra preallocated zeros from dest file. */
153         if ((inplace
154  #ifdef PREALLOCATE_NEEDS_TRUNCATE
155           || preallocated_len > offset
156  #endif
157 -         ) && fd != -1 && do_ftruncate(fd, offset) < 0) {
158 +         ) && fd != -1 && !IS_DEVICE(file->mode) && do_ftruncate(fd, offset) < 0) {
159                 rsyserr(FERROR_XFER, errno, "ftruncate failed on %s",
160                         full_fname(fname));
161         }
162  #endif
163  
164         if (INFO_GTE(PROGRESS, 1))
165 -               end_progress(total_size);
166 +               end_progress(F_LENGTH(file));
167  
168         if (fd != -1 && offset > 0 && sparse_end(fd, offset) != 0) {
169             report_write_error:
170 @@ -408,9 +409,9 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
171  }
172  
173  
174 -static void discard_receive_data(int f_in, OFF_T length)
175 +static void discard_receive_data(int f_in, struct file_struct *file)
176  {
177 -       receive_data(f_in, NULL, -1, 0, NULL, -1, length);
178 +       receive_data(f_in, NULL, -1, 0, NULL, -1, file);
179  }
180  
181  static void handle_delayed_updates(char *local_name)
182 @@ -666,7 +667,7 @@ int recv_files(int f_in, int f_out, char *local_name)
183                                         "(Skipping batched update for%s \"%s\")\n",
184                                         redoing ? " resend of" : "",
185                                         fname);
186 -                               discard_receive_data(f_in, F_LENGTH(file));
187 +                               discard_receive_data(f_in, file);
188                                 file->flags |= FLAG_FILE_SENT;
189                                 continue;
190                         }
191 @@ -677,13 +678,13 @@ int recv_files(int f_in, int f_out, char *local_name)
192                 if (!do_xfers) { /* log the transfer */
193                         log_item(FCLIENT, file, iflags, NULL);
194                         if (read_batch)
195 -                               discard_receive_data(f_in, F_LENGTH(file));
196 +                               discard_receive_data(f_in, file);
197                         continue;
198                 }
199                 if (write_batch < 0) {
200                         log_item(FCLIENT, file, iflags, NULL);
201                         if (!am_server)
202 -                               discard_receive_data(f_in, F_LENGTH(file));
203 +                               discard_receive_data(f_in, file);
204                         if (inc_recurse)
205                                 send_msg_int(MSG_SUCCESS, ndx);
206                         continue;
207 @@ -773,7 +774,7 @@ int recv_files(int f_in, int f_out, char *local_name)
208                 } else if (do_fstat(fd1,&st) != 0) {
209                         rsyserr(FERROR_XFER, errno, "fstat %s failed",
210                                 full_fname(fnamecmp));
211 -                       discard_receive_data(f_in, F_LENGTH(file));
212 +                       discard_receive_data(f_in, file);
213                         close(fd1);
214                         if (inc_recurse)
215                                 send_msg_int(MSG_NO_SEND, ndx);
216 @@ -788,18 +789,32 @@ int recv_files(int f_in, int f_out, char *local_name)
217                          */
218                         rprintf(FERROR_XFER, "recv_files: %s is a directory\n",
219                                 full_fname(fnamecmp));
220 -                       discard_receive_data(f_in, F_LENGTH(file));
221 +                       discard_receive_data(f_in, file);
222                         close(fd1);
223                         if (inc_recurse)
224                                 send_msg_int(MSG_NO_SEND, ndx);
225                         continue;
226                 }
227  
228 -               if (fd1 != -1 && !S_ISREG(st.st_mode)) {
229 +               if (fd1 != -1 && !(S_ISREG(st.st_mode) || (write_devices && IS_DEVICE(st.st_mode)))) {
230                         close(fd1);
231                         fd1 = -1;
232                 }
233  
234 +               /* On Linux systems (at least), st_size is typically 0 for devices.
235 +                * If so, try to determine the actual device size. */
236 +               if (fd1 != -1 && IS_DEVICE(st.st_mode) && st.st_size == 0) {
237 +                       OFF_T off = lseek(fd1, 0, SEEK_END);
238 +                       if (off == (OFF_T) -1)
239 +                               rsyserr(FERROR, errno, "failed to seek to end of %s to determine size", fname);
240 +                       else {
241 +                               st.st_size = off;
242 +                               off = lseek(fd1, 0, SEEK_SET);
243 +                               if (off != 0)
244 +                                       rsyserr(FERROR, errno, "failed to seek back to beginning of %s to read it", fname);
245 +                       }
246 +               }
247 +
248                 /* If we're not preserving permissions, change the file-list's
249                  * mode based on the local permissions and some heuristics. */
250                 if (!preserve_perms) {
251 @@ -831,7 +846,7 @@ int recv_files(int f_in, int f_out, char *local_name)
252                 }
253  
254                 if (fd2 == -1) {
255 -                       discard_receive_data(f_in, F_LENGTH(file));
256 +                       discard_receive_data(f_in, file);
257                         if (fd1 != -1)
258                                 close(fd1);
259                         if (inc_recurse)
260 @@ -846,8 +861,7 @@ int recv_files(int f_in, int f_out, char *local_name)
261                         rprintf(FINFO, "%s\n", fname);
262  
263                 /* recv file data */
264 -               recv_ok = receive_data(f_in, fnamecmp, fd1, st.st_size,
265 -                                      fname, fd2, F_LENGTH(file));
266 +               recv_ok = receive_data(f_in, fnamecmp, fd1, st.st_size, fname, fd2, file);
267  
268                 log_item(log_code, file, iflags, NULL);
269