X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/342bfb5e23ebb372e0b1230f8124a4312f8a66d8..0d5ebab1d6df5169c5834c02c978fd5bb67421ca:/flist.c diff --git a/flist.c b/flist.c index fe6d0b91..7208efa4 100644 --- a/flist.c +++ b/flist.c @@ -4,7 +4,7 @@ * Copyright (C) 1996 Andrew Tridgell * Copyright (C) 1996 Paul Mackerras * Copyright (C) 2001, 2002 Martin Pool - * Copyright (C) 2002-2008 Wayne Davison + * Copyright (C) 2002-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 @@ -23,6 +23,7 @@ #include "rsync.h" #include "ifuncs.h" #include "rounding.h" +#include "inums.h" #include "io.h" extern int am_root; @@ -50,6 +51,7 @@ extern int preserve_links; extern int preserve_hard_links; extern int preserve_devices; extern int preserve_specials; +extern int missing_args; extern int uid_ndx; extern int gid_ndx; extern int eol_nulls; @@ -65,18 +67,20 @@ extern int protocol_version; extern int sanitize_paths; extern int munge_symlinks; extern int need_unsorted_flist; +extern int sender_symlink_iconv; extern int output_needs_newline; extern int sender_keeps_checksum; extern int unsort_ndx; extern struct stats stats; extern char *filesfrom_host; +extern char *usermap, *groupmap; extern char curr_dir[MAXPATHLEN]; extern struct chmod_mode_struct *chmod_modes; -extern struct filter_list_struct filter_list; -extern struct filter_list_struct daemon_filter_list; +extern filter_rule_list filter_list; +extern filter_rule_list daemon_filter_list; #ifdef ICONV_OPTION extern int filesfrom_convert; @@ -122,7 +126,6 @@ static char tmp_sum[MAX_DIGEST_LEN]; static char empty_sum[MAX_DIGEST_LEN]; static int flist_count_offset; /* for --delete --progress */ -static int dir_count = 0; static void flist_sort_and_clean(struct file_list *flist, int strip_root); static void output_flist(struct file_list *flist); @@ -320,7 +323,7 @@ static void flist_expand(struct file_list *flist, int extra) if (DEBUG_GTE(FLIST, 1) && flist->malloced != FLIST_START) { rprintf(FCLIENT, "[%s] expand file_list pointer array to %s bytes, did%s move\n", who_am_i(), - big_num(sizeof flist->files[0] * flist->malloced, 0), + big_num(sizeof flist->files[0] * flist->malloced), (new_ptr == flist->files) ? " not" : ""); } @@ -387,7 +390,11 @@ int change_pathname(struct file_struct *file, const char *dir, int dirlen) return 1; } -static void send_file_entry(int f, const char *fname, struct file_struct *file, int ndx, int first_ndx) +static void send_file_entry(int f, const char *fname, struct file_struct *file, +#ifdef SUPPORT_LINKS + const char *symlink_name, int symlink_len, +#endif + int ndx, int first_ndx) { static time_t modtime; static mode_t mode; @@ -406,25 +413,36 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file, int l1, l2; int xflags; - /* Initialize starting value of xflags. */ - if (protocol_version >= 30 && S_ISDIR(file->mode)) { - dir_count++; - if (file->flags & FLAG_CONTENT_DIR) - xflags = file->flags & FLAG_TOP_DIR; - else if (file->flags & FLAG_IMPLIED_DIR) - xflags = XMIT_TOP_DIR | XMIT_NO_CONTENT_DIR; - else - xflags = XMIT_NO_CONTENT_DIR; - } else - xflags = file->flags & FLAG_TOP_DIR; /* FLAG_TOP_DIR == XMIT_TOP_DIR */ + /* Initialize starting value of xflags and adjust counts. */ + if (S_ISREG(file->mode)) + xflags = 0; + else if (S_ISDIR(file->mode)) { + stats.num_dirs++; + if (protocol_version >= 30) { + if (file->flags & FLAG_CONTENT_DIR) + xflags = file->flags & FLAG_TOP_DIR; + else if (file->flags & FLAG_IMPLIED_DIR) + xflags = XMIT_TOP_DIR | XMIT_NO_CONTENT_DIR; + else + xflags = XMIT_NO_CONTENT_DIR; + } else + xflags = file->flags & FLAG_TOP_DIR; /* FLAG_TOP_DIR == XMIT_TOP_DIR */ + } else { + if (S_ISLNK(file->mode)) + stats.num_symlinks++; + else if (IS_DEVICE(file->mode)) + stats.num_devices++; + else if (IS_SPECIAL(file->mode)) + stats.num_specials++; + xflags = 0; + } if (file->mode == mode) xflags |= XMIT_SAME_MODE; else mode = file->mode; - if ((preserve_devices && IS_DEVICE(mode)) - || (preserve_specials && IS_SPECIAL(mode))) { + if (preserve_devices && IS_DEVICE(mode)) { if (protocol_version < 28) { if (tmp_rdev == rdev) xflags |= XMIT_SAME_RDEV_pre28; @@ -439,6 +457,17 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file, if (protocol_version < 30 && (uint32)minor(rdev) <= 0xFFu) xflags |= XMIT_RDEV_MINOR_8_pre30; } + } else if (preserve_specials && IS_SPECIAL(mode) && protocol_version < 31) { + /* Special files don't need an rdev number, so just make + * the historical transmission of the value efficient. */ + if (protocol_version < 28) + xflags |= XMIT_SAME_RDEV_pre28; + else { + rdev = MAKEDEV(major(rdev), 0); + xflags |= XMIT_SAME_RDEV_MAJOR; + if (protocol_version < 30) + xflags |= XMIT_RDEV_MINOR_8_pre30; + } } else if (protocol_version < 28) rdev = MAKEDEV(0, 0); if (!preserve_uid || ((uid_t)F_OWNER(file) == uid && *lastname)) @@ -483,7 +512,7 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file, } else if (DEBUG_GTE(HLINK, 3)) { rprintf(FINFO, "[%s] dev:inode for #%d is %s:%s\n", who_am_i(), first_ndx + ndx, - big_num(tmp_dev, 0), big_num(tmp_ino, 0)); + big_num(tmp_dev), big_num(tmp_ino)); } } } else { @@ -574,7 +603,7 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file, } } if ((preserve_devices && IS_DEVICE(mode)) - || (preserve_specials && IS_SPECIAL(mode))) { + || (preserve_specials && IS_SPECIAL(mode) && protocol_version < 31)) { if (protocol_version < 28) { if (!(xflags & XMIT_SAME_RDEV_pre28)) write_int(f, (int)rdev); @@ -591,11 +620,9 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file, } #ifdef SUPPORT_LINKS - if (preserve_links && S_ISLNK(mode)) { - const char *sl = F_SYMLINK(file); - int len = strlen(sl); - write_varint30(f, len); - write_buf(f, sl, len); + if (symlink_len) { + write_varint30(f, symlink_len); + write_buf(f, symlink_name, symlink_len); } #endif @@ -693,12 +720,12 @@ static struct file_struct *recv_file_entry(struct file_list *flist, if (iconvbufs(ic_recv, &inbuf, &outbuf, 0) < 0) { io_error |= IOERR_GENERAL; - rprintf(FERROR_XFER, + rprintf(FERROR_UTF8, "[%s] cannot convert filename: %s (%s)\n", who_am_i(), lastname, strerror(errno)); outbuf.len = 0; } - outbuf.buf[outbuf.len] = '\0'; + thisname[outbuf.len] = '\0'; } #endif @@ -745,8 +772,7 @@ static struct file_struct *recv_file_entry(struct file_list *flist, uid = F_OWNER(first); if (preserve_gid) gid = F_GROUP(first); - if ((preserve_devices && IS_DEVICE(mode)) - || (preserve_specials && IS_SPECIAL(mode))) { + if (preserve_devices && IS_DEVICE(mode)) { uint32 *devp = F_RDEV_P(first); rdev = MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp)); extra_len += DEV_EXTRA_CNT * EXTRA_LEN; @@ -777,7 +803,7 @@ static struct file_struct *recv_file_entry(struct file_list *flist, if (!(xflags & XMIT_SAME_MODE)) mode = from_wire_mode(read_int(f)); - if (chmod_modes && !S_ISLNK(mode)) + if (chmod_modes && !S_ISLNK(mode) && mode) mode = tweak_mode(mode, chmod_modes); if (preserve_uid && !(xflags & XMIT_SAME_UID)) { @@ -787,7 +813,7 @@ static struct file_struct *recv_file_entry(struct file_list *flist, uid = (uid_t)read_varint(f); if (xflags & XMIT_USER_NAME_FOLLOWS) uid = recv_user_name(f, uid); - else if (inc_recurse && am_root && !numeric_ids) + else if (inc_recurse && am_root && (!numeric_ids || usermap)) uid = match_uid(uid); } } @@ -799,13 +825,13 @@ static struct file_struct *recv_file_entry(struct file_list *flist, gid_flags = 0; if (xflags & XMIT_GROUP_NAME_FOLLOWS) gid = recv_group_name(f, gid, &gid_flags); - else if (inc_recurse && (!am_root || !numeric_ids)) + else if (inc_recurse && (!am_root || !numeric_ids || groupmap)) gid = match_gid(gid, &gid_flags); } } if ((preserve_devices && IS_DEVICE(mode)) - || (preserve_specials && IS_SPECIAL(mode))) { + || (preserve_specials && IS_SPECIAL(mode) && protocol_version < 31)) { if (protocol_version < 28) { if (!(xflags & XMIT_SAME_RDEV_pre28)) rdev = (dev_t)read_int(f); @@ -821,7 +847,8 @@ static struct file_struct *recv_file_entry(struct file_list *flist, rdev_minor = read_int(f); rdev = MAKEDEV(rdev_major, rdev_minor); } - extra_len += DEV_EXTRA_CNT * EXTRA_LEN; + if (IS_DEVICE(mode)) + extra_len += DEV_EXTRA_CNT * EXTRA_LEN; file_length = 0; } else if (protocol_version < 28) rdev = MAKEDEV(0, 0); @@ -834,6 +861,13 @@ static struct file_struct *recv_file_entry(struct file_list *flist, linkname_len - 1); overflow_exit("recv_file_entry"); } +#ifdef ICONV_OPTION + /* We don't know how much extra room we need to convert + * the as-yet-unread symlink data, so let's hope that a + * double-size buffer is plenty. */ + if (sender_symlink_iconv) + linkname_len *= 2; +#endif if (munge_symlinks) linkname_len += SYMLINK_PREFIX_LEN; } @@ -955,8 +989,7 @@ static struct file_struct *recv_file_entry(struct file_list *flist, } } - if ((preserve_devices && IS_DEVICE(mode)) - || (preserve_specials && IS_SPECIAL(mode))) { + if (preserve_devices && IS_DEVICE(mode)) { uint32 *devp = F_RDEV_P(file); DEV_MAJOR(devp) = major(rdev); DEV_MINOR(devp) = minor(rdev); @@ -968,14 +1001,40 @@ static struct file_struct *recv_file_entry(struct file_list *flist, if (first_hlink_ndx >= flist->ndx_start) { struct file_struct *first = flist->files[first_hlink_ndx - flist->ndx_start]; memcpy(bp, F_SYMLINK(first), linkname_len); - } else if (munge_symlinks) { - strlcpy(bp, SYMLINK_PREFIX, linkname_len); - bp += SYMLINK_PREFIX_LEN; - linkname_len -= SYMLINK_PREFIX_LEN; - read_sbuf(f, bp, linkname_len - 1); } else { - read_sbuf(f, bp, linkname_len - 1); - if (sanitize_paths) + if (munge_symlinks) { + strlcpy(bp, SYMLINK_PREFIX, linkname_len); + bp += SYMLINK_PREFIX_LEN; + linkname_len -= SYMLINK_PREFIX_LEN; + } +#ifdef ICONV_OPTION + if (sender_symlink_iconv) { + xbuf outbuf, inbuf; + + alloc_len = linkname_len; + linkname_len /= 2; + + /* Read the symlink data into the end of our double-sized + * buffer and then convert it into the right spot. */ + INIT_XBUF(inbuf, bp + alloc_len - linkname_len, + linkname_len - 1, (size_t)-1); + read_sbuf(f, inbuf.buf, inbuf.len); + INIT_XBUF(outbuf, bp, 0, alloc_len); + + if (iconvbufs(ic_recv, &inbuf, &outbuf, 0) < 0) { + io_error |= IOERR_GENERAL; + rprintf(FERROR_XFER, + "[%s] cannot convert symlink data for: %s (%s)\n", + who_am_i(), full_fname(thisname), strerror(errno)); + bp = (char*)file->basename; + *bp++ = '\0'; + outbuf.len = 0; + } + bp[outbuf.len] = '\0'; + } else +#endif + read_sbuf(f, bp, linkname_len - 1); + if (sanitize_paths && !munge_symlinks && *bp) sanitize_path(bp, bp, "", lastdir_depth, SP_DEFAULT); } } @@ -1075,8 +1134,10 @@ struct file_struct *make_file(const char *fname, struct file_list *flist, if (sanitize_paths) sanitize_path(thisname, thisname, "", 0, SP_DEFAULT); - if (stp && S_ISDIR(stp->st_mode)) { - st = *stp; /* Needed for "symlink/." with --relative. */ + if (stp && (S_ISDIR(stp->st_mode) || stp->st_mode == 0)) { + /* This is needed to handle a "symlink/." with a --relative + * dir, or a request to delete a specific file. */ + st = *stp; *linkname = '\0'; /* make IBM code checker happy */ } else if (readlink_stat(thisname, &st, linkname) != 0) { int save_errno = errno; @@ -1113,10 +1174,15 @@ struct file_struct *make_file(const char *fname, struct file_list *flist, } } else { io_error |= IOERR_GENERAL; - rsyserr(FERROR_XFER, save_errno, "readlink %s failed", + rsyserr(FERROR_XFER, save_errno, "readlink_stat(%s) failed", full_fname(thisname)); } return NULL; + } else if (st.st_mode == 0) { + io_error |= IOERR_GENERAL; + rprintf(FINFO, "skipping file with bogus (zero) st_mode: %s\n", + full_fname(thisname)); + return NULL; } if (filter_level == NO_FILTERS) @@ -1250,10 +1316,11 @@ struct file_struct *make_file(const char *fname, struct file_list *flist, #endif #ifdef HAVE_STRUCT_STAT_ST_RDEV - if (IS_DEVICE(st.st_mode) || IS_SPECIAL(st.st_mode)) { + if (IS_DEVICE(st.st_mode)) { tmp_rdev = st.st_rdev; st.st_size = 0; - } + } else if (IS_SPECIAL(st.st_mode)) + st.st_size = 0; #endif file->flags = flags; @@ -1284,25 +1351,6 @@ struct file_struct *make_file(const char *fname, struct file_list *flist, else if (!pool) F_DEPTH(file) = extra_len / EXTRA_LEN; - /* This code is only used by the receiver when it is building - * a list of files for a delete pass. */ - if (keep_dirlinks && linkname_len && flist) { - STRUCT_STAT st2; - int save_mode = file->mode; - file->mode = S_IFDIR; /* Find a directory with our name. */ - if (flist_find(dir_flist, file) >= 0 - && x_stat(thisname, &st2, NULL) == 0 && S_ISDIR(st2.st_mode)) { - file->modtime = st2.st_mtime; - file->len32 = 0; - file->mode = st2.st_mode; - if (uid_ndx) - F_OWNER(file) = st2.st_uid; - if (gid_ndx) - F_GROUP(file) = st2.st_gid; - } else - file->mode = save_mode; - } - if (basename_len == 0+1) { if (!pool) unmake_file(file); @@ -1313,7 +1361,7 @@ struct file_struct *make_file(const char *fname, struct file_list *flist, memcpy(F_SUM(file), tmp_sum, checksum_len); if (unsort_ndx) - F_NDX(file) = dir_count; + F_NDX(file) = stats.num_dirs; return file; } @@ -1334,13 +1382,39 @@ static struct file_struct *send_file_name(int f, struct file_list *flist, if (!file) return NULL; - if (chmod_modes && !S_ISLNK(file->mode)) + if (chmod_modes && !S_ISLNK(file->mode) && file->mode) file->mode = tweak_mode(file->mode, chmod_modes); if (f >= 0) { char fbuf[MAXPATHLEN]; +#ifdef SUPPORT_LINKS + const char *symlink_name; + int symlink_len; +#ifdef ICONV_OPTION + char symlink_buf[MAXPATHLEN]; +#endif +#endif #if defined SUPPORT_ACLS || defined SUPPORT_XATTRS stat_x sx; + init_stat_x(&sx); +#endif + +#ifdef SUPPORT_LINKS + if (preserve_links && S_ISLNK(file->mode)) { + symlink_name = F_SYMLINK(file); + symlink_len = strlen(symlink_name); + if (symlink_len == 0) { + io_error |= IOERR_GENERAL; + f_name(file, fbuf); + rprintf(FERROR_XFER, + "skipping symlink with 0-length value: %s\n", + full_fname(fbuf)); + return NULL; + } + } else { + symlink_name = NULL; + symlink_len = 0; + } #endif #ifdef ICONV_OPTION @@ -1355,7 +1429,7 @@ static struct file_struct *send_file_name(int f, struct file_list *flist, if (iconvbufs(ic_send, &inbuf, &outbuf, 0) < 0) goto convert_error; outbuf.size += 2; - outbuf.buf[outbuf.len++] = '/'; + fbuf[outbuf.len++] = '/'; } INIT_XBUF_STRLEN(inbuf, (char*)file->basename); @@ -1367,7 +1441,26 @@ static struct file_struct *send_file_name(int f, struct file_list *flist, who_am_i(), f_name(file, fbuf), strerror(errno)); return NULL; } - outbuf.buf[outbuf.len] = '\0'; + fbuf[outbuf.len] = '\0'; + +#ifdef SUPPORT_LINKS + if (symlink_len && sender_symlink_iconv) { + INIT_XBUF(inbuf, (char*)symlink_name, symlink_len, (size_t)-1); + INIT_CONST_XBUF(outbuf, symlink_buf); + if (iconvbufs(ic_send, &inbuf, &outbuf, 0) < 0) { + io_error |= IOERR_GENERAL; + f_name(file, fbuf); + rprintf(FERROR_XFER, + "[%s] cannot convert symlink data for: %s (%s)\n", + who_am_i(), full_fname(fbuf), strerror(errno)); + return NULL; + } + symlink_buf[outbuf.len] = '\0'; + + symlink_name = symlink_buf; + symlink_len = outbuf.len; + } +#endif } else #endif f_name(file, fbuf); @@ -1375,7 +1468,6 @@ static struct file_struct *send_file_name(int f, struct file_list *flist, #ifdef SUPPORT_ACLS if (preserve_acls && !S_ISLNK(file->mode)) { sx.st.st_mode = file->mode; - sx.acc_acl = sx.def_acl = NULL; if (get_acl(fname, &sx) < 0) { io_error |= IOERR_GENERAL; return NULL; @@ -1384,7 +1476,7 @@ static struct file_struct *send_file_name(int f, struct file_list *flist, #endif #ifdef SUPPORT_XATTRS if (preserve_xattrs) { - sx.xattr = NULL; + sx.st.st_mode = file->mode; if (get_xattr(fname, &sx) < 0) { io_error |= IOERR_GENERAL; return NULL; @@ -1392,7 +1484,11 @@ static struct file_struct *send_file_name(int f, struct file_list *flist, } #endif - send_file_entry(f, fbuf, file, flist->used, flist->ndx_start); + send_file_entry(f, fbuf, file, +#ifdef SUPPORT_LINKS + symlink_name, symlink_len, +#endif + flist->used, flist->ndx_start); #ifdef SUPPORT_ACLS if (preserve_acls && !S_ISLNK(file->mode)) { @@ -1550,6 +1646,19 @@ static void add_dirs_to_tree(int parent_ndx, struct file_list *from_flist, DIR_NEXT_SIBLING(dp) = -1; } +static void interpret_stat_error(const char *fname, int is_dir) +{ + if (errno == ENOENT) { + io_error |= IOERR_VANISHED; + rprintf(FWARNING, "%s has vanished: %s\n", + is_dir ? "directory" : "file", full_fname(fname)); + } else { + io_error |= IOERR_GENERAL; + rsyserr(FERROR_XFER, errno, "link_stat %s failed", + full_fname(fname)); + } +} + /* This function is normally called by the sender, but the receiving side also * calls it from get_dirlist() with f set to -1 so that we just construct the * file list in memory without sending it over the wire. Also, get_dirlist() @@ -1569,6 +1678,11 @@ static void send_directory(int f, struct file_list *flist, char *fbuf, int len, assert(flist != NULL); if (!(d = opendir(fbuf))) { + if (errno == ENOENT) { + if (am_sender) /* Can abuse this for vanished error w/ENOENT: */ + interpret_stat_error(fbuf, True); + return; + } io_error |= IOERR_GENERAL; rsyserr(FERROR_XFER, errno, "opendir %s failed", full_fname(fbuf)); return; @@ -1630,7 +1744,7 @@ static void send_implied_dirs(int f, struct file_list *flist, char *fname, item_list *relname_list; relnamecache **rnpp; int len, need_new_dir, depth = 0; - struct filter_list_struct save_filter_list = filter_list; + filter_rule_list save_filter_list = filter_list; flags = (flags | FLAG_IMPLIED_DIR) & ~(FLAG_TOP_DIR | FLAG_CONTENT_DIR); filter_list.head = filter_list.tail = NULL; /* Don't filter implied dirs. */ @@ -1740,9 +1854,7 @@ static void send1extra(int f, struct file_struct *file, struct file_list *flist) if (one_file_system) { STRUCT_STAT st; if (link_stat(fbuf, &st, copy_dirlinks) != 0) { - io_error |= IOERR_GENERAL; - rsyserr(FERROR_XFER, errno, "link_stat %s failed", - full_fname(fbuf)); + interpret_stat_error(fbuf, True); return; } filesystem_dev = st.st_dev; @@ -1777,9 +1889,7 @@ static void send1extra(int f, struct file_struct *file, struct file_list *flist) if (name_type != NORMAL_NAME) { STRUCT_STAT st; if (link_stat(fbuf, &st, 1) != 0) { - io_error |= IOERR_GENERAL; - rsyserr(FERROR_XFER, errno, "link_stat %s failed", - full_fname(fbuf)); + interpret_stat_error(fbuf, True); continue; } send_file_name(f, flist, fbuf, &st, FLAG_TOP_DIR | flags, ALL_FILTERS); @@ -1807,7 +1917,7 @@ void send_extra_file_list(int f, int at_least) old_cnt += flist->used; while (file_total - old_cnt < at_least) { struct file_struct *file = dir_flist->sorted[send_dir_ndx]; - int dir_ndx, dstart = dir_count; + int dir_ndx, dstart = stats.num_dirs; const char *pathname = F_PATHNAME(file); int32 *dp; @@ -1840,7 +1950,12 @@ void send_extra_file_list(int f, int at_least) dp = F_DIR_NODE_P(file); } - write_byte(f, 0); + if (protocol_version < 31 || io_error == save_io_error || ignore_errors) + write_byte(f, 0); + else { + write_shortint(f, XMIT_EXTENDED_FLAGS|XMIT_IO_ERROR_ENDLIST); + write_varint(f, io_error); + } if (need_unsorted_flist) { if (!(flist->sorted = new_array(struct file_struct *, flist->used))) @@ -1852,7 +1967,7 @@ void send_extra_file_list(int f, int at_least) flist_sort_and_clean(flist, 0); - add_dirs_to_tree(send_dir_ndx, flist, dir_count - dstart); + add_dirs_to_tree(send_dir_ndx, flist, stats.num_dirs - dstart); flist_done_allocating(flist); file_total += flist->used; @@ -1881,7 +1996,7 @@ void send_extra_file_list(int f, int at_least) } finish: - if (io_error != save_io_error && !ignore_errors) + if (io_error != save_io_error && protocol_version == 30 && !ignore_errors) send_msg_int(MSG_IO_ERROR, io_error); } @@ -1915,9 +2030,6 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) start_write = stats.total_written; gettimeofday(&start_tv, NULL); - if (!orig_dir) - orig_dir = strdup(curr_dir); - if (relative_paths && protocol_version >= 30) implied_dirs = 1; /* We send flagged implied dirs */ @@ -1943,6 +2055,9 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) use_ff_fd = 1; } + if (!orig_dir) + orig_dir = strdup(curr_dir); + while (1) { char fbuf[MAXPATHLEN], *fn, name_type; @@ -2073,14 +2188,26 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) if (link_stat(fbuf, &st, copy_dirlinks || name_type != NORMAL_NAME) != 0 || (name_type != DOTDIR_NAME && is_daemon_excluded(fbuf, S_ISDIR(st.st_mode))) || (relative_paths && path_is_daemon_excluded(fbuf, 1))) { - io_error |= IOERR_GENERAL; - rsyserr(FERROR_XFER, errno, "link_stat %s failed", - full_fname(fbuf)); - continue; + if (errno != ENOENT || missing_args == 0) { + /* This is a transfer error, but inhibit deletion + * only if we might be omitting an existing file. */ + if (errno != ENOENT) + io_error |= IOERR_GENERAL; + rsyserr(FERROR_XFER, errno, "link_stat %s failed", + full_fname(fbuf)); + continue; + } else if (missing_args == 1) { + /* Just ignore the arg. */ + continue; + } else /* (missing_args == 2) */ { + /* Send the arg as a "missing" entry with + * mode 0, which tells the generator to delete it. */ + memset(&st, 0, sizeof st); + } } /* A dot-dir should not be excluded! */ - if (name_type != DOTDIR_NAME + if (name_type != DOTDIR_NAME && st.st_mode != 0 && is_excluded(fbuf, S_ISDIR(st.st_mode) != 0, ALL_FILTERS)) continue; @@ -2137,7 +2264,13 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) stats.flist_buildtime = 1; start_tv = end_tv; - write_byte(f, 0); /* Indicate end of file list */ + /* Indicate end of file list */ + if (protocol_version < 31 || io_error == 0 || ignore_errors) + write_byte(f, 0); + else { + write_shortint(f, XMIT_EXTENDED_FLAGS|XMIT_IO_ERROR_ENDLIST); + write_varint(f, io_error); + } #ifdef SUPPORT_HARD_LINKS if (preserve_hard_links && protocol_version >= 30 && !inc_recurse) @@ -2172,10 +2305,12 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) if (numeric_ids <= 0 && !inc_recurse) send_id_list(f); + set_msg_fd_in(-1); + /* send the io_error flag */ if (protocol_version < 30) write_int(f, ignore_errors ? 0 : io_error); - else if (io_error && !ignore_errors) + else if (io_error && protocol_version == 30 && !ignore_errors) send_msg_int(MSG_IO_ERROR, io_error); if (disable_buffering) @@ -2192,8 +2327,8 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) if (inc_recurse) { send_dir_depth = 1; - add_dirs_to_tree(-1, flist, dir_count); - if (!file_total || strcmp(flist->sorted[0]->basename, ".") != 0) + add_dirs_to_tree(-1, flist, stats.num_dirs); + if (!file_total || strcmp(flist->sorted[flist->low]->basename, ".") != 0) flist->parent_ndx = -1; flist_done_allocating(flist); if (send_dir_ndx < 0) { @@ -2206,7 +2341,8 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) * file-list to check if this is a 1-file xfer. */ send_extra_file_list(f, 1); } - } + } else + flist_eof = 1; return flist; } @@ -2223,6 +2359,10 @@ struct file_list *recv_file_list(int f) else if (inc_recurse && INFO_GTE(FLIST, 1) && !am_server) rprintf(FCLIENT, "receiving incremental file list\n"); rprintf(FLOG, "receiving file list\n"); + if (usermap) + parse_name_map(usermap, True); + if (groupmap) + parse_name_map(groupmap, False); } start_read = stats.total_read; @@ -2246,24 +2386,46 @@ struct file_list *recv_file_list(int f) while ((flags = read_byte(f)) != 0) { struct file_struct *file; - flist_expand(flist, 1); - if (protocol_version >= 28 && (flags & XMIT_EXTENDED_FLAGS)) flags |= read_byte(f) << 8; - file = recv_file_entry(flist, flags, f); - if (inc_recurse && S_ISDIR(file->mode)) { - flist_expand(dir_flist, 1); - dir_flist->files[dir_flist->used++] = file; + if (flags == (XMIT_EXTENDED_FLAGS|XMIT_IO_ERROR_ENDLIST)) { + int err; + if (protocol_version < 31) { + rprintf(FERROR, "Invalid flist flag: %x\n", flags); + exit_cleanup(RERR_PROTOCOL); + } + err = read_varint(f); + if (!ignore_errors) + io_error |= err; + break; } + flist_expand(flist, 1); + file = recv_file_entry(flist, flags, f); + + if (S_ISREG(file->mode)) { + /* Already counted */ + } else if (S_ISDIR(file->mode)) { + if (inc_recurse) { + flist_expand(dir_flist, 1); + dir_flist->files[dir_flist->used++] = file; + } + stats.num_dirs++; + } else if (S_ISLNK(file->mode)) + stats.num_symlinks++; + else if (IS_DEVICE(file->mode)) + stats.num_symlinks++; + else + stats.num_specials++; + flist->files[flist->used++] = file; maybe_emit_filelist_progress(flist->used); if (DEBUG_GTE(FLIST, 2)) { - rprintf(FINFO, "recv_file_name(%s)\n", - f_name(file, NULL)); + char *name = f_name(file, NULL); + rprintf(FINFO, "recv_file_name(%s)\n", NS(name)); } } file_total += flist->used; @@ -2306,19 +2468,20 @@ struct file_list *recv_file_list(int f) if (inc_recurse) flist_done_allocating(flist); - else if (f >= 0) + else if (f >= 0) { recv_id_list(f, flist); + flist_eof = 1; + } flist_sort_and_clean(flist, relative_paths); if (protocol_version < 30) { /* Recv the io_error flag */ - if (ignore_errors) - read_int(f); - else - io_error |= read_int(f); + int err = read_int(f); + if (!ignore_errors) + io_error |= err; } else if (inc_recurse && flist->ndx_start == 1) { - if (!file_total || strcmp(flist->sorted[0]->basename, ".") != 0) + if (!file_total || strcmp(flist->sorted[flist->low]->basename, ".") != 0) flist->parent_ndx = -1; } @@ -2413,6 +2576,28 @@ int flist_find(struct file_list *flist, struct file_struct *f) return -1; } +/* Search for an identically-named item in the file list. Differs from + * flist_find in that an item that agrees with "f" in directory-ness is + * preferred but one that does not is still found. */ +int flist_find_ignore_dirness(struct file_list *flist, struct file_struct *f) +{ + mode_t save_mode; + int ndx; + + /* First look for an item that agrees in directory-ness. */ + ndx = flist_find(flist, f); + if (ndx >= 0) + return ndx; + + /* Temporarily flip f->mode to look for an item of opposite + * directory-ness. */ + save_mode = f->mode; + f->mode = S_ISDIR(f->mode) ? S_IFREG : S_IFDIR; + ndx = flist_find(flist, f); + f->mode = save_mode; + return ndx; +} + /* * Free up any resources a file_struct has allocated * and clear the file. @@ -2721,7 +2906,7 @@ static void output_flist(struct file_list *flist) "[%s] i=%d %s %s%s%s%s mode=0%o len=%s%s%s flags=%x\n", who, i + flist->ndx_start, root, dir, slash, name, trail, - (int)file->mode, big_num(F_LENGTH(file), 0), + (int)file->mode, comma_num(F_LENGTH(file)), uidbuf, gidbuf, file->flags); } } @@ -2932,7 +3117,7 @@ struct file_list *get_dirlist(char *dirname, int dlen, int ignore_filter_rules) recurse = 0; xfer_dirs = 1; - send_directory(ignore_filter_rules ? -2 : -1, dirlist, dirname, dlen, 0); + send_directory(ignore_filter_rules ? -2 : -1, dirlist, dirname, dlen, FLAG_CONTENT_DIR); xfer_dirs = save_xfer_dirs; recurse = save_recurse; if (INFO_GTE(PROGRESS, 1))