Commit | Line | Data |
---|---|---|
a2d23604 | 1 | Patch from Mark Curtis to implement the --inplace option. |
c46f8a95 | 2 | Improved by Wayne Davison. |
a2d23604 | 3 | |
c46f8a95 WD |
4 | Run these commands after applying the patch: |
5 | ||
6 | autoconf | |
7 | autoheader | |
8 | ./configure | |
ec4215e6 WD |
9 | make |
10 | ||
11 | Optional: | |
12 | ||
13 | make rsync.1 | |
c46f8a95 | 14 | |
13bed3dd WD |
15 | --- orig/configure.in 2004-04-30 18:04:07 |
16 | +++ configure.in 2004-07-03 20:19:54 | |
c46f8a95 WD |
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 | ||
13bed3dd WD |
26 | --- orig/match.c 2004-07-02 23:31:44 |
27 | +++ match.c 2004-07-03 20:19:54 | |
7b675ff5 | 28 | @@ -23,6 +23,7 @@ extern int verbose; |
a2d23604 WD |
29 | extern int am_server; |
30 | extern int do_progress; | |
7b675ff5 | 31 | extern int checksum_seed; |
a2d23604 WD |
32 | +extern int inplace; |
33 | ||
34 | typedef unsigned short tag; | |
35 | ||
13bed3dd | 36 | @@ -52,9 +53,26 @@ static size_t *tag_table; |
c46f8a95 WD |
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; | |
c46f8a95 WD |
47 | + |
48 | + if (t1->t != t2->t) | |
49 | + return t1->t < t2->t ? -1 : 1; | |
50 | + | |
51 | + s1 = ¤t_sums[t1->i]; | |
52 | + s2 = ¤t_sums[t2->i]; | |
53 | + | |
54 | + if (s1->sum1 != s2->sum1) | |
55 | + return s1->sum1 < s2->sum1 ? -1 : 1; | |
56 | + | |
13bed3dd WD |
57 | + if (s1->len != s2->len) |
58 | + return s1->len < s2->len ? -1 : 1; | |
59 | + | |
ec4215e6 | 60 | + return memcmp(s1->sum2, s2->sum2, current_s2length); |
c46f8a95 WD |
61 | } |
62 | ||
63 | ||
13bed3dd | 64 | @@ -74,6 +92,8 @@ static void build_hash_table(struct sum_ |
c46f8a95 WD |
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++) | |
13bed3dd | 73 | @@ -192,12 +212,24 @@ static void hash_search(int f,struct sum |
c46f8a95 WD |
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; | |
a2d23604 | 81 | continue; |
c46f8a95 | 82 | + } |
a2d23604 | 83 | |
c46f8a95 WD |
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; | |
7b675ff5 | 90 | + continue; |
c46f8a95 | 91 | + } |
7b675ff5 | 92 | + |
c46f8a95 WD |
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 | ||
a2d23604 | 99 | if (verbose > 3) |
13bed3dd | 100 | @@ -215,15 +247,41 @@ static void hash_search(int f,struct sum |
c46f8a95 WD |
101 | continue; |
102 | } | |
103 | ||
13bed3dd WD |
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. */ | |
ec4215e6 | 107 | + if (inplace) { |
13bed3dd | 108 | + do { |
ec4215e6 WD |
109 | + size_t i2 = targets[j].i; |
110 | + if (s->sums[i2].offset != offset) | |
c46f8a95 | 111 | + continue; |
ec4215e6 WD |
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; | |
c46f8a95 | 119 | + } |
ec4215e6 WD |
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; | |
13bed3dd | 125 | + } while (++j < s->count && targets[j].t == t); |
ec4215e6 | 126 | + } |
13bed3dd WD |
127 | + |
128 | /* we've found a match, but now check to see | |
129 | * if last_i can hint at a better match */ | |
ec4215e6 WD |
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); | |
13bed3dd WD |
142 | --- orig/options.c 2004-06-20 19:30:00 |
143 | +++ options.c 2004-07-03 20:19:54 | |
125d7fca | 144 | @@ -94,6 +94,7 @@ int ignore_errors = 0; |
a2d23604 WD |
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 | ||
c46f8a95 WD |
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) | |
a2d23604 WD |
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"); | |
c46f8a95 | 186 | + rprintf(F," --inplace update the destination file inplace (see man page)\n"); |
125d7fca | 187 | rprintf(F," -K, --keep-dirlinks treat symlinked dir on receiver as dir\n"); |
a2d23604 WD |
188 | rprintf(F," -l, --links copy symlinks as symlinks\n"); |
189 | rprintf(F," -L, --copy-links copy the referent of all symlinks\n"); | |
c46f8a95 | 190 | @@ -341,6 +348,7 @@ static struct poptOption long_options[] |
125d7fca WD |
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 }, | |
a2d23604 | 194 | + {"inplace", 0, POPT_ARG_NONE, &inplace, 0, 0, 0 }, |
125d7fca WD |
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, ©_links, 0, 0, 0 }, | |
c46f8a95 | 198 | @@ -739,6 +747,18 @@ int parse_arguments(int *argc, const cha |
78114162 WD |
199 | bwlimit_writemax = 512; |
200 | } | |
a2d23604 | 201 | |
c46f8a95 WD |
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 | + } | |
a2d23604 WD |
213 | + |
214 | if (files_from) { | |
215 | char *colon; | |
216 | if (*argc != 2 && !(am_server && am_sender && *argc == 1)) { | |
c46f8a95 | 217 | @@ -963,6 +983,9 @@ void server_options(char **args,int *arg |
a2d23604 WD |
218 | if (opt_ignore_existing && am_sender) |
219 | args[ac++] = "--ignore-existing"; | |
7b675ff5 | 220 | |
a2d23604 WD |
221 | + if (inplace) |
222 | + args[ac++] = "--inplace"; | |
7b675ff5 | 223 | + |
a2d23604 WD |
224 | if (tmpdir) { |
225 | args[ac++] = "--temp-dir"; | |
7b675ff5 | 226 | args[ac++] = tmpdir; |
13bed3dd WD |
227 | --- orig/receiver.c 2004-07-02 18:23:01 |
228 | +++ receiver.c 2004-07-03 20:19:54 | |
7628f156 | 229 | @@ -48,6 +48,7 @@ extern int ignore_errors; |
a2d23604 | 230 | extern int orig_umask; |
7f2baf27 | 231 | extern int keep_partial; |
7b675ff5 | 232 | extern int checksum_seed; |
a2d23604 WD |
233 | +extern int inplace; |
234 | ||
235 | static void delete_one(char *fn, int is_dir) | |
236 | { | |
c46f8a95 | 237 | @@ -255,16 +256,30 @@ static int receive_data(int f_in,struct |
a2d23604 WD |
238 | sum_update(map,len); |
239 | } | |
240 | ||
c46f8a95 | 241 | - if (fd != -1 && write_file(fd, map, len) != (int)len) { |
fe6407b5 WD |
242 | - rsyserr(FERROR, errno, "write failed on %s", |
243 | - full_fname(fname)); | |
a2d23604 WD |
244 | - exit_cleanup(RERR_FILEIO); |
245 | + if (!inplace || offset != offset2) { | |
c46f8a95 | 246 | + if (fd != -1 && write_file(fd, map, len) != (int)len) { |
fe6407b5 WD |
247 | + rsyserr(FERROR, errno, "write failed on %s", |
248 | + full_fname(fname)); | |
a2d23604 WD |
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 | ||
c46f8a95 | 264 | +#ifdef HAVE_FTRUNCATE |
a2d23604 WD |
265 | + if (inplace) |
266 | + ftruncate(fd, offset); | |
c46f8a95 | 267 | +#endif |
a2d23604 WD |
268 | + |
269 | if (do_progress) | |
270 | end_progress(total_size); | |
271 | ||
c46f8a95 | 272 | @@ -414,44 +429,59 @@ int recv_files(int f_in,struct file_list |
a2d23604 WD |
273 | } else |
274 | mapbuf = NULL; | |
275 | ||
276 | - if (!get_tmpname(fnametmp,fname)) { | |
c46f8a95 WD |
277 | - if (mapbuf) |
278 | - unmap_file(mapbuf); | |
279 | - if (fd1 != -1) | |
280 | - close(fd1); | |
a2d23604 WD |
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) { | |
fe6407b5 | 287 | + rsyserr(FERROR, errno, "open %s failed", |
c46f8a95 | 288 | + full_fname(fnamecmp)); |
a2d23604 | 289 | + receive_data(f_in,mapbuf,-1,NULL,file->length); |
c46f8a95 WD |
290 | + if (mapbuf) |
291 | + unmap_file(mapbuf); | |
292 | + if (fd1 != -1) | |
293 | + close(fd1); | |
a2d23604 WD |
294 | + continue; |
295 | + } | |
296 | + } else { | |
297 | + if (!get_tmpname(fnametmp,fname)) { | |
c46f8a95 WD |
298 | + if (mapbuf) |
299 | + unmap_file(mapbuf); | |
300 | + if (fd1 != -1) | |
301 | + close(fd1); | |
a2d23604 WD |
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) { | |
fe6407b5 WD |
331 | - rsyserr(FERROR, errno, "mkstemp %s failed", |
332 | - full_fname(fnametmp)); | |
a2d23604 | 333 | - receive_data(f_in,mapbuf,-1,NULL,file->length); |
c46f8a95 WD |
334 | - if (mapbuf) |
335 | - unmap_file(mapbuf); | |
336 | - if (fd1 != -1) | |
337 | - close(fd1); | |
a2d23604 | 338 | - continue; |
c46f8a95 WD |
339 | - } |
340 | ||
341 | - cleanup_set(fnametmp, fname, file, mapbuf, fd1, fd2); | |
a2d23604 WD |
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) { | |
fe6407b5 WD |
351 | + rsyserr(FERROR, errno, "mkstemp %s failed", |
352 | + full_fname(fnametmp)); | |
a2d23604 | 353 | + receive_data(f_in,mapbuf,-1,NULL,file->length); |
c46f8a95 WD |
354 | + if (mapbuf) |
355 | + unmap_file(mapbuf); | |
356 | + if (fd1 != -1) | |
357 | + close(fd1); | |
a2d23604 WD |
358 | + continue; |
359 | + } | |
c46f8a95 WD |
360 | + |
361 | + cleanup_set(fnametmp, fname, file, mapbuf, fd1, fd2); | |
362 | + } | |
a2d23604 | 363 | |
c46f8a95 WD |
364 | if (!am_server && verbose) |
365 | rprintf(FINFO, "%s\n", fname); | |
13bed3dd WD |
366 | --- orig/rsync.c 2004-07-02 18:06:32 |
367 | +++ rsync.c 2004-07-03 20:19:54 | |
7b675ff5 WD |
368 | @@ -34,6 +34,7 @@ extern int force_delete; |
369 | extern int recurse; | |
a2d23604 | 370 | extern int make_backups; |
7b675ff5 | 371 | extern char *backup_dir; |
a2d23604 WD |
372 | +extern int inplace; |
373 | ||
374 | ||
375 | /* | |
c46f8a95 | 376 | @@ -239,6 +240,13 @@ void finish_transfer(char *fname, char * |
a2d23604 WD |
377 | if (make_backups && !make_backup(fname)) |
378 | return; | |
7b675ff5 | 379 | |
a2d23604 | 380 | + if (inplace) { |
c46f8a95 WD |
381 | + if (verbose > 2) |
382 | + rprintf(FINFO, "finishing %s\n", fname); | |
383 | + set_perms(fname, file, NULL, 0); | |
a2d23604 WD |
384 | + return; |
385 | + } | |
7b675ff5 | 386 | + |
a2d23604 | 387 | /* move tmp file over real file */ |
c46f8a95 WD |
388 | if (verbose > 2) |
389 | rprintf(FINFO, "renaming %s to %s\n", fnametmp, fname); | |
13bed3dd WD |
390 | --- orig/rsync.yo 2004-06-17 06:32:00 |
391 | +++ rsync.yo 2004-07-03 20:19:55 | |
a2d23604 WD |
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 | |
125d7fca | 397 | -K, --keep-dirlinks treat symlinked dir on receiver as dir |
a2d23604 WD |
398 | -l, --links copy symlinks as symlinks |
399 | -L, --copy-links copy the referent of all symlinks | |
125d7fca WD |
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. | |
7b675ff5 | 403 | |
a2d23604 WD |
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. | |
7b675ff5 | 414 | + |
a2d23604 WD |
415 | dit(bf(-l, --links)) When symlinks are encountered, recreate the |
416 | symlink on the destination. | |
7b675ff5 | 417 |