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;
extern struct filter_list_struct filter_list;
extern struct filter_list_struct daemon_filter_list;
+extern struct filter_struct *last_hit_filter;
#ifdef ICONV_OPTION
extern int filesfrom_convert;
/* This function is used to check if a file should be included/excluded
* from the list of files based on its name and type etc. The value of
- * filter_level is set to either SERVER_FILTERS or ALL_FILTERS. */
+ * filter_level is set to either SERVER_FILTERS or ALL_FILTERS.
+ * "last_hit_filter" will be set to the operative filter, or NULL if none. */
static int is_excluded(const char *fname, int is_dir, int filter_level)
{
#if 0 /* This currently never happens, so avoid a useless compare. */
#endif
if (is_daemon_excluded(fname, is_dir))
return 1;
+ /* Don't leave a daemon include in last_hit_filter. */
+ last_hit_filter = NULL;
if (filter_level != ALL_FILTERS)
return 0;
if (filter_list.head
stats.num_symlinks++;
else if (IS_DEVICE(file->mode))
stats.num_devices++;
- else
+ else if (IS_SPECIAL(file->mode))
stats.num_specials++;
xflags = 0;
}
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;
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))
}
}
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);
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;
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)) {
}
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);
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);
}
}
- 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);
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;
/* See if file is excluded before reporting an error. */
- if (filter_level != NO_FILTERS
+ if (filter_level != NO_FILTERS && filter_level != ALL_FILTERS_NO_EXCLUDE
&& (is_excluded(thisname, 0, filter_level)
|| is_excluded(thisname, 1, filter_level))) {
if (ignore_perishable && save_errno != ENOENT)
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)
goto skip_filters;
+ if (filter_level == ALL_FILTERS_NO_EXCLUDE) {
+ /* Call only for the side effect of setting last_hit_filter to
+ * any operative include filter, which might affect attributes. */
+ is_excluded(thisname, S_ISDIR(st.st_mode) != 0, ALL_FILTERS);
+ goto skip_filters;
+ }
if (S_ISDIR(st.st_mode)) {
if (!xfer_dirs) {
#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;
int flags, int filter_level)
{
struct file_struct *file;
+ BOOL can_tweak_mode;
file = make_file(fname, flist, stp, flags, filter_level);
if (!file)
return NULL;
- if (chmod_modes && !S_ISLNK(file->mode))
+ can_tweak_mode = !S_ISLNK(file->mode) && file->mode;
+ if ((filter_level == ALL_FILTERS || filter_level == ALL_FILTERS_NO_EXCLUDE)
+ && last_hit_filter) {
+ if ((last_hit_filter->flags & MATCHFLG_CHMOD) && can_tweak_mode)
+ file->mode = tweak_mode(file->mode, last_hit_filter->chmod->modes);
+ if ((last_hit_filter->flags & MATCHFLG_FORCE_OWNER) && uid_ndx)
+ F_OWNER(file) = last_hit_filter->force_uid;
+ if ((last_hit_filter->flags & MATCHFLG_FORCE_GROUP) && gid_ndx)
+ F_GROUP(file) = last_hit_filter->force_gid;
+ }
+ if (chmod_modes && can_tweak_mode)
file->mode = tweak_mode(file->mode, chmod_modes);
if (f >= 0) {
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;
struct file_struct *file;
file = send_file_name(f, flist, fbuf, &st,
FLAG_TOP_DIR | FLAG_CONTENT_DIR | flags,
- NO_FILTERS);
+ ALL_FILTERS_NO_EXCLUDE);
if (!file)
continue;
if (inc_recurse) {
} else
send_if_directory(f, flist, file, fbuf, len, flags);
} else
- send_file_name(f, flist, fbuf, &st, flags, NO_FILTERS);
+ send_file_name(f, flist, fbuf, &st, flags, ALL_FILTERS_NO_EXCLUDE);
}
gettimeofday(&end_tv, NULL);
* file-list to check if this is a 1-file xfer. */
send_extra_file_list(f, 1);
}
- }
+ } else
+ flist_eof = 1;
return flist;
}
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);
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))