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