X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/b3bf9b9df95137a3a43248be9599d919b04877af..HEAD:/generator.c diff --git a/generator.c b/generator.c index 06279565..d5dc85f5 100644 --- a/generator.c +++ b/generator.c @@ -44,12 +44,11 @@ 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; extern int delete_after; +extern int missing_args; extern int msgdone_cnt; extern int ignore_errors; extern int remove_source_files; @@ -70,7 +69,6 @@ extern int io_error; extern int flist_eof; extern int allowed_lull; extern int sock_f_out; -extern int ignore_timeout; extern int protocol_version; extern int file_total; extern int fuzzy_basis; @@ -90,13 +88,13 @@ extern int max_delete; extern int force_delete; extern int one_file_system; extern int skipped_deletes; -extern struct stats stats; extern dev_t filesystem_dev; extern mode_t orig_umask; extern uid_t our_uid; +extern char *tmpdir; extern char *basis_dir[MAX_BASIS_DIRS+1]; extern struct file_list *cur_flist, *first_flist, *dir_flist; -extern struct filter_list_struct daemon_filter_list; +extern filter_rule_list filter_list, daemon_filter_list; int maybe_ATTRS_REPORT = 0; @@ -121,13 +119,16 @@ static void handle_skipped_hlink(struct file_struct *file, int itemizing, enum logcode code, int f_out); #endif +#define EARLY_DELAY_DONE_MSG() (!delay_updates) +#define EARLY_DELETE_DONE_MSG() (!(delete_during == 2 || delete_after)) + static int start_delete_delay_temp(void) { char fnametmp[MAXPATHLEN]; int save_dry_run = dry_run; dry_run = 0; - if (!get_tmpname(fnametmp, "deldelay") + if (!get_tmpname(fnametmp, "deldelay", False) || (deldelay_fd = do_mkstemp(fnametmp, 0600)) < 0) { rprintf(FINFO, "NOTE: Unable to create delete-delay temp file%s.\n", inc_recurse ? "" : " -- switching to --delete-after"); @@ -275,7 +276,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); @@ -286,9 +286,9 @@ static void delete_in_dir(char *fbuf, struct file_struct *file, dev_t *fs_dev) rprintf(FINFO, "delete_in_dir(%s)\n", fbuf); if (allowed_lull) - maybe_send_keepalive(); + maybe_send_keepalive(time(NULL), MSK_ALLOW_FLUSH); - if (io_error && !ignore_errors) { + if (io_error & IOERR_GENERAL && !ignore_errors) { if (already_warned) return; rprintf(FINFO, @@ -307,9 +307,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 @@ -329,7 +326,7 @@ static void delete_in_dir(char *fbuf, struct file_struct *file, dev_t *fs_dev) * 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) { @@ -341,11 +338,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 @@ -385,47 +377,111 @@ static void do_delete_pass(void) rprintf(FINFO, " \r"); } -int unchanged_attrs(const char *fname, struct file_struct *file, stat_x *sxp) +static BOOL preserve_time_of(struct file_struct *file) { -#if !defined HAVE_LUTIMES || !defined HAVE_UTIMES - if (S_ISLNK(file->mode)) { - ; - } else -#endif - if (preserve_times && cmp_time(sxp->st.st_mtime, file->modtime) != 0) - return 0; + if (S_ISDIR(file->mode)) + return preserve_times & PRESERVE_DIR_TIMES; + else if (S_ISLNK(file->mode)) + /* PRESERVE_LINK_TIMES will only be set if CAN_SET_SYMLINK_TIMES. */ + return preserve_times & PRESERVE_LINK_TIMES; + else + return preserve_times; +} - if (preserve_perms) { - if (!BITS_EQUAL(sxp->st.st_mode, file->mode, CHMOD_BITS)) - return 0; - } else if (preserve_executability - && ((sxp->st.st_mode & 0111 ? 1 : 0) ^ (file->mode & 0111 ? 1 : 0))) +static BOOL report_time(struct file_struct *file, stat_x *sxp) +{ + return preserve_time_of(file) && + cmp_time(sxp->st.st_mtime, file->modtime); +} + +static BOOL report_perms(struct file_struct *file, stat_x *sxp) +{ +#ifndef CAN_CHMOD_SYMLINK + if (S_ISLNK(file->mode)) return 0; +#endif + + if (preserve_perms) + return !BITS_EQUAL(sxp->st.st_mode, file->mode, CHMOD_BITS); + + if (preserve_executability) + return (sxp->st.st_mode & 0111 ? 1 : 0) ^ (file->mode & 0111 ? 1 : 0); - if (am_root && uid_ndx && sxp->st.st_uid != (uid_t)F_OWNER(file)) + return 0; +} + +static BOOL report_owner(struct file_struct *file, stat_x *sxp) +{ +#ifndef CAN_CHOWN_SYMLINK + if (S_ISLNK(file->mode)) return 0; +#endif + + return uid_ndx && am_root && sxp->st.st_uid != (uid_t)F_OWNER(file); +} - if (gid_ndx && !(file->flags & FLAG_SKIP_GROUP) && sxp->st.st_gid != (gid_t)F_GROUP(file)) +static BOOL report_group(struct file_struct *file, stat_x *sxp) +{ +#ifndef CAN_CHOWN_SYMLINK + if (S_ISLNK(file->mode)) return 0; +#endif + + return gid_ndx && !(file->flags & FLAG_SKIP_GROUP) && sxp->st.st_gid != (gid_t)F_GROUP(file); +} #ifdef SUPPORT_ACLS - if (preserve_acls && !S_ISLNK(file->mode)) { +static BOOL report_acls(const char *fname, struct file_struct *file, stat_x *sxp) +{ +#ifndef CAN_SET_SYMLINK_ACLS /* not currently */ + if (S_ISLNK(file->mode)) + return 0; +#endif + + if (preserve_acls) { if (!ACL_READY(*sxp)) get_acl(fname, sxp); - if (set_acl(NULL, file, sxp) == 0) - return 0; + if (set_acl(NULL, file, sxp, file->mode)) + return 1; } + + return 0; +} #endif + #ifdef SUPPORT_XATTRS +static BOOL report_xattrs(const char *fname, struct file_struct *file, stat_x *sxp, + BOOL mark_needed) +{ +#ifdef NO_SYMLINK_XATTRS + if (S_ISLNK(file->mode)) + return 0; +#endif + if (preserve_xattrs) { if (!XATTR_READY(*sxp)) get_xattr(fname, sxp); - if (xattr_diff(file, sxp, 0)) - return 0; + if (xattr_diff(file, sxp, mark_needed)) + return 1; } + + return 0; +} #endif - return 1; +int unchanged_attrs(const char *fname, struct file_struct *file, stat_x *sxp) +{ + return !(report_time(file, sxp) + || report_perms(file, sxp) + || report_owner(file, sxp) + || report_group(file, sxp) +#ifdef SUPPORT_ACLS + || report_acls(fname, file, sxp) +#endif +#ifdef SUPPORT_XATTRS + || report_xattrs(fname, file, sxp, False) +#endif + ); } void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statret, @@ -433,59 +489,33 @@ void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statre const char *xname) { if (statret >= 0) { /* A from-dest-dir statret can == 1! */ - int keep_time = !preserve_times ? 0 - : S_ISDIR(file->mode) ? preserve_times > 1 : -#if defined HAVE_LUTIMES && defined HAVE_UTIMES - 1; -#else - !S_ISLNK(file->mode); -#endif - if (S_ISREG(file->mode) && F_LENGTH(file) != sxp->st.st_size) iflags |= ITEM_REPORT_SIZE; if (file->flags & FLAG_TIME_FAILED) { /* symlinks only */ if (iflags & ITEM_LOCAL_CHANGE) iflags |= symlink_timeset_failed_flags; - } else if (keep_time + } else if (preserve_time_of(file) ? cmp_time(file->modtime, sxp->st.st_mtime) != 0 : iflags & (ITEM_TRANSFER|ITEM_LOCAL_CHANGE) && !(iflags & ITEM_MATCHED) && (!(iflags & ITEM_XNAME_FOLLOWS) || *xname)) iflags |= ITEM_REPORT_TIME; -#if !defined HAVE_LCHMOD && !defined HAVE_SETATTRLIST - if (S_ISLNK(file->mode)) { - ; - } else -#endif - if (preserve_perms) { - if (!BITS_EQUAL(sxp->st.st_mode, file->mode, CHMOD_BITS)) - iflags |= ITEM_REPORT_PERMS; - } else if (preserve_executability - && ((sxp->st.st_mode & 0111 ? 1 : 0) ^ (file->mode & 0111 ? 1 : 0))) + if (report_perms(file, sxp)) iflags |= ITEM_REPORT_PERMS; - if (uid_ndx && am_root && (uid_t)F_OWNER(file) != sxp->st.st_uid) + if (report_owner(file, sxp)) iflags |= ITEM_REPORT_OWNER; - if (gid_ndx && !(file->flags & FLAG_SKIP_GROUP) - && sxp->st.st_gid != (gid_t)F_GROUP(file)) + if (report_group(file, sxp)) iflags |= ITEM_REPORT_GROUP; #ifdef SUPPORT_ACLS - if (preserve_acls && !S_ISLNK(file->mode)) { - if (!ACL_READY(*sxp)) - get_acl(fnamecmp, sxp); - if (set_acl(NULL, file, sxp) == 0) - iflags |= ITEM_REPORT_ACL; - } + if (report_acls(fnamecmp, file, sxp)) + iflags |= ITEM_REPORT_ACL; #endif #ifdef SUPPORT_XATTRS - if (preserve_xattrs) { - if (!XATTR_READY(*sxp)) - get_xattr(fnamecmp, sxp); - if (xattr_diff(file, sxp, 1)) - iflags |= ITEM_REPORT_XATTR; - } + if (report_xattrs(fnamecmp, file, sxp, True)) + iflags |= ITEM_REPORT_XATTR; #endif } else { #ifdef SUPPORT_XATTRS - if (preserve_xattrs && xattr_diff(file, NULL, 1)) + if (preserve_xattrs && xattr_diff(file, NULL, True)) iflags |= ITEM_REPORT_XATTR; #endif iflags |= ITEM_IS_NEW; @@ -503,15 +533,17 @@ 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 (preserve_xattrs && !dry_run + if (preserve_xattrs && do_xfers && iflags & (ITEM_REPORT_XATTR|ITEM_TRANSFER)) { - send_xattr_request(NULL, file, - iflags & ITEM_REPORT_XATTR ? sock_f_out : -1); + int fd = iflags & ITEM_REPORT_XATTR + && (protocol_version < 31 || !BITS_SET(iflags, ITEM_XNAME_FOLLOWS|ITEM_LOCAL_CHANGE)) + ? sock_f_out : -1; + send_xattr_request(NULL, file, fd); } #endif } else if (ndx >= 0) { enum logcode code = logfile_format_has_i ? FINFO : FCLIENT; - log_item(code, file, &stats, iflags, xname); + log_item(code, file, iflags, xname); } } } @@ -564,6 +596,12 @@ static void sum_sizes_sqroot(struct sum_struct *sum, int64 len) 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) @@ -752,7 +790,7 @@ static int copy_altdest_file(const char *src, const char *dest, struct file_stru copy_to = buf; } cleanup_set(copy_to, NULL, NULL, -1, -1); - if (copy_file(src, copy_to, fd_w, file->mode, 0) < 0) { + if (copy_file(src, copy_to, fd_w, file->mode) < 0) { if (INFO_GTE(COPY, 1)) { rsyserr(FINFO, errno, "copy_file %s => %s", full_fname(src), copy_to); @@ -869,12 +907,15 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx, char *cmpbuf, stat_x *sxp, int itemizing, enum logcode code) { - char lnk[MAXPATHLEN]; int best_match = -1; int match_level = 0; enum nonregtype type; uint32 *devp; - int len, j = 0; +#ifdef SUPPORT_LINKS + char lnk[MAXPATHLEN]; + int len; +#endif + int j = 0; #ifndef SUPPORT_LINKS if (S_ISLNK(file->mode)) @@ -914,11 +955,13 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx, if (!IS_DEVICE(sxp->st.st_mode)) continue; break; -#ifdef SUPPORT_LINKS case TYPE_SYMLINK: +#ifdef SUPPORT_LINKS if (!S_ISLNK(sxp->st.st_mode)) continue; break; +#else + return -1; #endif } if (match_level < 1) { @@ -927,21 +970,23 @@ 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))) continue; break; -#ifdef SUPPORT_LINKS case TYPE_SYMLINK: - if ((len = readlink(cmpbuf, lnk, MAXPATHLEN-1)) <= 0) +#ifdef SUPPORT_LINKS + if ((len = do_readlink(cmpbuf, lnk, MAXPATHLEN-1)) <= 0) continue; lnk[len] = '\0'; if (strcmp(lnk, F_SYMLINK(file)) != 0) continue; break; +#else + return -1; #endif } if (match_level < 2) { @@ -1022,14 +1067,18 @@ static void list_file_entry(struct file_struct *f) #ifdef SUPPORT_LINKS if (preserve_links && S_ISLNK(f->mode)) { rprintf(FINFO, "%s %*s %s %s -> %s\n", - permbuf, colwidth, comma_num(len), + permbuf, colwidth, human_num(len), timestring(f->modtime), f_name(f, NULL), F_SYMLINK(f)); } else #endif - { + if (missing_args == 2 && f->mode == 0) { + rprintf(FINFO, "%-*s %s\n", + colwidth + 31, "*missing", + f_name(f, NULL)); + } else { rprintf(FINFO, "%s %*s %s %s\n", - permbuf, colwidth, comma_num(len), + permbuf, colwidth, human_num(len), timestring(f->modtime), f_name(f, NULL)); } } @@ -1105,6 +1154,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, skip_dir = NULL; } + init_stat_x(&sx); if (daemon_filter_list.head && (*fname != '.' || fname[1])) { if (check_filter(&daemon_filter_list, FLOG, fname, is_dir) < 0) { if (is_dir < 0) @@ -1114,7 +1164,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, handle_skipped_hlink(file, itemizing, code, f_out); #endif rprintf(FERROR_XFER, - "skipping daemon-excluded %s \"%s\"\n", + "ERROR: daemon refused to receive %s \"%s\"\n", is_dir ? "directory" : "file", fname); if (is_dir) goto skipping_dir_contents; @@ -1122,7 +1172,6 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, } } - init_stat_x(&sx); if (dry_run > 1 || (dry_missing_dir && is_below(file, dry_missing_dir))) { parent_is_dry_missing: if (fuzzy_dirlist) { @@ -1140,7 +1189,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, && do_stat(dn, &sx.st) < 0) { if (dry_run) goto parent_is_dry_missing; - if (create_directory_path(fname) < 0) { + if (make_path(fname, MKP_DROP_NAME | MKP_SKIP_SLASH) < 0) { rsyserr(FERROR_XFER, errno, "recv_generator: mkdir %s failed", full_fname(dn)); @@ -1161,7 +1210,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; } @@ -1169,6 +1218,14 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, stat_errno = errno; } + if (missing_args == 2 && file->mode == 0) { + if (filter_list.head && check_filter(&filter_list, FINFO, fname, is_dir) < 0) + return; + if (statret == 0) + delete_item(fname, sx.st.st_mode, del_opts); + return; + } + if (ignore_non_existing > 0 && statret == -1 && stat_errno == ENOENT) { if (is_dir) { if (is_dir < 0) @@ -1202,9 +1259,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 @@ -1215,7 +1281,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; } @@ -1249,17 +1315,19 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, itemizing = 0; code = FNONE; statret = 1; - } else if (j >= 0) + } 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)) { + || make_path(fname, MKP_DROP_NAME | MKP_SKIP_SLASH) < 0 + || (do_mkdir(fname, file->mode|added_perms) < 0 && errno != EEXIST)) { rsyserr(FERROR_XFER, errno, "recv_generator: mkdir %s failed", full_fname(fname)); @@ -1271,6 +1339,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, goto cleanup; } } + #ifdef SUPPORT_XATTRS if (preserve_xattrs && statret == 1) copy_xattrs(fnamecmpbuf, fname); @@ -1332,11 +1401,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 (INFO_GTE(NAME, 1)) { - 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; } @@ -1344,10 +1416,9 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, char lnk[MAXPATHLEN]; int len; - if (!S_ISLNK(sx.st.st_mode)) - statret = -1; - else if ((len = readlink(fname, lnk, MAXPATHLEN-1)) > 0 - && strncmp(lnk, sl, len) == 0 && sl[len] == '\0') { + if (S_ISLNK(sx.st.st_mode) + && (len = do_readlink(fname, lnk, MAXPATHLEN-1)) > 0 + && strncmp(lnk, sl, len) == 0 && sl[len] == '\0') { /* The link is pointing to the right place. */ set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT); if (itemizing) @@ -1360,10 +1431,6 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, goto return_with_success; goto cleanup; } - /* Not the right symlink (or not a symlink), so - * delete it. */ - if (delete_item(fname, sx.st.st_mode, del_opts | DEL_FOR_SYMLINK) != 0) - goto cleanup; } else if (basis_dir[0] != NULL) { int j = try_dests_non(file, fname, ndx, fnamecmpbuf, &sx, itemizing, code); @@ -1380,18 +1447,11 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, } else if (j >= 0) statret = 1; } -#ifdef SUPPORT_HARD_LINKS - if (preserve_hard_links && F_HLINK_NOT_LAST(file)) { - cur_flist->in_progress++; - goto cleanup; - } -#endif - if (do_symlink(sl, fname) != 0) { - rsyserr(FERROR_XFER, errno, "symlink %s -> \"%s\" failed", - full_fname(fname), sl); - } else { + if (atomic_create(file, fname, sl, MAKEDEV(0, 0), &sx, statret == 0 ? DEL_FOR_SYMLINK : 0)) { set_file_attrs(fname, file, NULL, NULL, 0); if (itemizing) { + if (statret == 0 && !S_ISLNK(sx.st.st_mode)) + statret = -1; itemize(fname, file, ndx, statret, &sx, ITEM_LOCAL_CHANGE|ITEM_REPORT_CHANGE, 0, NULL); } @@ -1413,10 +1473,14 @@ 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; + int del_for_flag = 0; + 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)) { if (!IS_DEVICE(sx.st.st_mode)) statret = -1; @@ -1428,7 +1492,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) @@ -1441,8 +1505,6 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, goto return_with_success; goto cleanup; } - if (delete_item(fname, sx.st.st_mode, del_opts | del_for_flag) != 0) - goto cleanup; } else if (basis_dir[0] != NULL) { int j = try_dests_non(file, fname, ndx, fnamecmpbuf, &sx, itemizing, code); @@ -1459,21 +1521,12 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, } else if (j >= 0) statret = 1; } -#ifdef SUPPORT_HARD_LINKS - if (preserve_hard_links && F_HLINK_NOT_LAST(file)) { - cur_flist->in_progress++; - goto cleanup; - } -#endif if (DEBUG_GTE(GENR, 1)) { rprintf(FINFO, "mknod(%s, 0%o, [%ld,%ld])\n", fname, (int)file->mode, (long)major(rdev), (long)minor(rdev)); } - if (do_mknod(fname, file->mode, rdev) < 0) { - rsyserr(FERROR_XFER, errno, "mknod %s failed", - full_fname(fname)); - } else { + if (atomic_create(file, fname, NULL, rdev, &sx, del_for_flag)) { set_file_attrs(fname, file, NULL, NULL, 0); if (itemizing) { itemize(fname, file, ndx, statret, &sx, @@ -1526,7 +1579,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)) { @@ -1641,7 +1693,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, goto cleanup; if (!(back_file = make_file(fname, NULL, NULL, 0, NO_FILTERS))) goto pretend_missing; - if (copy_file(fname, backupptr, -1, back_file->mode, 1) < 0) { + if (copy_file(fname, backupptr, -1, back_file->mode) < 0) { unmake_file(back_file); back_file = NULL; goto cleanup; @@ -1690,20 +1742,11 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, 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; - } + rsyserr(FERROR_XFER, errno, "open %s", full_fname(backupptr)); + unmake_file(back_file); + back_file = NULL; + close(fd); + goto cleanup; } fnamecmp_type = FNAMECMP_BACKUP; } @@ -1802,6 +1845,74 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, return; } +/* If we are replacing an existing hard link, symlink, device, or special file, + * create a temp-name item and rename it into place. Only a symlink or hard + * link puts a non-NULL value into the lnk arg. Only a device puts a non-0 + * value into the rdev arg. Specify 0 for the del_for_flag if there is not a + * file to replace. This returns 1 on success and 0 on failure. */ +int atomic_create(struct file_struct *file, char *fname, const char *lnk, + dev_t rdev, stat_x *sxp, int del_for_flag) +{ + char tmpname[MAXPATHLEN]; + const char *create_name; + int skip_atomic, dir_in_the_way = del_for_flag && S_ISDIR(sxp->st.st_mode); + + if (!del_for_flag || dir_in_the_way || tmpdir || !get_tmpname(tmpname, fname, True)) + skip_atomic = 1; + else + skip_atomic = 0; + + if (del_for_flag) { + if (make_backups > 0 && !dir_in_the_way) { + if (!make_backup(fname, skip_atomic)) + return 0; + } else if (skip_atomic) { + int del_opts = delete_mode || force_delete ? DEL_RECURSE : 0; + if (delete_item(fname, sxp->st.st_mode, del_opts | del_for_flag) != 0) + return 0; + } + } + + create_name = skip_atomic ? fname : tmpname; + + if (lnk) { +#ifdef SUPPORT_LINKS + if (S_ISLNK(file->mode) +#ifdef SUPPORT_HARD_LINKS /* The first symlink in a hard-linked cluster is always created. */ + && (!F_IS_HLINKED(file) || file->flags & FLAG_HLINK_FIRST) +#endif + ) { + if (do_symlink(lnk, create_name) < 0) { + rsyserr(FERROR_XFER, errno, "symlink %s -> \"%s\" failed", + full_fname(create_name), lnk); + return 0; + } + } else +#endif +#ifdef SUPPORT_HARD_LINKS + if (!hard_link_one(file, create_name, lnk, 0)) + return 0; +#endif + } else { + if (do_mknod(create_name, file->mode, rdev) < 0) { + rsyserr(FERROR_XFER, errno, "mknod %s failed", + full_fname(create_name)); + return 0; + } + } + + if (!skip_atomic) { + if (do_rename(tmpname, fname) < 0) { + rsyserr(FERROR_XFER, errno, "rename %s -> \"%s\" failed", + full_fname(tmpname), full_fname(fname)); + do_unlink(tmpname); + return 0; + } + } + + return 1; +} + #ifdef SUPPORT_HARD_LINKS static void handle_skipped_hlink(struct file_struct *file, int itemizing, enum logcode code, int f_out) @@ -1829,6 +1940,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) { @@ -1849,21 +1961,23 @@ 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; if (link_stat(fname, &st, 0) == 0 && cmp_time(st.st_mtime, file->modtime) != 0) - set_modtime(fname, file->modtime, file->mode); + set_modtime(fname, file->modtime, F_MOD_NSEC(file), file->mode); } if (counter >= loopchk_limit) { if (allowed_lull) - maybe_send_keepalive(); + maybe_send_keepalive(time(NULL), MSK_ALLOW_FLUSH); else maybe_flush_socket(0); counter = 0; @@ -1937,9 +2051,12 @@ 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); - maybe_flush_socket(1); + write_ndx(sock_f_out, NDX_DONE); + if (!read_batch && !flist_eof) { + int old_total = 0; + for (flist = first_flist; flist != cur_flist; flist = flist->next) + old_total += flist->used; + maybe_flush_socket(!flist_eof && file_total - old_total < MIN_FILECNT_LOOKAHEAD/2); } if (delete_during == 2 || !dir_tweaking) { @@ -1979,7 +2096,7 @@ 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; + need_retouch_dir_times = preserve_times & PRESERVE_DIR_TIMES; loopchk_limit = allowed_lull ? allowed_lull * 5 : 200; symlink_timeset_failed_flags = ITEM_REPORT_TIME | (protocol_version >= 30 || !am_server ? ITEM_REPORT_TIMEFAIL : 0); @@ -2007,18 +2124,12 @@ void generate_files(int f_out, const char *local_name) : "enabled"); } - /* 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). */ - ignore_timeout = 1; - dflt_perms = (ACCESSPERMS & ~orig_umask); do { #ifdef SUPPORT_HARD_LINKS if (preserve_hard_links && inc_recurse) { - while (!flist_eof && file_total < FILECNT_LOOKAHEAD/2) + while (!flist_eof && file_total < MIN_FILECNT_LOOKAHEAD/2) wait_for_receiver(); } #endif @@ -2066,7 +2177,7 @@ void generate_files(int f_out, const char *local_name) if (i + cur_flist->ndx_start >= next_loopchk) { if (allowed_lull) - maybe_send_keepalive(); + maybe_send_keepalive(time(NULL), MSK_ALLOW_FLUSH); else maybe_flush_socket(0); next_loopchk += loopchk_limit; @@ -2086,9 +2197,6 @@ void generate_files(int f_out, const char *local_name) } } while ((cur_flist = cur_flist->next) != NULL); - if (read_batch && inc_recurse) - write_ndx(f_out, NDX_DONE); - if (delete_during) delete_in_dir(NULL, NULL, &dev_zero); phase++; @@ -2107,12 +2215,18 @@ void generate_files(int f_out, const char *local_name) rprintf(FINFO, "generate_files phase=%d\n", phase); write_ndx(f_out, NDX_DONE); - write_del_stats(f_out); /* Reduce round-trip lag-time for a useless delay-updates phase. */ - if (protocol_version >= 29 && !delay_updates) + if (protocol_version >= 29 && EARLY_DELAY_DONE_MSG()) write_ndx(f_out, NDX_DONE); + if (protocol_version >= 31 && EARLY_DELETE_DONE_MSG()) { + if ((INFO_GTE(STATS, 2) && (delete_mode || force_delete)) || read_batch) + write_del_stats(f_out); + if (EARLY_DELAY_DONE_MSG()) /* Can't send this before delay */ + write_ndx(f_out, NDX_DONE); + } + /* Read MSG_DONE for the redo phase (and any prior messages). */ while (1) { check_for_finished_files(itemizing, code, 0); @@ -2125,8 +2239,11 @@ void generate_files(int f_out, const char *local_name) phase++; if (DEBUG_GTE(GENR, 1)) rprintf(FINFO, "generate_files phase=%d\n", phase); - if (delay_updates) + if (!EARLY_DELAY_DONE_MSG()) { write_ndx(f_out, NDX_DONE); + if (protocol_version >= 31 && EARLY_DELETE_DONE_MSG()) + write_ndx(f_out, NDX_DONE); + } /* Read MSG_DONE for delay-updates phase & prior messages. */ while (msgdone_cnt == 2) wait_for_receiver(); @@ -2140,10 +2257,6 @@ void generate_files(int f_out, const char *local_name) if (delete_after && !solo_file && file_total > 0) do_delete_pass(); - if ((need_retouch_dir_perms || need_retouch_dir_times) - && dir_tweaking && (!inc_recurse || delete_during == 2)) - touch_up_dirs(dir_flist, -1); - if (max_delete >= 0 && skipped_deletes) { rprintf(FWARNING, "Deletions stopped due to --max-delete limit (%d skipped)\n", @@ -2151,6 +2264,22 @@ void generate_files(int f_out, const char *local_name) io_error |= IOERR_DEL_LIMIT; } + if (protocol_version >= 31) { + if (!EARLY_DELETE_DONE_MSG()) { + if (INFO_GTE(STATS, 2) || read_batch) + write_del_stats(f_out); + write_ndx(f_out, NDX_DONE); + } + + /* Read MSG_DONE for late-delete phase & prior messages. */ + while (msgdone_cnt == 3) + wait_for_receiver(); + } + + if ((need_retouch_dir_perms || need_retouch_dir_times) + && dir_tweaking && (!inc_recurse || delete_during == 2)) + touch_up_dirs(dir_flist, -1); + if (DEBUG_GTE(GENR, 1)) rprintf(FINFO, "generate_files finished\n"); }