* Copyright (C) 1996 Andrew Tridgell
* Copyright (C) 1996 Paul Mackerras
* Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org>
- * 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
extern int preserve_hard_links;
extern int preserve_devices;
extern int preserve_specials;
+extern int delete_missing_args;
extern int uid_ndx;
extern int gid_ndx;
extern int eol_nulls;
extern int sanitize_paths;
extern int munge_symlinks;
extern int need_unsorted_flist;
+extern int sender_symlink_iconv;
extern int unsort_ndx;
extern struct stats stats;
extern char *filesfrom_host;
#define NORMAL_NAME 0
#define SLASH_ENDING_NAME 1
-#define DOT_NAME 2
+#define DOTDIR_NAME 2
/* Starting from protocol version 26, we always use 64-bit ino_t and dev_t
* internally, even if this platform does not allow files to have 64-bit inums.
static inline int path_is_daemon_excluded(char *path, int ignore_filename)
{
- if (daemon_filter_list.head && path) {
+ if (daemon_filter_list.head) {
char *slash = path;
while ((slash = strchr(slash+1, '/')) != NULL) {
if (filter_level == NO_FILTERS)
return 0;
#endif
- if (fname) {
- /* never exclude '.', even if somebody does --exclude '*' */
- if (fname[0] == '.' && !fname[1])
- return 0;
- /* Handle the -R version of the '.' dir. */
- if (fname[0] == '/') {
- int len = strlen(fname);
- if (fname[len-1] == '.' && fname[len-2] == '/')
- return 0;
- }
- }
if (is_daemon_excluded(fname, is_dir))
return 1;
if (filter_level != ALL_FILTERS)
static const char *pathname, *orig_dir;
static int pathname_len;
-
/* Make sure flist can hold at least flist->used + extra entries. */
static void flist_expand(struct file_list *flist, int extra)
{
flist->pool_boundary = ptr;
}
-int push_pathname(const char *dir, int len)
+/* Call this with EITHER (1) "file, NULL, 0" to chdir() to the file's
+ * F_PATHNAME(), or (2) "NULL, dir, dirlen" to chdir() to the supplied dir,
+ * with dir == NULL taken to be the starting directory, and dirlen < 0
+ * indicating that strdup(dir) should be called and then the -dirlen length
+ * value checked to ensure that it is not daemon-excluded. */
+int change_pathname(struct file_struct *file, const char *dir, int dirlen)
{
- if (dir == pathname)
- return 1;
+ if (dirlen < 0) {
+ char *cpy = strdup(dir);
+ if (*cpy != '/')
+ change_dir(orig_dir, CD_SKIP_CHDIR);
+ if (path_is_daemon_excluded(cpy, 0))
+ goto chdir_error;
+ dir = cpy;
+ dirlen = -dirlen;
+ } else {
+ if (file) {
+ if (pathname == F_PATHNAME(file))
+ return 1;
+ dir = F_PATHNAME(file);
+ if (dir)
+ dirlen = strlen(dir);
+ } else if (pathname == dir)
+ return 1;
+ if (dir && *dir != '/')
+ change_dir(orig_dir, CD_SKIP_CHDIR);
+ }
- if (!orig_dir)
- orig_dir = strdup(curr_dir);
+ pathname = dir;
+ pathname_len = dirlen;
- if (pathname && !pop_dir(orig_dir)) {
- rsyserr(FERROR, errno, "pop_dir %s failed",
- full_fname(orig_dir));
- exit_cleanup(RERR_FILESELECT);
- }
+ if (!dir)
+ dir = orig_dir;
- if (dir && !push_dir(dir, 0)) {
+ if (!change_dir(dir, CD_NORMAL)) {
+ chdir_error:
io_error |= IOERR_GENERAL;
- rsyserr(FERROR, errno, "push_dir %s failed in %s",
- full_fname(dir), curr_dir);
+ rsyserr(FERROR_XFER, errno, "change_dir %s failed", full_fname(dir));
+ if (dir != orig_dir)
+ change_dir(orig_dir, CD_NORMAL);
+ pathname = NULL;
+ pathname_len = 0;
return 0;
}
- pathname = dir;
- pathname_len = len >= 0 ? len : dir ? (int)strlen(dir) : 0;
-
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;
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)) {
+ /* 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) {
- if ((uid_t)F_OWNER(file) == uid && *lastname)
- xflags |= XMIT_SAME_UID;
- else {
- uid = F_OWNER(file);
- if (!numeric_ids) {
- user_name = add_uid(uid);
- if (inc_recurse && user_name)
- xflags |= XMIT_USER_NAME_FOLLOWS;
- }
+ if (!preserve_uid || ((uid_t)F_OWNER(file) == uid && *lastname))
+ xflags |= XMIT_SAME_UID;
+ else {
+ uid = F_OWNER(file);
+ if (!numeric_ids) {
+ user_name = add_uid(uid);
+ if (inc_recurse && user_name)
+ xflags |= XMIT_USER_NAME_FOLLOWS;
}
}
- if (preserve_gid) {
- if ((gid_t)F_GROUP(file) == gid && *lastname)
- xflags |= XMIT_SAME_GID;
- else {
- gid = F_GROUP(file);
- if (!numeric_ids) {
- group_name = add_gid(gid);
- if (inc_recurse && group_name)
- xflags |= XMIT_GROUP_NAME_FOLLOWS;
- }
+ if (!preserve_gid || ((gid_t)F_GROUP(file) == gid && *lastname))
+ xflags |= XMIT_SAME_GID;
+ else {
+ gid = F_GROUP(file);
+ if (!numeric_ids) {
+ group_name = add_gid(gid);
+ if (inc_recurse && group_name)
+ xflags |= XMIT_GROUP_NAME_FOLLOWS;
}
}
if (file->modtime == modtime)
np->data = (void*)(long)(first_ndx + ndx + 1);
xflags |= XMIT_HLINK_FIRST;
}
- xflags |= XMIT_HLINKED;
} else {
if (tmp_dev == dev) {
if (protocol_version >= 28)
xflags |= XMIT_SAME_DEV_pre30;
} else
dev = tmp_dev;
- xflags |= XMIT_HLINKED;
}
+ xflags |= XMIT_HLINKED;
}
#endif
}
#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
int alloc_len, basename_len, linkname_len;
int extra_len = file_extra_cnt * EXTRA_LEN;
int first_hlink_ndx = -1;
- OFF_T file_length;
+ int64 file_length;
const char *basename;
struct file_struct *file;
alloc_pool_t *pool;
if (iconvbufs(ic_recv, &inbuf, &outbuf, 0) < 0) {
io_error |= IOERR_GENERAL;
- rprintf(FINFO,
+ 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
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)) {
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);
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;
}
if (always_checksum && S_ISREG(mode))
extra_len += SUM_EXTRA_CNT * EXTRA_LEN;
+#if SIZEOF_INT64 >= 8
if (file_length > 0xFFFFFFFFu && S_ISREG(mode))
extra_len += EXTRA_LEN;
+#endif
+ if (file_length < 0) {
+ rprintf(FERROR, "Offset underflow: file-length is negative\n");
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
if (inc_recurse && S_ISDIR(mode)) {
if (one_file_system) {
#endif
file->modtime = (time_t)modtime;
file->len32 = (uint32)file_length;
+#if SIZEOF_INT64 >= 8
if (file_length > 0xFFFFFFFFu && S_ISREG(mode)) {
+#if SIZEOF_CAPITAL_OFF_T < 8
+ rprintf(FERROR, "Offset overflow: attempted 64-bit file-length\n");
+ exit_cleanup(RERR_UNSUPPORTED);
+#else
file->flags |= FLAG_LENGTH64;
OPT_EXTRA(file, 0)->unum = (uint32)(file_length >> 32);
+#endif
}
+#endif
file->mode = mode;
if (preserve_uid)
F_OWNER(file) = uid;
}
}
- 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 (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);
}
}
* and performing extensive checks against global options.
*
* Returns a pointer to the new file struct, or NULL if there was an error
- * or this file should be excluded. */
+ * or this file should be excluded.
+ *
+ * Note: Any error (here or in send_file_name) that results in the omission of
+ * an existent source file from the file list should set
+ * "io_error |= IOERR_GENERAL" to avoid deletion of the file from the
+ * destination if --delete is on. */
struct file_struct *make_file(const char *fname, struct file_list *flist,
STRUCT_STAT *stp, int flags, int filter_level)
{
char *bp;
if (strlcpy(thisname, fname, sizeof thisname) >= sizeof thisname) {
- rprintf(FINFO, "skipping overly long name: %s\n", fname);
+ io_error |= IOERR_GENERAL;
+ rprintf(FERROR_XFER, "skipping overly long name: %s\n", fname);
return NULL;
}
clean_fname(thisname, 0);
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;
}
} 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;
}
- /* backup.c calls us with filter_level set to NO_FILTERS. */
if (filter_level == NO_FILTERS)
goto skip_filters;
linkname_len = 0;
#endif
+#if SIZEOF_CAPITAL_OFF_T >= 8
if (st.st_size > 0xFFFFFFFFu && S_ISREG(st.st_mode))
extra_len += EXTRA_LEN;
+#endif
#if EXTRA_ROUNDING > 0
if (extra_len & (EXTRA_ROUNDING * EXTRA_LEN))
if (protocol_version >= 28
? (!S_ISDIR(st.st_mode) && st.st_nlink > 1)
: S_ISREG(st.st_mode)) {
- tmp_dev = st.st_dev;
- tmp_ino = st.st_ino;
+ tmp_dev = (int64)st.st_dev + 1;
+ tmp_ino = (int64)st.st_ino;
} else
tmp_dev = 0;
}
#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;
file->modtime = st.st_mtime;
file->len32 = (uint32)st.st_size;
+#if SIZEOF_CAPITAL_OFF_T >= 8
if (st.st_size > 0xFFFFFFFFu && S_ISREG(st.st_mode)) {
file->flags |= FLAG_LENGTH64;
OPT_EXTRA(file, 0)->unum = (uint32)(st.st_size >> 32);
}
+#endif
file->mode = st.st_mode;
if (uid_ndx) /* Check uid_ndx instead of preserve_uid for del support */
F_OWNER(file) = st.st_uid;
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);
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;
#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
if (ic_send != (iconv_t)-1) {
xbuf outbuf, inbuf;
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);
if (iconvbufs(ic_send, &inbuf, &outbuf, 0) < 0) {
convert_error:
io_error |= IOERR_GENERAL;
- rprintf(FINFO,
+ rprintf(FERROR_XFER,
"[%s] cannot convert filename: %s (%s)\n",
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);
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)
+ if (get_acl(fname, &sx) < 0) {
+ io_error |= IOERR_GENERAL;
return NULL;
+ }
}
#endif
#ifdef SUPPORT_XATTRS
if (preserve_xattrs) {
sx.xattr = NULL;
- if (get_xattr(fname, &sx) < 0)
+ if (get_xattr(fname, &sx) < 0) {
+ io_error |= IOERR_GENERAL;
return NULL;
+ }
}
#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)) {
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()
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;
continue;
if (strlcpy(p, dname, remainder) >= remainder) {
io_error |= IOERR_GENERAL;
- rprintf(FINFO,
+ rprintf(FERROR_XFER,
"cannot send long-named file %s\n",
full_fname(fbuf));
continue;
}
if (dname[0] == '\0') {
io_error |= IOERR_GENERAL;
- rprintf(FINFO,
+ rprintf(FERROR_XFER,
"cannot send file with empty name in %s\n",
full_fname(fbuf));
continue;
}
}
-static char lastpath[MAXPATHLEN] = "";
-static int lastpath_len = 0;
-static struct file_struct *lastpath_struct;
-
static void send_implied_dirs(int f, struct file_list *flist, char *fname,
char *start, char *limit, int flags, char name_type)
{
+ static char lastpath[MAXPATHLEN] = "";
+ static int lastpath_len = 0;
+ static struct file_struct *lastpath_struct = NULL;
struct file_struct *file;
item_list *relname_list;
relnamecache **rnpp;
- char *slash;
- int len, need_new_dir;
+ int len, need_new_dir, depth = 0;
struct filter_list_struct save_filter_list = filter_list;
flags = (flags | FLAG_IMPLIED_DIR) & ~(FLAG_TOP_DIR | FLAG_CONTENT_DIR);
need_new_dir = 0;
else
need_new_dir = 1;
- } else
+ } else {
+ char *tp = fname, *lp = lastpath;
+ /* Skip any initial directories in our path that we
+ * have in common with lastpath. */
+ assert(start == fname);
+ for ( ; ; tp++, lp++) {
+ if (tp == limit) {
+ if (*lp == '/' || *lp == '\0')
+ goto done;
+ break;
+ }
+ if (*lp != *tp)
+ break;
+ if (*tp == '/') {
+ start = tp;
+ depth++;
+ }
+ }
need_new_dir = 1;
+ }
if (need_new_dir) {
int save_copy_links = copy_links;
int save_xfer_dirs = xfer_dirs;
+ char *slash;
copy_links = xfer_dirs = 1;
for (slash = start; (slash = strchr(slash+1, '/')) != NULL; ) {
*slash = '\0';
- send_file_name(f, flist, fname, NULL, flags, ALL_FILTERS);
+ file = send_file_name(f, flist, fname, NULL, flags, ALL_FILTERS);
+ depth++;
+ if (!inc_recurse && file && S_ISDIR(file->mode))
+ change_local_filter_dir(fname, strlen(fname), depth);
*slash = '/';
}
if (file && !S_ISDIR(file->mode))
file = NULL;
lastpath_struct = file;
- }
+ } else if (file && S_ISDIR(file->mode))
+ change_local_filter_dir(fname, strlen(fname), ++depth);
strlcpy(lastpath, fname, sizeof lastpath);
lastpath_len = limit - fname;
f_name(file, fbuf);
dlen = strlen(fbuf);
- if (F_PATHNAME(file) != pathname) {
- if (!push_pathname(F_PATHNAME(file), -1))
- exit_cleanup(RERR_FILESELECT);
- }
+ if (!change_pathname(file, NULL, 0))
+ exit_cleanup(RERR_FILESELECT);
change_local_filter_dir(fbuf, dlen, send_dir_depth);
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;
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);
disable_buffering = io_start_buffering_out(f);
if (filesfrom_fd >= 0) {
- if (argv[0] && !push_dir(argv[0], 0)) {
- rsyserr(FERROR_XFER, errno, "push_dir %s failed in %s",
- full_fname(argv[0]), curr_dir);
+ if (argv[0] && !change_dir(argv[0], CD_NORMAL)) {
+ rsyserr(FERROR_XFER, errno, "change_dir %s failed",
+ full_fname(argv[0]));
exit_cleanup(RERR_FILESELECT);
}
use_ff_fd = 1;
}
+ if (!orig_dir)
+ orig_dir = strdup(curr_dir);
+
while (1) {
char fbuf[MAXPATHLEN], *fn, name_type;
fbuf[len++] = '.';
fbuf[len] = '\0';
}
- name_type = DOT_NAME;
+ name_type = DOTDIR_NAME;
} else if (len > 1 && fbuf[len-1] == '.' && fbuf[len-2] == '.'
&& (len == 2 || fbuf[len-3] == '/')) {
if (len + 2 >= MAXPATHLEN)
fbuf[len++] = '/';
fbuf[len++] = '.';
fbuf[len] = '\0';
- name_type = DOT_NAME;
+ name_type = DOTDIR_NAME;
} else if (fbuf[len-1] == '.' && (len == 1 || fbuf[len-2] == '/'))
- name_type = DOT_NAME;
+ name_type = DOTDIR_NAME;
else
name_type = NORMAL_NAME;
if (fn[0] == '/') {
fn = "/.";
len = 2;
- name_type = DOT_NAME;
+ name_type = DOTDIR_NAME;
} else if (fn[0] == '.')
- name_type = DOT_NAME;
+ name_type = DOTDIR_NAME;
} else if (fn[len-1] == '/') {
fn[--len] = '\0';
if (len == 1 && *fn == '.')
- name_type = DOT_NAME;
+ name_type = DOTDIR_NAME;
else
name_type = SLASH_ENDING_NAME;
}
if (!*fn) {
len = 1;
fn = ".";
- name_type = DOT_NAME;
+ name_type = DOTDIR_NAME;
}
dirlen = dir ? strlen(dir) : 0;
if (dirlen != lastdir_len || memcmp(lastdir, dir, dirlen) != 0) {
- if (path_is_daemon_excluded(dir, 0)) {
- io_error |= IOERR_GENERAL;
- rsyserr(FERROR, errno, "push_dir %s failed in %s",
- full_fname(dir), curr_dir);
- continue;
- }
- if (!push_pathname(dir ? strdup(dir) : NULL, dirlen))
+ if (!change_pathname(NULL, dir, -dirlen))
continue;
lastdir = pathname;
lastdir_len = pathname_len;
- } else if (!push_pathname(lastdir, lastdir_len))
+ } else if (!change_pathname(NULL, lastdir, lastdir_len))
continue;
if (fn != fbuf)
memmove(fbuf, fn, len + 1);
if (link_stat(fbuf, &st, copy_dirlinks || name_type != NORMAL_NAME) != 0
- || is_daemon_excluded(fbuf, S_ISDIR(st.st_mode) != 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 && delete_missing_args) {
+ /* Rsync will treat a mode of 0 as deleted. */
+ memset(&st, 0, sizeof st);
+ } else {
+ io_error |= IOERR_GENERAL;
+ rsyserr(FERROR_XFER, errno, "link_stat %s failed",
+ full_fname(fbuf));
+ continue;
+ }
}
+ /* A dot-dir should not be excluded! */
+ if (name_type != DOTDIR_NAME && st.st_mode != 0
+ && is_excluded(fbuf, S_ISDIR(st.st_mode) != 0, ALL_FILTERS))
+ continue;
+
if (S_ISDIR(st.st_mode) && !xfer_dirs) {
rprintf(FINFO, "skipping directory %s\n", fbuf);
continue;
} else if (implied_dirs && (p=strrchr(fbuf,'/')) && p != fbuf) {
/* Send the implied directories at the start of the
* source spec, so we get their permissions right. */
- char *lp = lastpath, *slash = fbuf;
- *p = '\0';
- /* Skip any initial directories in our path that we
- * have in common with lastpath. */
- for (fn = fbuf; *fn && *lp == *fn; lp++, fn++) {
- if (*fn == '/')
- slash = fn;
- }
- *p = '/';
- if (fn != p || (*lp && *lp != '/'))
- send_implied_dirs(f, flist, fbuf, slash, p, flags, 0);
+ send_implied_dirs(f, flist, fbuf, fbuf, p, flags, 0);
}
if (one_file_system)
if (recurse || (xfer_dirs && name_type != NORMAL_NAME)) {
struct file_struct *file;
- int top_flags = FLAG_TOP_DIR | FLAG_CONTENT_DIR | flags;
file = send_file_name(f, flist, fbuf, &st,
- top_flags, ALL_FILTERS);
+ FLAG_TOP_DIR | FLAG_CONTENT_DIR | flags,
+ NO_FILTERS);
if (!file)
continue;
if (inc_recurse) {
- if (name_type == DOT_NAME) {
+ if (name_type == DOTDIR_NAME) {
if (send_dir_depth < 0) {
send_dir_depth = 0;
change_local_filter_dir(fbuf, len, send_dir_depth);
} else
send_if_directory(f, flist, file, fbuf, len, flags);
} else
- send_file_name(f, flist, fbuf, &st, flags, ALL_FILTERS);
+ send_file_name(f, flist, fbuf, &st, flags, NO_FILTERS);
}
gettimeofday(&end_tv, NULL);
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);
if (inc_recurse) {
send_dir_depth = 1;
add_dirs_to_tree(-1, flist, dir_count);
- if (!file_total || strcmp(flist->sorted[0]->basename, ".") != 0)
+ if (!file_total || strcmp(flist->sorted[flist->low]->basename, ".") != 0)
flist->parent_ndx = -1;
flist_done_allocating(flist);
if (send_dir_ndx < 0) {
struct file_list *flist;
int dstart, flags;
int64 start_read;
+ int save_verbose = verbose;
if (!first_flist)
rprintf(FLOG, "receiving file list\n");
dstart = 0;
}
+ if (am_server && verbose > 2)
+ verbose = 2;
while ((flags = read_byte(f)) != 0) {
struct file_struct *file;
maybe_emit_filelist_progress(flist->used);
if (verbose > 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;
+ verbose = save_verbose;
if (verbose > 2)
rprintf(FINFO, "received %d names\n", flist->used);
else
io_error |= read_int(f);
} 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;
}
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.
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 (do_progress)