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