Ignore "tmp".
[rsync/rsync-patches.git] / inplace.diff
CommitLineData
a2d23604 1Patch from Mark Curtis to implement the --inplace option.
c46f8a95 2Improved by Wayne Davison.
a2d23604 3
c46f8a95
WD
4Run these commands after applying the patch:
5
6 autoconf
7 autoheader
8 ./configure
ec4215e6
WD
9 make
10
11Optional:
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 = &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+
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, &copy_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