Updated the format using the new verify-patches script.
[rsync-patches.git] / inplace.diff
1 Patch from Mark Curtis to implement the --inplace option.
2 Improved by Wayne Davison.
3
4 Run these commands after applying the patch:
5
6     autoconf
7     autoheader
8     ./configure
9     make
10
11 Optional:
12
13     make rsync.1
14
15 --- orig/configure.in   2004-04-30 18:04:07
16 +++ configure.in        2004-07-03 20:19:54
17 @@ -442,7 +442,7 @@ dnl AC_FUNC_MEMCMP
18  AC_FUNC_UTIME_NULL
19  AC_FUNC_ALLOCA
20  AC_CHECK_FUNCS(waitpid wait4 getcwd strdup strerror chown chmod mknod mkfifo \
21 -    fchmod fstat strchr readlink link utime utimes strftime mtrace \
22 +    fchmod fstat strchr readlink link utime utimes strftime mtrace ftruncate \
23      memmove lchown vsnprintf snprintf asprintf setsid glob strpbrk \
24      strlcat strlcpy strtol mallinfo getgroups setgroups geteuid getegid)
25  
26 --- orig/match.c        2004-07-02 23:31:44
27 +++ match.c     2004-07-03 20:19:54
28 @@ -23,6 +23,7 @@ extern int verbose;
29  extern int am_server;
30  extern int do_progress;
31  extern int checksum_seed;
32 +extern int inplace;
33  
34  typedef unsigned short tag;
35  
36 @@ -52,9 +53,26 @@ static size_t *tag_table;
37  #define gettag2(s1,s2) (((s1) + (s2)) & 0xFFFF)
38  #define gettag(sum) gettag2((sum)&0xFFFF,(sum)>>16)
39  
40 +static struct sum_buf *current_sums;
41 +static int current_s2length;
42 +
43  static int compare_targets(struct target *t1,struct target *t2)
44  {
45 -       return (int)t1->t - (int)t2->t;
46 +       struct sum_buf *s1, *s2;
47 +
48 +       if (t1->t != t2->t)
49 +               return t1->t < t2->t ? -1 : 1;
50 +
51 +       s1 = &current_sums[t1->i];
52 +       s2 = &current_sums[t2->i];
53 +
54 +       if (s1->sum1 != s2->sum1)
55 +               return s1->sum1 < s2->sum1 ? -1 : 1;
56 +
57 +       if (s1->len != s2->len)
58 +               return s1->len < s2->len ? -1 : 1;
59 +
60 +       return memcmp(s1->sum2, s2->sum2, current_s2length);
61  }
62  
63  
64 @@ -74,6 +92,8 @@ static void build_hash_table(struct sum_
65                 targets[i].t = gettag(s->sums[i].sum1);
66         }
67  
68 +       current_sums = s->sums;
69 +       current_s2length = s->s2length;
70         qsort(targets,s->count,sizeof(targets[0]),(int (*)())compare_targets);
71  
72         for (i = 0; i < TABLESIZE; i++)
73 @@ -192,12 +212,24 @@ static void hash_search(int f,struct sum
74                         unsigned int l;
75                         size_t i = targets[j].i;
76  
77 -                       if (sum != s->sums[i].sum1)
78 +                       if (sum != s->sums[i].sum1) {
79 +                               if (done_csum2)
80 +                                       break;
81                                 continue;
82 +                       }
83  
84                         /* also make sure the two blocks are the same length */
85                         l = MIN((OFF_T)s->blength, len-offset);
86 -                       if (l != s->sums[i].len)
87 +                       if (l != s->sums[i].len) {
88 +                               if (done_csum2)
89 +                                       break;
90 +                               continue;
91 +                       }
92 +
93 +                       /* inplace: ensure chunk's offset is either >= our
94 +                        * offset or that the data didn't move. */
95 +                       if (inplace && s->sums[i].offset < offset
96 +                           && s->sums[i].i >= 0)
97                                 continue;
98  
99                         if (verbose > 3)
100 @@ -215,15 +247,41 @@ static void hash_search(int f,struct sum
101                                 continue;
102                         }
103  
104 +                       /* If inplace is enabled, the best possible match is
105 +                        * one with an identical offset, so we prefer that over
106 +                        * the following last_i+1 optimization. */
107 +                       if (inplace) {
108 +                               do {
109 +                                       size_t i2 = targets[j].i;
110 +                                       if (s->sums[i2].offset != offset)
111 +                                               continue;
112 +                                       if (i2 != i) {
113 +                                               if (sum != s->sums[i2].sum1)
114 +                                                       break;
115 +                                               if (memcmp(sum2, s->sums[i2].sum2,
116 +                                                          s->s2length) != 0)
117 +                                                       break;
118 +                                               i = i2;
119 +                                       }
120 +                                       /* Use this as a flag to indicate that
121 +                                        * this chunk was at the same offset on
122 +                                        * both the sender and the receiver. */
123 +                                       s->sums[i].i = -1;
124 +                                       goto set_last_i;
125 +                               } while (++j < s->count && targets[j].t == t);
126 +                       }
127 +
128                         /* we've found a match, but now check to see
129                          * if last_i can hint at a better match */
130                         if (i != last_i + 1 && last_i + 1 < s->count
131 +                           && (!inplace || s->sums[last_i+1].offset >= offset || s->sums[last_i+1].i < 0)
132                             && sum == s->sums[last_i+1].sum1
133                             && memcmp(sum2, s->sums[last_i+1].sum2, s->s2length) == 0) {
134                                 /* we've found an adjacent match - the RLL coder
135                                  * will be happy */
136                                 i = last_i + 1;
137                         }
138 +                   set_last_i:
139                         last_i = i;
140  
141                         matched(f,s,buf,offset,i);
142 --- orig/options.c      2004-06-20 19:30:00
143 +++ options.c   2004-07-03 20:19:54
144 @@ -94,6 +94,7 @@ int ignore_errors = 0;
145  int modify_window = 0;
146  int blocking_io = -1;
147  int checksum_seed = 0;
148 +int inplace = 0;
149  unsigned int block_size = 0;
150  
151  
152 @@ -149,6 +150,7 @@ char *bind_address;
153  static void print_rsync_version(enum logcode f)
154  {
155         char const *got_socketpair = "no ";
156 +       char const *have_inplace = "no ";
157         char const *hardlinks = "no ";
158         char const *links = "no ";
159         char const *ipv6 = "no ";
160 @@ -158,6 +160,10 @@ static void print_rsync_version(enum log
161         got_socketpair = "";
162  #endif
163  
164 +#if HAVE_FTRUNCATE
165 +       have_inplace = "";
166 +#endif
167 +
168  #if SUPPORT_HARD_LINKS
169         hardlinks = "";
170  #endif
171 @@ -183,8 +189,8 @@ static void print_rsync_version(enum log
172         /* Note that this field may not have type ino_t.  It depends
173          * on the complicated interaction between largefile feature
174          * macros. */
175 -       rprintf(f, "              %sIPv6, %d-bit system inums, %d-bit internal inums\n",
176 -               ipv6,
177 +       rprintf(f, "              %sinplace, %sIPv6, %d-bit system inums, %d-bit internal inums\n",
178 +               have_inplace, ipv6,
179                 (int) (sizeof dumstat->st_ino * 8),
180                 (int) (sizeof (uint64) * 8));
181  #ifdef MAINTAINER_MODE
182 @@ -234,6 +240,7 @@ void usage(enum logcode F)
183    rprintf(F,"     --backup-dir            make backups into this directory\n");
184    rprintf(F,"     --suffix=SUFFIX         backup suffix (default %s w/o --backup-dir)\n",BACKUP_SUFFIX);
185    rprintf(F," -u, --update                update only (don't overwrite newer files)\n");
186 +  rprintf(F,"     --inplace               update the destination file inplace (see man page)\n");
187    rprintf(F," -K, --keep-dirlinks         treat symlinked dir on receiver as dir\n");
188    rprintf(F," -l, --links                 copy symlinks as symlinks\n");
189    rprintf(F," -L, --copy-links            copy the referent of all symlinks\n");
190 @@ -341,6 +348,7 @@ static struct poptOption long_options[] 
191    {"sparse",          'S', POPT_ARG_NONE,   &sparse_files, 0, 0, 0 },
192    {"cvs-exclude",     'C', POPT_ARG_NONE,   &cvs_exclude, 0, 0, 0 },
193    {"update",          'u', POPT_ARG_NONE,   &update_only, 0, 0, 0 },
194 +  {"inplace",          0,  POPT_ARG_NONE,   &inplace, 0, 0, 0 },
195    {"keep-dirlinks",   'K', POPT_ARG_NONE,   &keep_dirlinks, 0, 0, 0 },
196    {"links",           'l', POPT_ARG_NONE,   &preserve_links, 0, 0, 0 },
197    {"copy-links",      'L', POPT_ARG_NONE,   &copy_links, 0, 0, 0 },
198 @@ -739,6 +747,18 @@ int parse_arguments(int *argc, const cha
199                         bwlimit_writemax = 512;
200         }
201  
202 +       if (inplace) {
203 +#if HAVE_FTRUNCATE
204 +               if (keep_partial)
205 +                       keep_partial = 0;
206 +#else
207 +               snprintf(err_buf, sizeof err_buf,
208 +                        "inplace is not supported on this %s\n",
209 +                        am_server ? "server" : "client");
210 +               return 0;
211 +#endif
212 +       }
213 +
214         if (files_from) {
215                 char *colon;
216                 if (*argc != 2 && !(am_server && am_sender && *argc == 1)) {
217 @@ -963,6 +983,9 @@ void server_options(char **args,int *arg
218         if (opt_ignore_existing && am_sender)
219                 args[ac++] = "--ignore-existing";
220  
221 +       if (inplace)
222 +               args[ac++] = "--inplace";
223 +
224         if (tmpdir) {
225                 args[ac++] = "--temp-dir";
226                 args[ac++] = tmpdir;
227 --- orig/receiver.c     2004-07-02 18:23:01
228 +++ receiver.c  2004-07-03 20:19:54
229 @@ -48,6 +48,7 @@ extern int ignore_errors;
230  extern int orig_umask;
231  extern int keep_partial;
232  extern int checksum_seed;
233 +extern int inplace;
234  
235  static void delete_one(char *fn, int is_dir)
236  {
237 @@ -255,16 +256,30 @@ static int receive_data(int f_in,struct 
238                         sum_update(map,len);
239                 }
240  
241 -               if (fd != -1 && write_file(fd, map, len) != (int)len) {
242 -                       rsyserr(FERROR, errno, "write failed on %s",
243 -                               full_fname(fname));
244 -                       exit_cleanup(RERR_FILEIO);
245 +               if (!inplace || offset != offset2) {
246 +                       if (fd != -1 && write_file(fd, map, len) != (int)len) {
247 +                               rsyserr(FERROR, errno, "write failed on %s",
248 +                                       full_fname(fname));
249 +                               exit_cleanup(RERR_FILEIO);
250 +                       }
251 +               } else {
252 +                       flush_write_file(fd);
253 +                       if (do_lseek(fd,(OFF_T)len,SEEK_CUR) != offset+len) {
254 +                               rprintf(FERROR, "lseek failed on %s: %s, %lli, %lli, %i\n",
255 +                                       full_fname(fname), strerror(errno), do_lseek(fd,0,SEEK_CUR), (offset+len), i);
256 +                               exit_cleanup(RERR_FILEIO);
257 +                       }
258                 }
259                 offset += len;
260         }
261  
262         flush_write_file(fd);
263  
264 +#ifdef HAVE_FTRUNCATE
265 +       if (inplace)
266 +               ftruncate(fd, offset);
267 +#endif
268 +
269         if (do_progress)
270                 end_progress(total_size);
271  
272 @@ -414,44 +429,59 @@ int recv_files(int f_in,struct file_list
273                 } else
274                         mapbuf = NULL;
275  
276 -               if (!get_tmpname(fnametmp,fname)) {
277 -                       if (mapbuf)
278 -                               unmap_file(mapbuf);
279 -                       if (fd1 != -1)
280 -                               close(fd1);
281 -                       continue;
282 -               }
283 +               /* We now check to see if we are writing file "inplace" */
284 +               if (inplace)  {
285 +                       fd2 = do_open(fnamecmp, O_WRONLY|O_CREAT, 0);
286 +                       if (fd2 == -1) {
287 +                               rsyserr(FERROR, errno, "open %s failed",
288 +                                       full_fname(fnamecmp));
289 +                               receive_data(f_in,mapbuf,-1,NULL,file->length);
290 +                               if (mapbuf)
291 +                                       unmap_file(mapbuf);
292 +                               if (fd1 != -1)
293 +                                       close(fd1);
294 +                               continue;
295 +                       }
296 +               } else {
297 +                       if (!get_tmpname(fnametmp,fname)) {
298 +                               if (mapbuf)
299 +                                       unmap_file(mapbuf);
300 +                               if (fd1 != -1)
301 +                                       close(fd1);
302 +                               continue;
303 +                       }
304  
305 -               strlcpy(template, fnametmp, sizeof template);
306 +                       strlcpy(template, fnametmp, sizeof template);
307  
308 -               /* we initially set the perms without the
309 -                * setuid/setgid bits to ensure that there is no race
310 -                * condition. They are then correctly updated after
311 -                * the lchown. Thanks to snabb@epipe.fi for pointing
312 -                * this out.  We also set it initially without group
313 -                * access because of a similar race condition. */
314 -               fd2 = do_mkstemp(fnametmp, file->mode & INITACCESSPERMS);
315 -
316 -               /* in most cases parent directories will already exist
317 -                * because their information should have been previously
318 -                * transferred, but that may not be the case with -R */
319 -               if (fd2 == -1 && relative_paths && errno == ENOENT &&
320 -                   create_directory_path(fnametmp, orig_umask) == 0) {
321 -                       strlcpy(fnametmp, template, sizeof fnametmp);
322 +                       /* we initially set the perms without the
323 +                        * setuid/setgid bits to ensure that there is no race
324 +                        * condition. They are then correctly updated after
325 +                        * the lchown. Thanks to snabb@epipe.fi for pointing
326 +                        * this out.  We also set it initially without group
327 +                        * access because of a similar race condition. */
328                         fd2 = do_mkstemp(fnametmp, file->mode & INITACCESSPERMS);
329 -               }
330 -               if (fd2 == -1) {
331 -                       rsyserr(FERROR, errno, "mkstemp %s failed",
332 -                               full_fname(fnametmp));
333 -                       receive_data(f_in,mapbuf,-1,NULL,file->length);
334 -                       if (mapbuf)
335 -                               unmap_file(mapbuf);
336 -                       if (fd1 != -1)
337 -                               close(fd1);
338 -                       continue;
339 -               }
340  
341 -               cleanup_set(fnametmp, fname, file, mapbuf, fd1, fd2);
342 +                       /* in most cases parent directories will already exist
343 +                        * because their information should have been previously
344 +                        * transferred, but that may not be the case with -R */
345 +                       if (fd2 == -1 && relative_paths && errno == ENOENT
346 +                           && create_directory_path(fnametmp, orig_umask) == 0) {
347 +                               strlcpy(fnametmp, template, sizeof fnametmp);
348 +                               fd2 = do_mkstemp(fnametmp, file->mode & INITACCESSPERMS);
349 +                       }
350 +                       if (fd2 == -1) {
351 +                               rsyserr(FERROR, errno, "mkstemp %s failed",
352 +                                       full_fname(fnametmp));
353 +                               receive_data(f_in,mapbuf,-1,NULL,file->length);
354 +                               if (mapbuf)
355 +                                       unmap_file(mapbuf);
356 +                               if (fd1 != -1)
357 +                                       close(fd1);
358 +                               continue;
359 +                       }
360 +
361 +                       cleanup_set(fnametmp, fname, file, mapbuf, fd1, fd2);
362 +               }
363  
364                 if (!am_server && verbose)
365                         rprintf(FINFO, "%s\n", fname);
366 --- orig/rsync.c        2004-07-02 18:06:32
367 +++ rsync.c     2004-07-03 20:19:54
368 @@ -34,6 +34,7 @@ extern int force_delete;
369  extern int recurse;
370  extern int make_backups;
371  extern char *backup_dir;
372 +extern int inplace;
373  
374  
375  /*
376 @@ -239,6 +240,13 @@ void finish_transfer(char *fname, char *
377         if (make_backups && !make_backup(fname))
378                 return;
379  
380 +       if (inplace) {
381 +               if (verbose > 2)
382 +                       rprintf(FINFO, "finishing %s\n", fname);
383 +               set_perms(fname, file, NULL, 0);
384 +               return;
385 +       }
386 +
387         /* move tmp file over real file */
388         if (verbose > 2)
389                 rprintf(FINFO, "renaming %s to %s\n", fnametmp, fname);
390 --- orig/rsync.yo       2004-06-17 06:32:00
391 +++ rsync.yo    2004-07-03 20:19:55
392 @@ -289,6 +289,7 @@ verb(
393       --backup-dir            make backups into this directory
394       --suffix=SUFFIX         backup suffix (default ~ w/o --backup-dir)
395   -u, --update                update only (don't overwrite newer files)
396 +     --inplace               update the destination file inplace
397   -K, --keep-dirlinks         treat symlinked dir on receiver as dir
398   -l, --links                 copy symlinks as symlinks
399   -L, --copy-links            copy the referent of all symlinks
400 @@ -484,6 +485,17 @@ dit(bf(-K, --keep-dirlinks)) On the rece
401  pointing to a directory, it will be treated as matching a directory
402  from the sender.
403  
404 +dit(bf(--inplace)) This causes rsync not to create a new copy of the file
405 +and then move it into place.  Instead rsync will overwrite the existing
406 +file, meaning that the rsync algorithm can't extract the full ammount of
407 +network reduction it might otherwise.
408 +
409 +This option is useful for transfer of large files with block based changes
410 +and also on systems that are disk bound not network bound.
411 +
412 +WARNING: If the transfer is interrupted, you will have an inconsistent file
413 +and the transfer should be run again.
414 +
415  dit(bf(-l, --links)) When symlinks are encountered, recreate the
416  symlink on the destination.
417