Logic error in preserve_perms + link_dest
[rsync/rsync.git] / generator.c
... / ...
CommitLineData
1/* -*- c-file-style: "linux" -*-
2
3 rsync -- fast file replication program
4
5 Copyright (C) 1996-2000 by Andrew Tridgell
6 Copyright (C) Paul Mackerras 1996
7 Copyright (C) 2002 by Martin Pool <mbp@samba.org>
8
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.
13
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.
18
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.
22*/
23
24#include "rsync.h"
25
26extern int verbose;
27extern int dry_run;
28extern int relative_paths;
29extern int preserve_links;
30extern int am_root;
31extern int preserve_devices;
32extern int preserve_hard_links;
33extern int update_only;
34extern int opt_ignore_existing;
35extern int block_size;
36extern int csum_length;
37extern int ignore_times;
38extern int size_only;
39extern int io_timeout;
40extern int remote_version;
41extern int always_checksum;
42extern int modify_window;
43extern char *compare_dest;
44extern int link_dest;
45
46
47/* choose whether to skip a particular file */
48static int skip_file(char *fname,
49 struct file_struct *file, STRUCT_STAT *st)
50{
51 if (st->st_size != file->length) {
52 return 0;
53 }
54 if (link_dest) {
55 extern int preserve_perms;
56 extern int preserve_uid;
57 extern int preserve_gid;
58
59 if(preserve_perms
60 && (st->st_mode & ~_S_IFMT) != (file->mode & ~_S_IFMT))
61 return 0;
62
63 if (preserve_uid && st->st_uid != file->uid)
64 return 0;
65
66 if (preserve_gid && st->st_gid != file->gid)
67 return 0;
68 }
69
70 /* if always checksum is set then we use the checksum instead
71 of the file time to determine whether to sync */
72 if (always_checksum && S_ISREG(st->st_mode)) {
73 char sum[MD4_SUM_LENGTH];
74 char fnamecmpdest[MAXPATHLEN];
75
76 if (compare_dest != NULL) {
77 if (access(fname, 0) != 0) {
78 snprintf(fnamecmpdest,MAXPATHLEN,"%s/%s",
79 compare_dest,fname);
80 fname = fnamecmpdest;
81 }
82 }
83 file_checksum(fname,sum,st->st_size);
84 if (remote_version < 21) {
85 return (memcmp(sum,file->sum,2) == 0);
86 } else {
87 return (memcmp(sum,file->sum,MD4_SUM_LENGTH) == 0);
88 }
89 }
90
91 if (size_only) {
92 return 1;
93 }
94
95 if (ignore_times) {
96 return 0;
97 }
98
99 return (cmp_modtime(st->st_mtime,file->modtime) == 0);
100}
101
102
103/* use a larger block size for really big files */
104static int adapt_block_size(struct file_struct *file, int bsize)
105{
106 int ret;
107
108 if (bsize != BLOCK_SIZE) return bsize;
109
110 ret = file->length / (10000); /* rough heuristic */
111 ret = ret & ~15; /* multiple of 16 */
112 if (ret < bsize) ret = bsize;
113 if (ret > CHUNK_SIZE/2) ret = CHUNK_SIZE/2;
114 return ret;
115}
116
117
118/*
119 send a header that says "we have no checksums" down the f_out fd
120 */
121static void send_null_sums(int f_out)
122{
123 write_int(f_out, 0);
124 write_int(f_out, block_size);
125 write_int(f_out, 0);
126}
127
128
129
130/**
131 * Perhaps we want to just send an empty checksum set for this file,
132 * which will force the whole thing to be literally transferred.
133 *
134 * When do we do this? If the user's explicitly said they
135 * want the whole thing, or if { they haven't explicitly
136 * requested a delta, and it's local but not batch mode.}
137 *
138 * Whew. */
139static BOOL disable_deltas_p(void)
140{
141 extern int whole_file;
142 extern int local_server;
143 extern int write_batch;
144
145 if (whole_file > 0)
146 return True;
147 if (whole_file == 0 || write_batch)
148 return False;
149 return local_server;
150}
151
152
153/*
154 * Generate and send a stream of signatures/checksums that describe a buffer
155 *
156 * Generate approximately one checksum every block_len bytes.
157 */
158static void generate_and_send_sums(struct map_struct *buf, OFF_T len,
159 int block_len, int f_out)
160{
161 size_t i;
162 struct sum_struct sum;
163 OFF_T offset = 0;
164
165 sum.count = (len + (block_len - 1)) / block_len;
166 sum.remainder = (len % block_len);
167 sum.n = block_len;
168 sum.flength = len;
169 /* not needed here sum.sums = NULL; */
170
171 if (sum.count && verbose > 3) {
172 rprintf(FINFO, "count=%ld rem=%ld n=%ld flength=%.0f\n",
173 (long) sum.count, (long) sum.remainder,
174 (long) sum.n, (double) sum.flength);
175 }
176
177 write_int(f_out, sum.count);
178 write_int(f_out, sum.n);
179 write_int(f_out, sum.remainder);
180
181 for (i = 0; i < sum.count; i++) {
182 int n1 = MIN(len, block_len);
183 char *map = map_ptr(buf, offset, n1);
184 uint32 sum1 = get_checksum1(map, n1);
185 char sum2[SUM_LENGTH];
186
187 get_checksum2(map, n1, sum2);
188
189 if (verbose > 3) {
190 rprintf(FINFO,
191 "chunk[%d] offset=%.0f len=%d sum1=%08lx\n",
192 i, (double) offset, n1, (unsigned long) sum1);
193 }
194 write_int(f_out, sum1);
195 write_buf(f_out, sum2, csum_length);
196 len -= n1;
197 offset += n1;
198 }
199}
200
201
202
203/**
204 * Acts on file number @p i from @p flist, whose name is @p fname.
205 *
206 * First fixes up permissions, then generates checksums for the file.
207 *
208 * @note This comment was added later by mbp who was trying to work it
209 * out. It might be wrong.
210 **/
211void recv_generator(char *fname, struct file_list *flist, int i, int f_out)
212{
213 int fd;
214 STRUCT_STAT st;
215 struct map_struct *buf;
216 int statret;
217 struct file_struct *file = flist->files[i];
218 char *fnamecmp;
219 char fnamecmpbuf[MAXPATHLEN];
220 extern char *compare_dest;
221 extern int list_only;
222 extern int preserve_perms;
223 extern int only_existing;
224 extern int orig_umask;
225
226 if (list_only) return;
227
228 if (verbose > 2)
229 rprintf(FINFO,"recv_generator(%s,%d)\n",fname,i);
230
231 statret = link_stat(fname,&st);
232
233 if (only_existing && statret == -1 && errno == ENOENT) {
234 /* we only want to update existing files */
235 if (verbose > 1) rprintf(FINFO, "not creating new file \"%s\"\n",fname);
236 return;
237 }
238
239 if (statret == 0 &&
240 !preserve_perms &&
241 (S_ISDIR(st.st_mode) == S_ISDIR(file->mode))) {
242 /* if the file exists already and we aren't perserving
243 * permissions then act as though the remote end sent
244 * us the file permissions we already have */
245 file->mode = (file->mode & _S_IFMT) | (st.st_mode & ~_S_IFMT);
246 }
247
248 if (S_ISDIR(file->mode)) {
249 /* The file to be received is a directory, so we need
250 * to prepare appropriately. If there is already a
251 * file of that name and it is *not* a directory, then
252 * we need to delete it. If it doesn't exist, then
253 * recursively create it. */
254
255 if (dry_run) return; /* XXXX -- might cause inaccuracies?? -- mbp */
256 if (statret == 0 && !S_ISDIR(st.st_mode)) {
257 if (robust_unlink(fname) != 0) {
258 rprintf(FERROR, RSYNC_NAME
259 ": recv_generator: unlink \"%s\" to make room for directory: %s\n",
260 fname,strerror(errno));
261 return;
262 }
263 statret = -1;
264 }
265 if (statret != 0 && do_mkdir(fname,file->mode) != 0 && errno != EEXIST) {
266 if (!(relative_paths && errno==ENOENT &&
267 create_directory_path(fname, orig_umask)==0 &&
268 do_mkdir(fname,file->mode)==0)) {
269 rprintf(FERROR, RSYNC_NAME ": recv_generator: mkdir \"%s\": %s (2)\n",
270 fname,strerror(errno));
271 }
272 }
273 /* f_out is set to -1 when doing final directory
274 permission and modification time repair */
275 if (set_perms(fname,file,NULL,0) && verbose && (f_out != -1))
276 rprintf(FINFO,"%s/\n",fname);
277 return;
278 }
279
280 if (preserve_links && S_ISLNK(file->mode)) {
281#if SUPPORT_LINKS
282 char lnk[MAXPATHLEN];
283 int l;
284 extern int safe_symlinks;
285
286 if (safe_symlinks && unsafe_symlink(file->link, fname)) {
287 if (verbose) {
288 rprintf(FINFO,"ignoring unsafe symlink \"%s\" -> \"%s\"\n",
289 fname,file->link);
290 }
291 return;
292 }
293 if (statret == 0) {
294 l = readlink(fname,lnk,MAXPATHLEN-1);
295 if (l > 0) {
296 lnk[l] = 0;
297 /* A link already pointing to the
298 * right place -- no further action
299 * required. */
300 if (strcmp(lnk,file->link) == 0) {
301 set_perms(fname,file,&st,1);
302 return;
303 }
304 }
305 /* Not a symlink, so delete whatever's
306 * already there and put a new symlink
307 * in place. */
308 delete_file(fname);
309 }
310 if (do_symlink(file->link,fname) != 0) {
311 rprintf(FERROR,RSYNC_NAME": symlink \"%s\" -> \"%s\": %s\n",
312 fname,file->link,strerror(errno));
313 } else {
314 set_perms(fname,file,NULL,0);
315 if (verbose) {
316 rprintf(FINFO,"%s -> %s\n", fname,file->link);
317 }
318 }
319#endif
320 return;
321 }
322
323#ifdef HAVE_MKNOD
324 if (am_root && preserve_devices && IS_DEVICE(file->mode)) {
325 if (statret != 0 ||
326 st.st_mode != file->mode ||
327 st.st_rdev != file->rdev) {
328 delete_file(fname);
329 if (verbose > 2)
330 rprintf(FINFO,"mknod(%s,0%o,0x%x)\n",
331 fname,(int)file->mode,(int)file->rdev);
332 if (do_mknod(fname,file->mode,file->rdev) != 0) {
333 rprintf(FERROR,"mknod %s : %s\n",fname,strerror(errno));
334 } else {
335 set_perms(fname,file,NULL,0);
336 if (verbose)
337 rprintf(FINFO,"%s\n",fname);
338 }
339 } else {
340 set_perms(fname,file,&st,1);
341 }
342 return;
343 }
344#endif
345
346 if (preserve_hard_links && check_hard_link(file)) {
347 if (verbose > 1)
348 rprintf(FINFO, "recv_generator: \"%s\" is a hard link\n",f_name(file));
349 return;
350 }
351
352 if (!S_ISREG(file->mode)) {
353 rprintf(FINFO, "skipping non-regular file \"%s\"\n",fname);
354 return;
355 }
356
357 fnamecmp = fname;
358
359 if ((statret == -1) && (compare_dest != NULL)) {
360 /* try the file at compare_dest instead */
361 int saveerrno = errno;
362 snprintf(fnamecmpbuf,MAXPATHLEN,"%s/%s",compare_dest,fname);
363 statret = link_stat(fnamecmpbuf,&st);
364 if (!S_ISREG(st.st_mode))
365 statret = -1;
366 if (statret == -1)
367 errno = saveerrno;
368#if HAVE_LINK
369 else if (link_dest && !dry_run) {
370 if (do_link(fnamecmpbuf, fname) != 0) {
371 if (verbose > 0)
372 rprintf(FINFO,"link %s => %s : %s\n",
373 fnamecmpbuf,
374 fname,
375 strerror(errno));
376 }
377 fnamecmp = fnamecmpbuf;
378 }
379#endif
380 else
381 fnamecmp = fnamecmpbuf;
382 }
383
384 if (statret == -1) {
385 if (errno == ENOENT) {
386 write_int(f_out,i);
387 if (!dry_run) send_null_sums(f_out);
388 } else {
389 if (verbose > 1)
390 rprintf(FERROR, RSYNC_NAME
391 ": recv_generator failed to open \"%s\": %s\n",
392 fname, strerror(errno));
393 }
394 return;
395 }
396
397 if (!S_ISREG(st.st_mode)) {
398 if (delete_file(fname) != 0) {
399 return;
400 }
401
402 /* now pretend the file didn't exist */
403 write_int(f_out,i);
404 if (!dry_run) send_null_sums(f_out);
405 return;
406 }
407
408 if (opt_ignore_existing && fnamecmp == fname) {
409 if (verbose > 1)
410 rprintf(FINFO,"%s exists\n",fname);
411 return;
412 }
413
414 if (update_only && cmp_modtime(st.st_mtime,file->modtime)>0 && fnamecmp == fname) {
415 if (verbose > 1)
416 rprintf(FINFO,"%s is newer\n",fname);
417 return;
418 }
419
420 if (skip_file(fname, file, &st)) {
421 if (fnamecmp == fname)
422 set_perms(fname,file,&st,1);
423 return;
424 }
425
426 if (dry_run) {
427 write_int(f_out,i);
428 return;
429 }
430
431 if (disable_deltas_p()) {
432 write_int(f_out,i);
433 send_null_sums(f_out);
434 return;
435 }
436
437 /* open the file */
438 fd = do_open(fnamecmp, O_RDONLY, 0);
439
440 if (fd == -1) {
441 rprintf(FERROR,RSYNC_NAME": failed to open \"%s\", continuing : %s\n",fnamecmp,strerror(errno));
442 /* pretend the file didn't exist */
443 write_int(f_out,i);
444 send_null_sums(f_out);
445 return;
446 }
447
448 if (st.st_size > 0) {
449 buf = map_file(fd,st.st_size);
450 } else {
451 buf = NULL;
452 }
453
454 if (verbose > 3)
455 rprintf(FINFO,"gen mapped %s of size %.0f\n",fnamecmp,(double)st.st_size);
456
457 if (verbose > 2)
458 rprintf(FINFO, "generating and sending sums for %d\n", i);
459
460 write_int(f_out,i);
461 generate_and_send_sums(buf, st.st_size,
462 adapt_block_size(file, block_size), f_out);
463
464 close(fd);
465 if (buf) unmap_file(buf);
466}
467
468
469
470void generate_files(int f,struct file_list *flist,char *local_name,int f_recv)
471{
472 int i;
473 int phase=0;
474
475 if (verbose > 2)
476 rprintf(FINFO,"generator starting pid=%d count=%d\n",
477 (int)getpid(),flist->count);
478
479 if (verbose >= 2) {
480 rprintf(FINFO,
481 disable_deltas_p()
482 ? "delta-transmission disabled for local transfer or --whole-file\n"
483 : "delta transmission enabled\n");
484 }
485
486 /* we expect to just sit around now, so don't exit on a
487 timeout. If we really get a timeout then the other process should
488 exit */
489 io_timeout = 0;
490
491 for (i = 0; i < flist->count; i++) {
492 struct file_struct *file = flist->files[i];
493 mode_t saved_mode = file->mode;
494 if (!file->basename) continue;
495
496 /* we need to ensure that any directories we create have writeable
497 permissions initially so that we can create the files within
498 them. This is then fixed after the files are transferred */
499 if (!am_root && S_ISDIR(file->mode)) {
500 file->mode |= S_IWUSR; /* user write */
501 /* XXX: Could this be causing a problem on SCO? Perhaps their
502 * handling of permissions is strange? */
503 }
504
505 recv_generator(local_name?local_name:f_name(file), flist,i,f);
506
507 file->mode = saved_mode;
508 }
509
510 phase++;
511 csum_length = SUM_LENGTH;
512 ignore_times=1;
513
514 if (verbose > 2)
515 rprintf(FINFO,"generate_files phase=%d\n",phase);
516
517 write_int(f,-1);
518
519 if (remote_version >= 13) {
520 /* in newer versions of the protocol the files can cycle through
521 the system more than once to catch initial checksum errors */
522 for (i=read_int(f_recv); i != -1; i=read_int(f_recv)) {
523 struct file_struct *file = flist->files[i];
524 recv_generator(local_name?local_name:f_name(file),
525 flist,i,f);
526 }
527
528 phase++;
529 if (verbose > 2)
530 rprintf(FINFO,"generate_files phase=%d\n",phase);
531
532 write_int(f,-1);
533 }
534}