X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/46cd1ef6bc79f5779766d5829681cb2b0a66aa80..a8e6e1486960fe2e9ac190ad53e9830f0f3f900a:/flist.c diff --git a/flist.c b/flist.c index 0fc7851a..2af7e88b 100644 --- a/flist.c +++ b/flist.c @@ -51,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; @@ -431,7 +432,7 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file, stats.num_symlinks++; else if (IS_DEVICE(file->mode)) stats.num_devices++; - else + else if (IS_SPECIAL(file->mode)) stats.num_specials++; xflags = 0; } @@ -441,8 +442,7 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file, 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; @@ -457,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)) @@ -592,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); @@ -761,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; @@ -793,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)) { @@ -821,7 +831,7 @@ static struct file_struct *recv_file_entry(struct file_list *flist, } 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); @@ -837,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); @@ -978,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); @@ -1124,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; @@ -1166,6 +1178,11 @@ struct file_struct *make_file(const char *fname, struct file_list *flist, 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) @@ -1299,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; @@ -1364,7 +1382,7 @@ 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) { @@ -1627,6 +1645,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() @@ -1646,8 +1677,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 (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; @@ -1819,9 +1853,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; @@ -1856,9 +1888,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); @@ -2157,14 +2187,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; @@ -2298,7 +2340,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; } @@ -2424,8 +2467,10 @@ 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); @@ -3071,7 +3116,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))