Updated to apply to latest source.
[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
9be39c35 26--- orig/match.c 2004-07-15 02:21:10
55add4e9 27+++ match.c 2004-07-04 08:03:36
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
55add4e9
WD
36@@ -200,6 +201,12 @@ static void hash_search(int f,struct sum
37 if (l != s->sums[i].len)
a2d23604
WD
38 continue;
39
c46f8a95
WD
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)
55add4e9
WD
44+ continue;
45+
a2d23604 46 if (verbose > 3)
55add4e9
WD
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
c46f8a95
WD
50 continue;
51 }
52
13bed3dd
WD
53+ /* If inplace is enabled, the best possible match is
54+ * one with an identical offset, so we prefer that over
d5416699 55+ * the following want_i optimization. */
ec4215e6 56+ if (inplace) {
13bed3dd 57+ do {
ec4215e6
WD
58+ size_t i2 = targets[j].i;
59+ if (s->sums[i2].offset != offset)
c46f8a95 60+ continue;
ec4215e6
WD
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;
c46f8a95 68+ }
ec4215e6
WD
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;
d5416699 73+ goto set_want_i;
13bed3dd 74+ } while (++j < s->count && targets[j].t == t);
ec4215e6 75+ }
13bed3dd
WD
76+
77 /* we've found a match, but now check to see
d5416699
WD
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) {
ec4215e6
WD
83 /* we've found an adjacent match - the RLL coder
84 * will be happy */
d5416699 85 i = want_i;
ec4215e6 86 }
d5416699
WD
87+ set_want_i:
88 want_i = i + 1;
ec4215e6
WD
89
90 matched(f,s,buf,offset,i);
9be39c35 91--- orig/options.c 2004-07-15 16:51:50
13bed3dd 92+++ options.c 2004-07-03 20:19:54
125d7fca 93@@ -94,6 +94,7 @@ int ignore_errors = 0;
a2d23604
WD
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
9be39c35 101@@ -148,6 +149,7 @@ char *bind_address;
c46f8a95
WD
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 ";
9be39c35 109@@ -157,6 +159,10 @@ static void print_rsync_version(enum log
c46f8a95
WD
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
9be39c35 120@@ -182,8 +188,8 @@ static void print_rsync_version(enum log
c46f8a95
WD
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
9be39c35 131@@ -233,6 +239,7 @@ void usage(enum logcode F)
a2d23604
WD
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");
c46f8a95 135+ rprintf(F," --inplace update the destination file inplace (see man page)\n");
125d7fca 136 rprintf(F," -K, --keep-dirlinks treat symlinked dir on receiver as dir\n");
a2d23604
WD
137 rprintf(F," -l, --links copy symlinks as symlinks\n");
138 rprintf(F," -L, --copy-links copy the referent of all symlinks\n");
9be39c35 139@@ -340,6 +347,7 @@ static struct poptOption long_options[]
125d7fca
WD
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 },
a2d23604 143+ {"inplace", 0, POPT_ARG_NONE, &inplace, 0, 0, 0 },
125d7fca
WD
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 },
9be39c35 147@@ -738,6 +746,18 @@ int parse_arguments(int *argc, const cha
78114162
WD
148 bwlimit_writemax = 512;
149 }
a2d23604 150
c46f8a95
WD
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+ }
a2d23604
WD
162+
163 if (files_from) {
164 char *colon;
165 if (*argc != 2 && !(am_server && am_sender && *argc == 1)) {
9be39c35 166@@ -955,6 +975,9 @@ void server_options(char **args,int *arg
a2d23604
WD
167 if (opt_ignore_existing && am_sender)
168 args[ac++] = "--ignore-existing";
7b675ff5 169
a2d23604
WD
170+ if (inplace)
171+ args[ac++] = "--inplace";
7b675ff5 172+
a2d23604
WD
173 if (tmpdir) {
174 args[ac++] = "--temp-dir";
7b675ff5 175 args[ac++] = tmpdir;
9be39c35 176--- orig/receiver.c 2004-07-14 17:12:06
13bed3dd 177+++ receiver.c 2004-07-03 20:19:54
7628f156 178@@ -48,6 +48,7 @@ extern int ignore_errors;
a2d23604 179 extern int orig_umask;
7f2baf27 180 extern int keep_partial;
7b675ff5 181 extern int checksum_seed;
a2d23604
WD
182+extern int inplace;
183
184 static void delete_one(char *fn, int is_dir)
185 {
c46f8a95 186@@ -255,16 +256,30 @@ static int receive_data(int f_in,struct
a2d23604
WD
187 sum_update(map,len);
188 }
189
c46f8a95 190- if (fd != -1 && write_file(fd, map, len) != (int)len) {
fe6407b5
WD
191- rsyserr(FERROR, errno, "write failed on %s",
192- full_fname(fname));
a2d23604
WD
193- exit_cleanup(RERR_FILEIO);
194+ if (!inplace || offset != offset2) {
c46f8a95 195+ if (fd != -1 && write_file(fd, map, len) != (int)len) {
fe6407b5
WD
196+ rsyserr(FERROR, errno, "write failed on %s",
197+ full_fname(fname));
a2d23604
WD
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
c46f8a95 213+#ifdef HAVE_FTRUNCATE
a2d23604
WD
214+ if (inplace)
215+ ftruncate(fd, offset);
c46f8a95 216+#endif
a2d23604
WD
217+
218 if (do_progress)
219 end_progress(total_size);
220
9be39c35 221@@ -414,44 +429,59 @@ int recv_files(int f_in, struct file_lis
a2d23604
WD
222 } else
223 mapbuf = NULL;
224
225- if (!get_tmpname(fnametmp,fname)) {
c46f8a95
WD
226- if (mapbuf)
227- unmap_file(mapbuf);
228- if (fd1 != -1)
229- close(fd1);
a2d23604
WD
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) {
fe6407b5 236+ rsyserr(FERROR, errno, "open %s failed",
c46f8a95 237+ full_fname(fnamecmp));
a2d23604 238+ receive_data(f_in,mapbuf,-1,NULL,file->length);
c46f8a95
WD
239+ if (mapbuf)
240+ unmap_file(mapbuf);
241+ if (fd1 != -1)
242+ close(fd1);
a2d23604
WD
243+ continue;
244+ }
245+ } else {
246+ if (!get_tmpname(fnametmp,fname)) {
c46f8a95
WD
247+ if (mapbuf)
248+ unmap_file(mapbuf);
249+ if (fd1 != -1)
250+ close(fd1);
a2d23604
WD
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) {
fe6407b5
WD
280- rsyserr(FERROR, errno, "mkstemp %s failed",
281- full_fname(fnametmp));
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 287- continue;
c46f8a95
WD
288- }
289
290- cleanup_set(fnametmp, fname, file, mapbuf, fd1, fd2);
a2d23604
WD
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) {
fe6407b5
WD
300+ rsyserr(FERROR, errno, "mkstemp %s failed",
301+ full_fname(fnametmp));
a2d23604 302+ receive_data(f_in,mapbuf,-1,NULL,file->length);
c46f8a95
WD
303+ if (mapbuf)
304+ unmap_file(mapbuf);
305+ if (fd1 != -1)
306+ close(fd1);
a2d23604
WD
307+ continue;
308+ }
c46f8a95
WD
309+
310+ cleanup_set(fnametmp, fname, file, mapbuf, fd1, fd2);
311+ }
a2d23604 312
c46f8a95
WD
313 if (!am_server && verbose)
314 rprintf(FINFO, "%s\n", fname);
13bed3dd
WD
315--- orig/rsync.c 2004-07-02 18:06:32
316+++ rsync.c 2004-07-03 20:19:54
7b675ff5
WD
317@@ -34,6 +34,7 @@ extern int force_delete;
318 extern int recurse;
a2d23604 319 extern int make_backups;
7b675ff5 320 extern char *backup_dir;
a2d23604
WD
321+extern int inplace;
322
323
324 /*
c46f8a95 325@@ -239,6 +240,13 @@ void finish_transfer(char *fname, char *
a2d23604
WD
326 if (make_backups && !make_backup(fname))
327 return;
7b675ff5 328
a2d23604 329+ if (inplace) {
c46f8a95
WD
330+ if (verbose > 2)
331+ rprintf(FINFO, "finishing %s\n", fname);
332+ set_perms(fname, file, NULL, 0);
a2d23604
WD
333+ return;
334+ }
7b675ff5 335+
a2d23604 336 /* move tmp file over real file */
c46f8a95
WD
337 if (verbose > 2)
338 rprintf(FINFO, "renaming %s to %s\n", fnametmp, fname);
9be39c35 339--- orig/rsync.yo 2004-07-15 02:21:11
13bed3dd 340+++ rsync.yo 2004-07-03 20:19:55
a2d23604
WD
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
125d7fca 346 -K, --keep-dirlinks treat symlinked dir on receiver as dir
a2d23604
WD
347 -l, --links copy symlinks as symlinks
348 -L, --copy-links copy the referent of all symlinks
125d7fca
WD
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.
7b675ff5 352
a2d23604
WD
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.
7b675ff5 363+
a2d23604
WD
364 dit(bf(-l, --links)) When symlinks are encountered, recreate the
365 symlink on the destination.
7b675ff5 366