X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=generator.c;h=0c3060458deba2d6ba9aeec9ac97c5d4ac2dbe88;hb=83b94efa6b60a3ff5eee4c5f7812c617a90a03f6;hp=125b3132fcfbf694f8383e922dcffb0dc743bf8f;hpb=c9b62cf375f393de076d0fc7a3c2748d581c54a2;p=rsync.git diff --git a/generator.c b/generator.c index 125b3132..0c306045 100644 --- a/generator.c +++ b/generator.c @@ -4,7 +4,7 @@ * Copyright (C) 1996-2000 Andrew Tridgell * Copyright (C) 1996 Paul Mackerras * Copyright (C) 2002 Martin Pool - * Copyright (C) 2003-2008 Wayne Davison + * Copyright (C) 2003-2009 Wayne Davison * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -44,8 +44,6 @@ extern int preserve_hard_links; extern int preserve_executability; extern int preserve_perms; extern int preserve_times; -extern int uid_ndx; -extern int gid_ndx; extern int delete_mode; extern int delete_before; extern int delete_during; @@ -76,7 +74,7 @@ extern int fuzzy_basis; extern int always_checksum; extern int checksum_len; extern char *partial_dir; -extern char *basis_dir[]; +extern char *basis_dir[MAX_BASIS_DIRS+1]; extern int compare_dest; extern int copy_dest; extern int link_dest; @@ -97,7 +95,7 @@ extern char *backup_dir; extern char *backup_suffix; extern int backup_suffix_len; extern struct file_list *cur_flist, *first_flist, *dir_flist; -extern struct filter_list_struct server_filter_list; +extern struct filter_list_struct daemon_filter_list; int ignore_perishable = 0; int non_perishable_cnt = 0; @@ -108,7 +106,7 @@ static int deletion_count = 0; /* used to implement --max-delete */ static int deldelay_size = 0, deldelay_cnt = 0; static char *deldelay_buf = NULL; static int deldelay_fd = -1; -static int lull_mod; +static int loopchk_limit; static int dir_tweaking; static int symlink_timeset_failed_flags; static int need_retouch_dir_times; @@ -169,19 +167,12 @@ static enum delret delete_item(char *fbuf, uint16 mode, uint16 flags) do_chmod(fbuf, mode | S_IWUSR); if (S_ISDIR(mode) && !(flags & DEL_DIR_IS_EMPTY)) { - int save_uid_ndx = uid_ndx; /* This only happens on the first call to delete_item() since * delete_dir_contents() always calls us w/DEL_DIR_IS_EMPTY. */ - if (!uid_ndx) - uid_ndx = ++file_extra_cnt; ignore_perishable = 1; /* If DEL_RECURSE is not set, this just reports emptiness. */ ret = delete_dir_contents(fbuf, flags); ignore_perishable = 0; - if (!save_uid_ndx) { - --file_extra_cnt; - uid_ndx = 0; - } if (ret == DR_NOT_EMPTY || ret == DR_AT_LIMIT) goto check_ret; /* OK: try to delete the directory. */ @@ -294,7 +285,7 @@ static enum delret delete_dir_contents(char *fname, uint16 flags) } strlcpy(p, fp->basename, remainder); - if (!(fp->mode & S_IWUSR) && !am_root && (uid_t)F_OWNER(fp) == our_uid) + if (!(fp->mode & S_IWUSR) && !am_root && fp->flags & FLAG_OWNED_BY_US) do_chmod(fname, fp->mode | S_IWUSR); /* Save stack by recursing to ourself directly. */ if (S_ISDIR(fp->mode)) { @@ -472,7 +463,6 @@ static void delete_in_dir(char *fbuf, struct file_struct *file, dev_t *fs_dev) struct file_list *dirlist; char delbuf[MAXPATHLEN]; int dlen, i; - int save_uid_ndx = uid_ndx; if (!fbuf) { change_local_filter_dir(NULL, 0, 0); @@ -504,9 +494,6 @@ static void delete_in_dir(char *fbuf, struct file_struct *file, dev_t *fs_dev) return; } - if (!uid_ndx) - uid_ndx = ++file_extra_cnt; - dirlist = get_dirlist(fbuf, dlen, 0); /* If an item in dirlist is not found in flist, delete it @@ -521,9 +508,12 @@ static void delete_in_dir(char *fbuf, struct file_struct *file, dev_t *fs_dev) f_name(fp, NULL)); continue; } - if (flist_find(cur_flist, fp) < 0) { + /* Here we want to match regardless of file type. Replacement + * of a file with one of another type is handled separately by + * a delete_item call with a DEL_MAKE_ROOM flag. */ + if (flist_find_ignore_dirness(cur_flist, fp) < 0) { int flags = DEL_RECURSE; - if (!(fp->mode & S_IWUSR) && !am_root && (uid_t)F_OWNER(fp) == our_uid) + if (!(fp->mode & S_IWUSR) && !am_root && fp->flags & FLAG_OWNED_BY_US) flags |= DEL_NO_UID_WRITE; f_name(fp, delbuf); if (delete_during == 2) { @@ -535,11 +525,6 @@ static void delete_in_dir(char *fbuf, struct file_struct *file, dev_t *fs_dev) } flist_free(dirlist); - - if (!save_uid_ndx) { - --file_extra_cnt; - uid_ndx = 0; - } } /* This deletes any files on the receiving side that are not present on the @@ -557,10 +542,13 @@ static void do_delete_pass(void) for (j = 0; j < cur_flist->used; j++) { struct file_struct *file = cur_flist->sorted[j]; - if (!(file->flags & FLAG_CONTENT_DIR)) + f_name(file, fbuf); + + if (!(file->flags & FLAG_CONTENT_DIR)) { + change_local_filter_dir(fbuf, strlen(fbuf), F_DEPTH(file)); continue; + } - f_name(file, fbuf); if (verbose > 1 && file->flags & FLAG_TOP_DIR) rprintf(FINFO, "deleting in %s\n", fbuf); @@ -694,8 +682,11 @@ void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statre if (iflags & ITEM_XNAME_FOLLOWS) write_vstring(sock_f_out, xname, strlen(xname)); #ifdef SUPPORT_XATTRS - if (iflags & ITEM_REPORT_XATTR && !dry_run) - send_xattr_request(NULL, file, sock_f_out); + if (preserve_xattrs && do_xfers + && iflags & (ITEM_REPORT_XATTR|ITEM_TRANSFER)) { + send_xattr_request(NULL, file, + iflags & ITEM_REPORT_XATTR ? sock_f_out : -1); + } #endif } else if (ndx >= 0) { enum logcode code = logfile_format_has_i ? FINFO : FCLIENT; @@ -750,18 +741,25 @@ static void sum_sizes_sqroot(struct sum_struct *sum, int64 len) { int32 blength; int s2length; + int64 l; + + if (len < 0) { + /* The file length overflowed our int64 var, so we can't process this file. */ + sum->count = -1; /* indicate overflow error */ + return; + } if (block_size) blength = block_size; else if (len <= BLOCK_SIZE * BLOCK_SIZE) blength = BLOCK_SIZE; else { + int32 max_blength = protocol_version < 30 ? OLD_MAX_BLOCK_SIZE : MAX_BLOCK_SIZE; int32 c; - int64 l; int cnt; for (c = 1, l = len, cnt = 0; l >>= 2; c <<= 1, cnt++) {} - if (cnt >= 31 || c >= MAX_BLOCK_SIZE) - blength = MAX_BLOCK_SIZE; + if (c < 0 || c >= max_blength) + blength = max_blength; else { blength = 0; do { @@ -780,7 +778,6 @@ static void sum_sizes_sqroot(struct sum_struct *sum, int64 len) s2length = SUM_LENGTH; } else { int32 c; - int64 l; int b = BLOCKSUM_BIAS; for (l = len; l >>= 1; b += 2) {} for (c = blength; (c >>= 1) && b; b--) {} @@ -794,7 +791,10 @@ static void sum_sizes_sqroot(struct sum_struct *sum, int64 len) sum->blength = blength; sum->s2length = s2length; sum->remainder = (int32)(len % blength); - sum->count = (int32)(len / blength) + (sum->remainder != 0); + sum->count = (int32)(l = (len / blength) + (sum->remainder != 0)); + + if ((int64)sum->count != l) + sum->count = -1; if (sum->count && verbose > 2) { rprintf(FINFO, @@ -810,7 +810,7 @@ static void sum_sizes_sqroot(struct sum_struct *sum, int64 len) * * Generate approximately one checksum every block_len bytes. */ -static void generate_and_send_sums(int fd, OFF_T len, int f_out, int f_copy) +static int generate_and_send_sums(int fd, OFF_T len, int f_out, int f_copy) { int32 i; struct map_struct *mapbuf; @@ -818,10 +818,12 @@ static void generate_and_send_sums(int fd, OFF_T len, int f_out, int f_copy) OFF_T offset = 0; sum_sizes_sqroot(&sum, len); + if (sum.count < 0) + return -1; write_sum_head(f_out, &sum); if (append_mode > 0 && f_copy < 0) - return; + return 0; if (len > 0) mapbuf = map_file(fd, len, MAX_MAP_SIZE, sum.blength); @@ -858,6 +860,8 @@ static void generate_and_send_sums(int fd, OFF_T len, int f_out, int f_copy) if (mapbuf) unmap_file(mapbuf); + + return 0; } @@ -919,6 +923,7 @@ static int copy_altdest_file(const char *src, const char *dest, struct file_stru { char buf[MAXPATHLEN]; const char *copy_to, *partialptr; + int save_preserve_xattrs = preserve_xattrs; int ok, fd_w; if (inplace) { @@ -943,7 +948,9 @@ static int copy_altdest_file(const char *src, const char *dest, struct file_stru return -1; } partialptr = partial_dir ? partial_dir_fname(dest) : NULL; + preserve_xattrs = 0; /* xattrs were copied with file */ ok = finish_transfer(dest, copy_to, src, partialptr, file, 1, 0); + preserve_xattrs = save_preserve_xattrs; cleanup_disable(); return ok ? 0 : -1; } @@ -1105,8 +1112,8 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx, } switch (type) { case TYPE_DIR: - break; case TYPE_SPECIAL: + break; case TYPE_DEVICE: devp = F_RDEV_P(file); if (sxp->st.st_rdev != MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp))) @@ -1213,6 +1220,14 @@ static void list_file_entry(struct file_struct *f) static int phase = 0; static int dflt_perms; +static int implied_dirs_are_missing; +/* Helper for recv_generator's skip_dir and dry_missing_dir tests. */ +static BOOL is_below(struct file_struct *file, struct file_struct *subtree) +{ + return F_DEPTH(file) > F_DEPTH(subtree) + && (!implied_dirs_are_missing || f_name_has_prefix(file, subtree)); +} + /* Acts on the indicated item in cur_flist whose name is fname. If a dir, * make sure it exists, and has the right permissions/timestamp info. For * all other non-regular files (symlinks, etc.) we create them here. For @@ -1227,9 +1242,12 @@ static int dflt_perms; static void recv_generator(char *fname, struct file_struct *file, int ndx, int itemizing, enum logcode code, int f_out) { - static int missing_below = -1, excluded_below = -1; static const char *parent_dirname = ""; - static struct file_struct *missing_dir = NULL, *excluded_dir = NULL; + /* Missing dir not created due to --dry-run; will still be scanned. */ + static struct file_struct *dry_missing_dir = NULL; + /* Missing dir whose contents are skipped altogether due to + * --ignore-non-existing, daemon exclude, or mkdir failure. */ + static struct file_struct *skip_dir = NULL; static struct file_list *fuzzy_dirlist = NULL; static int need_fuzzy_dirlist = 0; struct file_struct *fuzzy_file = NULL; @@ -1241,7 +1259,6 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, char *fnamecmp, *partialptr, *backupptr = NULL; char fnamecmpbuf[MAXPATHLEN]; uchar fnamecmp_type; - int implied_dirs_are_missing = relative_paths && !implied_dirs && protocol_version < 30; int del_opts = delete_mode || force_delete ? DEL_RECURSE : 0; int is_dir = !S_ISDIR(file->mode) ? 0 : inc_recurse && ndx != cur_flist->ndx_start - 1 ? -1 @@ -1258,56 +1275,44 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, return; } - if (server_filter_list.head) { - int filtered = check_filter(&server_filter_list, fname, is_dir) < 0; - if (is_dir < 0 && filtered) - return; - if (excluded_below >= 0) { - if (F_DEPTH(file) > excluded_below - && (!implied_dirs_are_missing || f_name_has_prefix(file, excluded_dir))) - goto skipping; - excluded_below = -1; - } - if (filtered) { - if (is_dir) { - excluded_below = F_DEPTH(file); - excluded_dir = file; - } - skipping: -#ifdef SUPPORT_HARD_LINKS - if (F_IS_HLINKED(file)) - handle_skipped_hlink(file, itemizing, code, f_out); -#endif - rprintf(FERROR_XFER, - "skipping daemon-excluded file \"%s\"\n", - fname); - return; - } - } - - if (missing_below >= 0) { - if (F_DEPTH(file) <= missing_below - || (implied_dirs_are_missing && !f_name_has_prefix(file, missing_dir))) { - if (dry_run) - dry_run--; - missing_below = -1; - } else if (!dry_run) { + if (skip_dir) { + if (is_below(file, skip_dir)) { if (is_dir) file->flags |= FLAG_MISSING_DIR; #ifdef SUPPORT_HARD_LINKS - if (F_IS_HLINKED(file)) + else if (F_IS_HLINKED(file)) handle_skipped_hlink(file, itemizing, code, f_out); #endif return; } + skip_dir = NULL; } + #ifdef SUPPORT_ACLS sx.acc_acl = sx.def_acl = NULL; #endif #ifdef SUPPORT_XATTRS sx.xattr = NULL; #endif - if (dry_run > 1) { + if (daemon_filter_list.head && (*fname != '.' || fname[1])) { + if (check_filter(&daemon_filter_list, FLOG, fname, is_dir) < 0) { + if (is_dir < 0) + return; +#ifdef SUPPORT_HARD_LINKS + if (F_IS_HLINKED(file)) + handle_skipped_hlink(file, itemizing, code, f_out); +#endif + rprintf(FERROR_XFER, + "skipping daemon-excluded %s \"%s\"\n", + is_dir ? "directory" : "file", fname); + if (is_dir) + goto skipping_dir_contents; + return; + } + } + + if (dry_run > 1 || (dry_missing_dir && is_below(file, dry_missing_dir))) { + parent_is_dry_missing: if (fuzzy_dirlist) { flist_free(fuzzy_dirlist); fuzzy_dirlist = NULL; @@ -1317,13 +1322,17 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, stat_errno = ENOENT; } else { const char *dn = file->dirname ? file->dirname : "."; + dry_missing_dir = NULL; if (parent_dirname != dn && strcmp(parent_dirname, dn) != 0) { if (relative_paths && !implied_dirs - && do_stat(dn, &sx.st) < 0 - && create_directory_path(fname) < 0) { - rsyserr(FERROR_XFER, errno, - "recv_generator: mkdir %s failed", - full_fname(dn)); + && do_stat(dn, &sx.st) < 0) { + if (dry_run) + goto parent_is_dry_missing; + if (create_directory_path(fname) < 0) { + rsyserr(FERROR_XFER, errno, + "recv_generator: mkdir %s failed", + full_fname(dn)); + } } if (fuzzy_dirlist) { flist_free(fuzzy_dirlist); @@ -1340,7 +1349,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, if (need_fuzzy_dirlist && S_ISREG(file->mode)) { strlcpy(fnamecmpbuf, dn, sizeof fnamecmpbuf); - fuzzy_dirlist = get_dirlist(fnamecmpbuf, -1, 1); + fuzzy_dirlist = get_dirlist(fnamecmpbuf, -1, GDL_IGNORE_FILTER_RULES); need_fuzzy_dirlist = 0; } @@ -1352,12 +1361,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, if (is_dir) { if (is_dir < 0) return; - if (missing_below < 0) { - if (dry_run) - dry_run++; - missing_below = F_DEPTH(file); - missing_dir = file; - } + skip_dir = file; file->flags |= FLAG_MISSING_DIR; } #ifdef SUPPORT_HARD_LINKS @@ -1386,9 +1390,18 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, goto cleanup; } + fnamecmp = fname; + if (is_dir) { + mode_t added_perms; if (!implied_dirs && file->flags & FLAG_IMPLIED_DIR) goto cleanup; + if (am_root < 0) { + /* For --fake-super, the dir must be useable by the copying + * user, just like it would be for root. */ + added_perms = S_IRUSR|S_IWUSR|S_IXUSR; + } else + added_perms = 0; if (is_dir < 0) { /* In inc_recurse mode we want to make sure any missing * directories get created while we're still processing @@ -1399,7 +1412,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, && (S_ISDIR(sx.st.st_mode) || delete_item(fname, sx.st.st_mode, del_opts | DEL_FOR_DIR) != 0)) goto cleanup; /* Any errors get reported later. */ - if (do_mkdir(fname, file->mode & 0700) == 0) + if (do_mkdir(fname, (file->mode|added_perms) & 0700) == 0) file->flags |= FLAG_DIR_CREATED; goto cleanup; } @@ -1413,10 +1426,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, goto skipping_dir_contents; statret = -1; } - if (dry_run && statret != 0 && missing_below < 0) { - missing_below = F_DEPTH(file); - missing_dir = file; - dry_run++; + if (dry_run && statret != 0) { + if (!dry_missing_dir) + dry_missing_dir = file; + file->flags |= FLAG_MISSING_DIR; } real_ret = statret; real_sx = sx; @@ -1432,29 +1445,35 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, if (j == -2) { itemizing = 0; code = FNONE; - } else if (j >= 0) statret = 1; + } else if (j >= 0) { + statret = 1; + fnamecmp = fnamecmpbuf; + } } if (itemizing && f_out != -1) { - itemize(fname, file, ndx, statret, &sx, + itemize(fnamecmp, file, ndx, statret, &sx, statret ? ITEM_LOCAL_CHANGE : 0, 0, NULL); } - if (real_ret != 0 && do_mkdir(fname,file->mode) < 0 && errno != EEXIST) { + if (real_ret != 0 && do_mkdir(fname,file->mode|added_perms) < 0 && errno != EEXIST) { if (!relative_paths || errno != ENOENT - || create_directory_path(fname) < 0 - || (do_mkdir(fname, file->mode) < 0 && errno != EEXIST)) { + || create_directory_path(fname) < 0 + || (do_mkdir(fname, file->mode|added_perms) < 0 && errno != EEXIST)) { rsyserr(FERROR_XFER, errno, "recv_generator: mkdir %s failed", full_fname(fname)); skipping_dir_contents: rprintf(FERROR, "*** Skipping any contents from this failed directory ***\n"); - missing_below = F_DEPTH(file); - missing_dir = file; + skip_dir = file; file->flags |= FLAG_MISSING_DIR; goto cleanup; } } +#ifdef SUPPORT_XATTRS + if (preserve_xattrs && statret == 1) + copy_xattrs(fnamecmpbuf, fname); +#endif if (set_file_attrs(fname, file, real_ret ? NULL : &real_sx, NULL, 0) && verbose && code != FNONE && f_out != -1) rprintf(code, "%s/\n", fname); @@ -1483,9 +1502,13 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, DEV_MINOR(devp) = minor(real_sx.st.st_dev); } } - else if (delete_during && f_out != -1 && !phase && dry_run < 2 - && (file->flags & FLAG_CONTENT_DIR)) - delete_in_dir(fname, file, &real_sx.st.st_dev); + else if (delete_during && f_out != -1 && !phase + && !(file->flags & FLAG_MISSING_DIR)) { + if (file->flags & FLAG_CONTENT_DIR) + delete_in_dir(fname, file, &real_sx.st.st_dev); + else + change_local_filter_dir(fname, strlen(fname), F_DEPTH(file)); + } goto cleanup; } @@ -1508,11 +1531,14 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, const char *sl = F_SYMLINK(file); if (safe_symlinks && unsafe_symlink(sl, fname)) { if (verbose) { - if (solo_file) + if (solo_file) { + /* fname contains the destination path, but we + * want to report the source path. */ fname = f_name(file, NULL); + } rprintf(FINFO, - "ignoring unsafe symlink %s -> \"%s\"\n", - full_fname(fname), sl); + "ignoring unsafe symlink \"%s\" -> \"%s\"\n", + fname, sl); } return; } @@ -1589,8 +1615,12 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, if ((am_root && preserve_devices && IS_DEVICE(file->mode)) || (preserve_specials && IS_SPECIAL(file->mode))) { - uint32 *devp = F_RDEV_P(file); - dev_t rdev = MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp)); + dev_t rdev; + if (IS_DEVICE(file->mode)) { + uint32 *devp = F_RDEV_P(file); + rdev = MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp)); + } else + rdev = 0; if (statret == 0) { int del_for_flag; if (IS_DEVICE(file->mode)) { @@ -1604,7 +1634,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, } if (statret == 0 && BITS_EQUAL(sx.st.st_mode, file->mode, _S_IFMT) - && sx.st.st_rdev == rdev) { + && (IS_SPECIAL(sx.st.st_mode) || sx.st.st_rdev == rdev)) { /* The device or special file is identical. */ set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT); if (itemizing) @@ -1702,7 +1732,6 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, goto cleanup; } - fnamecmp = fname; fnamecmp_type = FNAMECMP_FNAME; if (statret == 0 && !S_ISREG(sx.st.st_mode)) { @@ -1738,7 +1767,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, } else partialptr = NULL; - if (statret != 0 && fuzzy_dirlist && dry_run <= 1) { + if (statret != 0 && fuzzy_dirlist) { int j = find_fuzzy(file, fuzzy_dirlist); if (j >= 0) { fuzzy_file = fuzzy_dirlist->files[j]; @@ -1865,15 +1894,21 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, close(fd); goto cleanup; } - if ((f_copy = do_open(backupptr, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0600)) < 0 - && (errno != ENOENT || make_bak_dir(backupptr) < 0 - || (f_copy = do_open(backupptr, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0600)) < 0)) { - rsyserr(FERROR_XFER, errno, "open %s", - full_fname(backupptr)); - unmake_file(back_file); - back_file = NULL; - close(fd); - goto cleanup; + if ((f_copy = do_open(backupptr, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0600)) < 0) { + int save_errno = errno ? errno : EINVAL; /* 0 paranoia */ + if (errno == ENOENT && make_bak_dir(backupptr) == 0) { + if ((f_copy = do_open(backupptr, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0600)) < 0) + save_errno = errno ? errno : save_errno; + else + save_errno = 0; + } + if (save_errno) { + rsyserr(FERROR_XFER, save_errno, "open %s", full_fname(backupptr)); + unmake_file(back_file); + back_file = NULL; + close(fd); + goto cleanup; + } } fnamecmp_type = FNAMECMP_BACKUP; } @@ -1928,16 +1963,32 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, if (statret != 0 || whole_file) write_sum_head(f_out, NULL); - else { - generate_and_send_sums(fd, sx.st.st_size, f_out, f_copy); + else if (sx.st.st_size <= 0) { + write_sum_head(f_out, NULL); + close(fd); + } else { + if (generate_and_send_sums(fd, sx.st.st_size, f_out, f_copy) < 0) { + rprintf(FWARNING, + "WARNING: file is too large for checksum sending: %s\n", + fnamecmp); + write_sum_head(f_out, NULL); + } close(fd); } cleanup: if (back_file) { + int save_preserve_xattrs = preserve_xattrs; if (f_copy >= 0) close(f_copy); +#ifdef SUPPORT_XATTRS + if (preserve_xattrs) { + copy_xattrs(fname, backupptr); + preserve_xattrs = 0; + } +#endif set_file_attrs(backupptr, back_file, NULL, NULL, 0); + preserve_xattrs = save_preserve_xattrs; if (verbose > 1) { rprintf(FINFO, "backed up %s to %s\n", fname, backupptr); @@ -1983,6 +2034,7 @@ static void touch_up_dirs(struct file_list *flist, int ndx) static int counter = 0; struct file_struct *file; char *fname; + BOOL fix_dir_perms; int i, start, end; if (ndx < 0) { @@ -2003,11 +2055,13 @@ static void touch_up_dirs(struct file_list *flist, int ndx) rprintf(FINFO, "touch_up_dirs: %s (%d)\n", NS(fname), i); } + /* Be sure not to retouch permissions with --fake-super. */ + fix_dir_perms = !am_root && !(file->mode & S_IWUSR); if (!F_IS_ACTIVE(file) || file->flags & FLAG_MISSING_DIR - || (!need_retouch_dir_times && file->mode & S_IWUSR)) + || !(need_retouch_dir_times || fix_dir_perms)) continue; fname = f_name(file, NULL); - if (!(file->mode & S_IWUSR)) + if (fix_dir_perms) do_chmod(fname, file->mode); if (need_retouch_dir_times) { STRUCT_STAT st; @@ -2015,10 +2069,13 @@ static void touch_up_dirs(struct file_list *flist, int ndx) && cmp_time(st.st_mtime, file->modtime) != 0) set_modtime(fname, file->modtime, file->mode); } - if (allowed_lull && !(counter % lull_mod)) - maybe_send_keepalive(); - else if (!(counter & 0xFF)) - maybe_flush_socket(0); + if (counter >= loopchk_limit) { + if (allowed_lull) + maybe_send_keepalive(); + else + maybe_flush_socket(0); + counter = 0; + } } } @@ -2032,8 +2089,7 @@ void check_for_finished_files(int itemizing, enum logcode code, int check_redo) while (1) { #ifdef SUPPORT_HARD_LINKS if (preserve_hard_links && (ndx = get_hlink_num()) != -1) { - flist = flist_for_ndx(ndx); - assert(flist != NULL); + flist = flist_for_ndx(ndx, "check_for_finished_files.1"); file = flist->files[ndx - flist->ndx_start]; assert(file->flags & FLAG_HLINKED); finish_hard_link(file, f_name(file, fbuf), ndx, NULL, itemizing, code, -1); @@ -2056,7 +2112,7 @@ void check_for_finished_files(int itemizing, enum logcode code, int check_redo) ignore_times++; flist = cur_flist; - cur_flist = flist_for_ndx(ndx); + cur_flist = flist_for_ndx(ndx, "check_for_finished_files.2"); file = cur_flist->files[ndx - cur_flist->ndx_start]; if (solo_file) @@ -2089,10 +2145,9 @@ void check_for_finished_files(int itemizing, enum logcode code, int check_redo) if (first_flist->in_progress || first_flist->to_redo) break; - if (!read_batch) { - write_ndx(sock_f_out, NDX_DONE); + write_ndx(sock_f_out, NDX_DONE); + if (!read_batch) maybe_flush_socket(1); - } if (delete_during == 2 || !dir_tweaking) { /* Skip directory touch-up. */ @@ -2105,7 +2160,7 @@ void check_for_finished_files(int itemizing, enum logcode code, int check_redo) void generate_files(int f_out, const char *local_name) { - int i, ndx; + int i, ndx, next_loopchk = 0; char fbuf[MAXPATHLEN]; int itemizing; enum logcode code; @@ -2131,9 +2186,10 @@ void generate_files(int f_out, const char *local_name) solo_file = local_name; dir_tweaking = !(list_only || solo_file || dry_run); need_retouch_dir_times = preserve_times > 1; - lull_mod = allowed_lull * 5; + loopchk_limit = allowed_lull ? allowed_lull * 5 : 200; symlink_timeset_failed_flags = ITEM_REPORT_TIME | (protocol_version >= 30 || !am_server ? ITEM_REPORT_TIMEFAIL : 0); + implied_dirs_are_missing = relative_paths && !implied_dirs && protocol_version < 30; if (verbose > 2) rprintf(FINFO, "generator starting pid=%ld\n", (long)getpid()); @@ -2160,7 +2216,7 @@ void generate_files(int f_out, const char *local_name) /* Since we often fill up the outgoing socket and then just sit around * waiting for the other 2 processes to do their thing, we don't want * to exit on a timeout. If the data stops flowing, the receiver will - * notice that and let us know via the redo pipe (or its closing). */ + * notice that and let us know via the message pipe (or its closing). */ ignore_timeout = 1; dflt_perms = (ACCESSPERMS & ~orig_umask); @@ -2175,19 +2231,24 @@ void generate_files(int f_out, const char *local_name) if (inc_recurse && cur_flist->parent_ndx >= 0) { struct file_struct *fp = dir_flist->files[cur_flist->parent_ndx]; - f_name(fp, fbuf); + if (solo_file) + strlcpy(fbuf, solo_file, sizeof fbuf); + else + f_name(fp, fbuf); ndx = cur_flist->ndx_start - 1; recv_generator(fbuf, fp, ndx, itemizing, code, f_out); - if (delete_during && dry_run < 2 && !list_only) { - if (BITS_SETnUNSET(fp->flags, FLAG_CONTENT_DIR, FLAG_MISSING_DIR)) { + if (delete_during && dry_run < 2 && !list_only + && !(fp->flags & FLAG_MISSING_DIR)) { + if (fp->flags & FLAG_CONTENT_DIR) { dev_t dirdev; if (one_file_system) { uint32 *devp = F_DIR_DEV_P(fp); dirdev = MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp)); } else dirdev = MAKEDEV(0, 0); - delete_in_dir(f_name(fp, fbuf), fp, &dirdev); - } + delete_in_dir(fbuf, fp, &dirdev); + } else + change_local_filter_dir(fbuf, strlen(fbuf), F_DEPTH(fp)); } } for (i = cur_flist->low; i <= cur_flist->high; i++) { @@ -2209,10 +2270,13 @@ void generate_files(int f_out, const char *local_name) check_for_finished_files(itemizing, code, 0); - if (allowed_lull && !(i % lull_mod)) - maybe_send_keepalive(); - else if (!(i & 0xFF)) - maybe_flush_socket(0); + if (i + cur_flist->ndx_start >= next_loopchk) { + if (allowed_lull) + maybe_send_keepalive(); + else + maybe_flush_socket(0); + next_loopchk += loopchk_limit; + } } if (!inc_recurse) { @@ -2246,6 +2310,7 @@ void generate_files(int f_out, const char *local_name) rprintf(FINFO, "generate_files phase=%d\n", phase); write_ndx(f_out, NDX_DONE); + /* Reduce round-trip lag-time for a useless delay-updates phase. */ if (protocol_version >= 29 && !delay_updates) write_ndx(f_out, NDX_DONE); @@ -2280,7 +2345,7 @@ void generate_files(int f_out, const char *local_name) touch_up_dirs(dir_flist, -1); if (max_delete >= 0 && deletion_count > max_delete) { - rprintf(FINFO, + rprintf(FWARNING, "Deletions stopped due to --max-delete limit (%d skipped)\n", deletion_count - max_delete); io_error |= IOERR_DEL_LIMIT;