1 /* -*- c-file-style: "linux" -*-
3 rsync -- fast file replication program
5 Copyright (C) 1996-2000 by Andrew Tridgell
6 Copyright (C) Paul Mackerras 1996
7 Copyright (C) 2002 by Martin Pool <mbp@samba.org>
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 extern int relative_paths;
29 extern int preserve_links;
31 extern int preserve_devices;
32 extern int preserve_hard_links;
33 extern int preserve_perms;
34 extern int preserve_uid;
35 extern int preserve_gid;
36 extern int update_only;
37 extern int opt_ignore_existing;
38 extern int csum_length;
39 extern int ignore_times;
41 extern int io_timeout;
42 extern int protocol_version;
43 extern int always_checksum;
44 extern char *compare_dest;
48 /* choose whether to skip a particular file */
49 static int skip_file(char *fname, struct file_struct *file, STRUCT_STAT *st)
51 if (st->st_size != file->length) {
56 && (st->st_mode & ~_S_IFMT) != (file->mode & ~_S_IFMT))
59 if (am_root && preserve_uid && st->st_uid != file->uid)
62 if (preserve_gid && file->gid != GID_NONE
63 && st->st_gid != file->gid)
67 /* if always checksum is set then we use the checksum instead
68 of the file time to determine whether to sync */
69 if (always_checksum && S_ISREG(st->st_mode)) {
70 char sum[MD4_SUM_LENGTH];
71 char fnamecmpdest[MAXPATHLEN];
73 if (compare_dest != NULL) {
74 if (access(fname, 0) != 0) {
75 snprintf(fnamecmpdest,MAXPATHLEN,"%s/%s",
80 file_checksum(fname,sum,st->st_size);
81 if (protocol_version < 21) {
82 return (memcmp(sum,file->sum,2) == 0);
84 return (memcmp(sum,file->sum,MD4_SUM_LENGTH) == 0);
96 return (cmp_modtime(st->st_mtime,file->modtime) == 0);
101 * NULL sum_struct means we have no checksums
103 void write_sum_head(int f, struct sum_struct *sum)
105 static struct sum_struct null_sum;
107 if (sum == (struct sum_struct *)NULL)
110 write_int(f, sum->count);
111 write_int(f, sum->blength);
112 if (protocol_version >= 27)
113 write_int(f, sum->s2length);
114 write_int(f, sum->remainder);
118 * set (initialize) the size entries in the per-file sum_struct
119 * calulating dynamic block ans checksum sizes.
121 * This is only called from generate_and_send_sums() but is a seperate
122 * function to encapsulate the logic.
124 * The block size is a rounded square root of file length.
126 * The checksum size is determined according to:
127 * blocksum_bits = BLOCKSUM_EXP + 2*log2(file_len) - log2(block_len)
128 * provided by Donovan Baarda which gives a probability of rsync
129 * algorithm corrupting data and falling back using the whole md4
132 * This might be made one of several selectable heuristics.
135 static void sum_sizes_sqroot(struct sum_struct *sum, uint64 len)
137 extern unsigned int block_size;
138 unsigned int blength;
144 blength = block_size;
145 } else if (len <= BLOCK_SIZE * BLOCK_SIZE) {
146 blength = BLOCK_SIZE;
156 if (len < (uint64)blength * blength)
159 } while (c >= 8); /* round to multiple of 8 */
160 blength = MAX(blength, BLOCK_SIZE);
163 if (protocol_version < 27) {
164 s2length = csum_length;
165 } else if (csum_length == SUM_LENGTH) {
166 s2length = SUM_LENGTH;
168 int b = BLOCKSUM_BIAS;
174 while (c >>= 1 && b) {
177 s2length = (b + 1 - 32 + 7) / 8; /* add a bit,
180 * --optimize in compiler--
182 s2length = MAX(s2length, csum_length);
183 s2length = MIN(s2length, SUM_LENGTH);
187 sum->blength = blength;
188 sum->s2length = s2length;
189 sum->count = (len + (blength - 1)) / blength;
190 sum->remainder = (len % blength);
192 if (sum->count && verbose > 2) {
193 rprintf(FINFO, "count=%.0f rem=%u blength=%u s2length=%d flength=%.0f\n",
194 (double)sum->count, sum->remainder, sum->blength,
195 sum->s2length, (double)sum->flength);
200 * Perhaps we want to just send an empty checksum set for this file,
201 * which will force the whole thing to be literally transferred.
203 * When do we do this? If the user's explicitly said they
204 * want the whole thing, or if { they haven't explicitly
205 * requested a delta, and it's local but not batch mode.}
208 static BOOL disable_deltas_p(void)
210 extern int whole_file;
211 extern int local_server;
212 extern int write_batch;
216 if (whole_file == 0 || write_batch)
223 * Generate and send a stream of signatures/checksums that describe a buffer
225 * Generate approximately one checksum every block_len bytes.
227 static void generate_and_send_sums(struct map_struct *buf, size_t len, int f_out)
230 struct sum_struct sum;
233 sum_sizes_sqroot(&sum, len);
235 write_sum_head(f_out, &sum);
237 for (i = 0; i < sum.count; i++) {
238 unsigned int n1 = MIN(len, sum.blength);
239 char *map = map_ptr(buf, offset, n1);
240 uint32 sum1 = get_checksum1(map, n1);
241 char sum2[SUM_LENGTH];
243 get_checksum2(map, n1, sum2);
247 "chunk[%.0f] offset=%.0f len=%u sum1=%08lx\n",
248 (double)i, (double)offset, n1,
249 (unsigned long)sum1);
251 write_int(f_out, sum1);
252 write_buf(f_out, sum2, sum.s2length);
261 * Acts on file number @p i from @p flist, whose name is @p fname.
263 * First fixes up permissions, then generates checksums for the file.
265 * @note This comment was added later by mbp who was trying to work it
266 * out. It might be wrong.
268 void recv_generator(char *fname, struct file_struct *file, int i, int f_out)
272 struct map_struct *mapbuf;
275 char fnamecmpbuf[MAXPATHLEN];
276 extern char *compare_dest;
277 extern int list_only;
278 extern int only_existing;
279 extern int orig_umask;
285 rprintf(FINFO,"recv_generator(%s,%d)\n",fname,i);
287 statret = link_stat(fname,&st);
289 if (only_existing && statret == -1 && errno == ENOENT) {
290 /* we only want to update existing files */
291 if (verbose > 1) rprintf(FINFO, "not creating new file \"%s\"\n",fname);
297 (S_ISDIR(st.st_mode) == S_ISDIR(file->mode))) {
298 /* if the file exists already and we aren't perserving
299 * permissions then act as though the remote end sent
300 * us the file permissions we already have */
301 file->mode = (file->mode & _S_IFMT) | (st.st_mode & ~_S_IFMT);
304 if (S_ISDIR(file->mode)) {
305 /* The file to be received is a directory, so we need
306 * to prepare appropriately. If there is already a
307 * file of that name and it is *not* a directory, then
308 * we need to delete it. If it doesn't exist, then
309 * recursively create it. */
311 if (dry_run) return; /* XXXX -- might cause inaccuracies?? -- mbp */
312 if (statret == 0 && !S_ISDIR(st.st_mode)) {
313 if (robust_unlink(fname) != 0) {
315 "recv_generator: unlink %s to make room for directory: %s\n",
316 full_fname(fname), strerror(errno));
321 if (statret != 0 && do_mkdir(fname,file->mode) != 0 && errno != EEXIST) {
322 if (!(relative_paths && errno==ENOENT &&
323 create_directory_path(fname, orig_umask)==0 &&
324 do_mkdir(fname,file->mode)==0)) {
325 rprintf(FERROR, "recv_generator: mkdir %s failed: %s\n",
326 full_fname(fname), strerror(errno));
329 /* f_out is set to -1 when doing final directory
330 permission and modification time repair */
331 if (set_perms(fname,file,NULL,0) && verbose && (f_out != -1))
332 rprintf(FINFO,"%s/\n",fname);
336 if (preserve_links && S_ISLNK(file->mode)) {
338 char lnk[MAXPATHLEN];
340 extern int safe_symlinks;
342 if (safe_symlinks && unsafe_symlink(file->link, fname)) {
344 rprintf(FINFO, "ignoring unsafe symlink %s -> \"%s\"\n",
345 full_fname(fname), file->link);
350 l = readlink(fname,lnk,MAXPATHLEN-1);
353 /* A link already pointing to the
354 * right place -- no further action
356 if (strcmp(lnk,file->link) == 0) {
357 set_perms(fname,file,&st,1);
361 /* Not a symlink, so delete whatever's
362 * already there and put a new symlink
366 if (do_symlink(file->link,fname) != 0) {
367 rprintf(FERROR, "symlink %s -> \"%s\" failed: %s\n",
368 full_fname(fname), file->link, strerror(errno));
370 set_perms(fname,file,NULL,0);
372 rprintf(FINFO,"%s -> %s\n", fname,file->link);
380 if (am_root && preserve_devices && IS_DEVICE(file->mode)) {
382 st.st_mode != file->mode ||
383 (DEV64_T)st.st_rdev != file->rdev) {
386 rprintf(FINFO,"mknod(%s,0%o,0x%x)\n",
387 fname,(int)file->mode,(int)file->rdev);
388 if (do_mknod(fname,file->mode,file->rdev) != 0) {
389 rprintf(FERROR, "mknod %s failed: %s\n",
390 full_fname(fname), strerror(errno));
392 set_perms(fname,file,NULL,0);
394 rprintf(FINFO,"%s\n",fname);
397 set_perms(fname,file,&st,1);
403 if (preserve_hard_links && check_hard_link(file)) {
405 rprintf(FINFO, "recv_generator: \"%s\" is a hard link\n",
411 if (!S_ISREG(file->mode)) {
412 rprintf(FINFO, "skipping non-regular file \"%s\"\n",fname);
418 if ((statret == -1) && (compare_dest != NULL)) {
419 /* try the file at compare_dest instead */
420 int saveerrno = errno;
421 snprintf(fnamecmpbuf,MAXPATHLEN,"%s/%s",compare_dest,fname);
422 statret = link_stat(fnamecmpbuf,&st);
423 if (!S_ISREG(st.st_mode))
428 else if (link_dest && !dry_run) {
429 if (do_link(fnamecmpbuf, fname) != 0) {
431 rprintf(FINFO,"link %s => %s : %s\n",
436 fnamecmp = fnamecmpbuf;
440 fnamecmp = fnamecmpbuf;
444 if (errno == ENOENT) {
446 if (!dry_run) write_sum_head(f_out, NULL);
447 } else if (verbose > 1) {
449 "recv_generator: failed to open %s: %s\n",
450 full_fname(fname), strerror(errno));
455 if (!S_ISREG(st.st_mode)) {
456 if (delete_file(fname) != 0) {
460 /* now pretend the file didn't exist */
462 if (!dry_run) write_sum_head(f_out, NULL);
466 if (opt_ignore_existing && fnamecmp == fname) {
468 rprintf(FINFO,"%s exists\n",fname);
472 if (update_only && cmp_modtime(st.st_mtime,file->modtime)>0 && fnamecmp == fname) {
474 rprintf(FINFO,"%s is newer\n",fname);
478 if (skip_file(fname, file, &st)) {
479 if (fnamecmp == fname)
480 set_perms(fname,file,&st,1);
489 if (disable_deltas_p()) {
491 write_sum_head(f_out, NULL);
496 fd = do_open(fnamecmp, O_RDONLY, 0);
499 rprintf(FERROR, "failed to open %s, continuing: %s\n",
500 full_fname(fnamecmp), strerror(errno));
501 /* pretend the file didn't exist */
503 write_sum_head(f_out, NULL);
508 mapbuf = map_file(fd,st.st_size);
513 rprintf(FINFO,"gen mapped %s of size %.0f\n", fnamecmp,
518 rprintf(FINFO, "generating and sending sums for %d\n", i);
521 generate_and_send_sums(mapbuf, st.st_size, f_out);
524 if (mapbuf) unmap_file(mapbuf);
529 void generate_files(int f,struct file_list *flist,char *local_name,int f_recv)
533 char fbuf[MAXPATHLEN];
536 rprintf(FINFO,"generator starting pid=%d count=%d\n",
537 (int)getpid(),flist->count);
542 ? "delta-transmission disabled for local transfer or --whole-file\n"
543 : "delta transmission enabled\n");
546 /* we expect to just sit around now, so don't exit on a
547 timeout. If we really get a timeout then the other process should
551 for (i = 0; i < flist->count; i++) {
552 struct file_struct *file = flist->files[i];
553 struct file_struct copy;
557 /* we need to ensure that any directories we create have writeable
558 permissions initially so that we can create the files within
559 them. This is then fixed after the files are transferred */
560 if (!am_root && S_ISDIR(file->mode) && !(file->mode & S_IWUSR)) {
562 /* XXX: Could this be causing a problem on SCO? Perhaps their
563 * handling of permissions is strange? */
564 copy.mode |= S_IWUSR; /* user write */
568 recv_generator(local_name? local_name
569 : f_name_to(file,fbuf,sizeof fbuf), file, i, f);
573 csum_length = SUM_LENGTH;
577 rprintf(FINFO,"generate_files phase=%d\n",phase);
581 /* files can cycle through the system more than once
582 * to catch initial checksum errors */
583 for (i = read_int(f_recv); i != -1; i = read_int(f_recv)) {
584 struct file_struct *file = flist->files[i];
585 recv_generator(local_name? local_name
586 : f_name_to(file,fbuf,sizeof fbuf), file, i, f);
591 rprintf(FINFO,"generate_files phase=%d\n",phase);