+ uint32 rdev_minor;
+ if (!(xflags & XMIT_SAME_RDEV_MAJOR))
+ rdev_major = read_varint30(f);
+ if (protocol_version >= 30)
+ rdev_minor = read_varint(f);
+ else if (xflags & XMIT_RDEV_MINOR_8_pre30)
+ rdev_minor = read_byte(f);
+ else
+ rdev_minor = read_int(f);
+ rdev = MAKEDEV(rdev_major, rdev_minor);
+ }
+ if (IS_DEVICE(mode))
+ extra_len += DEV_EXTRA_CNT * EXTRA_LEN;
+ file_length = 0;
+ } else if (protocol_version < 28)
+ rdev = MAKEDEV(0, 0);
+
+#ifdef SUPPORT_LINKS
+ if (preserve_links && S_ISLNK(mode)) {
+ linkname_len = read_varint30(f) + 1; /* count the '\0' */
+ if (linkname_len <= 0 || linkname_len > MAXPATHLEN) {
+ rprintf(FERROR, "overflow: linkname_len=%d\n",
+ 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;
+ }
+ else
+#endif
+ linkname_len = 0;
+
+#ifdef SUPPORT_HARD_LINKS
+ create_object:
+ if (preserve_hard_links) {
+ if (protocol_version < 28 && S_ISREG(mode))
+ xflags |= XMIT_HLINKED;
+ if (xflags & XMIT_HLINKED)
+ extra_len += (inc_recurse+1) * EXTRA_LEN;
+ }
+#endif
+
+#ifdef SUPPORT_ACLS
+ /* Directories need an extra int32 for the default ACL. */
+ if (preserve_acls && S_ISDIR(mode))
+ extra_len += EXTRA_LEN;
+#endif
+
+ 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
+#ifdef HAVE_UTIMENSAT
+ if (modtime_nsec)
+ 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) {
+ /* Room to save the dir's device for -x */
+ extra_len += DEV_EXTRA_CNT * EXTRA_LEN;
+ }
+ pool = dir_flist->file_pool;
+ } else
+ pool = flist->file_pool;
+
+#if EXTRA_ROUNDING > 0
+ if (extra_len & (EXTRA_ROUNDING * EXTRA_LEN))
+ extra_len = (extra_len | (EXTRA_ROUNDING * EXTRA_LEN)) + EXTRA_LEN;
+#endif
+
+ alloc_len = FILE_STRUCT_LEN + extra_len + basename_len
+ + linkname_len;
+ bp = pool_alloc(pool, alloc_len, "recv_file_entry");
+
+ memset(bp, 0, extra_len + FILE_STRUCT_LEN);
+ bp += extra_len;
+ file = (struct file_struct *)bp;
+ bp += FILE_STRUCT_LEN;
+
+ memcpy(bp, basename, basename_len);
+
+#ifdef SUPPORT_HARD_LINKS
+ if (xflags & XMIT_HLINKED)
+ file->flags |= FLAG_HLINKED;
+#endif
+ file->modtime = (time_t)modtime;
+#ifdef HAVE_UTIMENSAT
+ if (modtime_nsec) {
+ file->flags |= FLAG_MOD_NSEC;
+ OPT_EXTRA(file, 0)->unum = modtime_nsec;
+ }
+#endif
+ 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, NSEC_BUMP(file))->unum = (uint32)(file_length >> 32);
+#endif
+ }
+#endif
+ file->mode = mode;
+ if (preserve_uid)
+ F_OWNER(file) = uid;
+ if (preserve_gid) {
+ F_GROUP(file) = gid;
+ file->flags |= gid_flags;
+ }
+ if (unsort_ndx)
+ F_NDX(file) = flist->used + flist->ndx_start;
+
+ if (basename != thisname) {
+ file->dirname = lastdir;
+ F_DEPTH(file) = lastdir_depth + 1;
+ } else
+ F_DEPTH(file) = 1;
+
+ if (S_ISDIR(mode)) {
+ if (basename_len == 1+1 && *basename == '.') /* +1 for '\0' */
+ F_DEPTH(file)--;
+ if (protocol_version >= 30) {
+ if (!(xflags & XMIT_NO_CONTENT_DIR)) {
+ if (xflags & XMIT_TOP_DIR)
+ file->flags |= FLAG_TOP_DIR;
+ file->flags |= FLAG_CONTENT_DIR;
+ } else if (xflags & XMIT_TOP_DIR)
+ file->flags |= FLAG_IMPLIED_DIR;
+ } else if (xflags & XMIT_TOP_DIR) {
+ in_del_hier = recurse;
+ del_hier_name_len = F_DEPTH(file) == 0 ? 0 : l1 + l2;
+ if (relative_paths && del_hier_name_len > 2
+ && lastname[del_hier_name_len-1] == '.'
+ && lastname[del_hier_name_len-2] == '/')
+ del_hier_name_len -= 2;
+ file->flags |= FLAG_TOP_DIR | FLAG_CONTENT_DIR;
+ } else if (in_del_hier) {
+ if (!relative_paths || !del_hier_name_len
+ || (l1 >= del_hier_name_len
+ && lastname[del_hier_name_len] == '/'))
+ file->flags |= FLAG_CONTENT_DIR;
+ else
+ in_del_hier = 0;
+ }
+ }
+
+ if (preserve_devices && IS_DEVICE(mode)) {
+ uint32 *devp = F_RDEV_P(file);
+ DEV_MAJOR(devp) = major(rdev);
+ DEV_MINOR(devp) = minor(rdev);
+ }
+
+#ifdef SUPPORT_LINKS
+ if (linkname_len) {
+ bp += basename_len;
+ 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;
+ }
+#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, ICB_INIT) < 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);
+ }
+ }
+#endif
+
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && xflags & XMIT_HLINKED) {
+ if (protocol_version >= 30) {
+ if (xflags & XMIT_HLINK_FIRST) {
+ F_HL_GNUM(file) = flist->ndx_start + flist->used;
+ } else
+ F_HL_GNUM(file) = first_hlink_ndx;
+ } else {
+ static int32 cnt = 0;
+ struct ht_int64_node *np;
+ int64 ino;
+ int32 ndx;
+ if (protocol_version < 26) {
+ dev = read_int(f);
+ ino = read_int(f);
+ } else {
+ if (!(xflags & XMIT_SAME_DEV_pre30))
+ dev = read_longint(f);
+ ino = read_longint(f);
+ }
+ np = idev_find(dev, ino);
+ ndx = (int32)(long)np->data - 1;
+ if (ndx < 0) {
+ ndx = cnt++;
+ np->data = (void*)(long)cnt;
+ }
+ F_HL_GNUM(file) = ndx;
+ }
+ }
+#endif
+
+ if (always_checksum && (S_ISREG(mode) || protocol_version < 28)) {
+ if (S_ISREG(mode))
+ bp = F_SUM(file);
+ else {
+ /* Prior to 28, we get a useless set of nulls. */
+ bp = tmp_sum;
+ }
+ if (first_hlink_ndx >= flist->ndx_start) {
+ struct file_struct *first = flist->files[first_hlink_ndx - flist->ndx_start];
+ memcpy(bp, F_SUM(first), checksum_len);
+ } else
+ read_buf(f, bp, checksum_len);
+ }
+
+#ifdef SUPPORT_ACLS
+ if (preserve_acls && !S_ISLNK(mode))
+ receive_acl(f, file);
+#endif
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs)
+ receive_xattr(f, file);
+#endif
+
+ if (S_ISREG(mode) || S_ISLNK(mode))
+ stats.total_size += file_length;
+
+ return file;
+}
+
+/* Create a file_struct for a named file by reading its stat() information
+ * 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.
+ *
+ * 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)
+{
+ static char *lastdir;
+ static int lastdir_len = -1;
+ struct file_struct *file;
+ char thisname[MAXPATHLEN];
+ char linkname[MAXPATHLEN];
+ int alloc_len, basename_len, linkname_len;
+ int extra_len = file_extra_cnt * EXTRA_LEN;
+ const char *basename;
+ alloc_pool_t *pool;
+ STRUCT_STAT st;
+ char *bp;
+
+ if (strlcpy(thisname, fname, sizeof thisname) >= sizeof thisname) {
+ 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) || 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
+ && (is_excluded(thisname, 0, filter_level)
+ || is_excluded(thisname, 1, filter_level))) {
+ if (ignore_perishable && save_errno != ENOENT)
+ non_perishable_cnt++;
+ return NULL;
+ }
+ if (save_errno == ENOENT) {
+#ifdef SUPPORT_LINKS
+ /* When our options tell us to follow a symlink that
+ * points nowhere, tell the user about the symlink
+ * instead of giving a "vanished" message. We only
+ * dereference a symlink if one of the --copy*links
+ * options was specified, so there's no need for the
+ * extra lstat() if one of these options isn't on. */
+ if ((copy_links || copy_unsafe_links || copy_dirlinks)
+ && x_lstat(thisname, &st, NULL) == 0
+ && S_ISLNK(st.st_mode)) {
+ io_error |= IOERR_GENERAL;
+ rprintf(FERROR_XFER, "symlink has no referent: %s\n",
+ full_fname(thisname));
+ } else
+#endif
+ {
+ enum logcode c = am_daemon && protocol_version < 28
+ ? FERROR : FWARNING;
+ io_error |= IOERR_VANISHED;
+ rprintf(c, "file has vanished: %s\n",
+ full_fname(thisname));
+ }
+ } else {
+ io_error |= IOERR_GENERAL;
+ 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)
+ goto skip_filters;
+
+ if (S_ISDIR(st.st_mode)) {
+ if (!xfer_dirs) {
+ rprintf(FINFO, "skipping directory %s\n", thisname);
+ return NULL;
+ }
+ /* -x only affects dirs because we need to avoid recursing
+ * into a mount-point directory, not to avoid copying a
+ * symlinked file if -L (or similar) was specified. */
+ if (one_file_system && st.st_dev != filesystem_dev
+ && BITS_SETnUNSET(flags, FLAG_CONTENT_DIR, FLAG_TOP_DIR)) {
+ if (one_file_system > 1) {
+ if (INFO_GTE(MOUNT, 1)) {
+ rprintf(FINFO,
+ "[%s] skipping mount-point dir %s\n",
+ who_am_i(), thisname);
+ }
+ return NULL;
+ }
+ flags |= FLAG_MOUNT_DIR;
+ flags &= ~FLAG_CONTENT_DIR;
+ }
+ } else
+ flags &= ~FLAG_CONTENT_DIR;
+
+ if (is_excluded(thisname, S_ISDIR(st.st_mode) != 0, filter_level)) {
+ if (ignore_perishable)
+ non_perishable_cnt++;
+ return NULL;
+ }
+
+ if (lp_ignore_nonreadable(module_id)) {
+#ifdef SUPPORT_LINKS
+ if (!S_ISLNK(st.st_mode))
+#endif
+ if (access(thisname, R_OK) != 0)
+ return NULL;
+ }
+
+ skip_filters:
+
+ /* Only divert a directory in the main transfer. */
+ if (flist) {
+ if (flist->prev && S_ISDIR(st.st_mode)
+ && flags & FLAG_DIVERT_DIRS) {
+ /* Room for parent/sibling/next-child info. */
+ extra_len += DIRNODE_EXTRA_CNT * EXTRA_LEN;
+ if (relative_paths)
+ extra_len += PTR_EXTRA_CNT * EXTRA_LEN;
+ pool = dir_flist->file_pool;
+ } else
+ pool = flist->file_pool;
+ } else {
+#ifdef SUPPORT_ACLS
+ /* Directories need an extra int32 for the default ACL. */
+ if (preserve_acls && S_ISDIR(st.st_mode))
+ extra_len += EXTRA_LEN;
+#endif
+ pool = NULL;
+ }
+
+ if (DEBUG_GTE(FLIST, 2)) {
+ rprintf(FINFO, "[%s] make_file(%s,*,%d)\n",
+ who_am_i(), thisname, filter_level);
+ }
+
+ if ((basename = strrchr(thisname, '/')) != NULL) {
+ int len = basename++ - thisname;
+ if (len != lastdir_len || memcmp(thisname, lastdir, len) != 0) {
+ lastdir = new_array(char, len + 1);
+ memcpy(lastdir, thisname, len);
+ lastdir[len] = '\0';
+ lastdir_len = len;
+ }
+ } else
+ basename = thisname;
+ basename_len = strlen(basename) + 1; /* count the '\0' */
+
+#ifdef SUPPORT_LINKS
+ linkname_len = S_ISLNK(st.st_mode) ? strlen(linkname) + 1 : 0;
+#else
+ linkname_len = 0;
+#endif
+
+#ifdef ST_MTIME_NSEC
+ if (st.ST_MTIME_NSEC && protocol_version >= 31)
+ extra_len += EXTRA_LEN;
+#endif
+#if SIZEOF_CAPITAL_OFF_T >= 8
+ if (st.st_size > 0xFFFFFFFFu && S_ISREG(st.st_mode))
+ extra_len += EXTRA_LEN;
+#endif
+
+ if (always_checksum && am_sender && S_ISREG(st.st_mode)) {
+ file_checksum(thisname, tmp_sum, st.st_size);
+ if (sender_keeps_checksum)
+ extra_len += SUM_EXTRA_CNT * EXTRA_LEN;
+ }
+
+#if EXTRA_ROUNDING > 0
+ if (extra_len & (EXTRA_ROUNDING * EXTRA_LEN))
+ extra_len = (extra_len | (EXTRA_ROUNDING * EXTRA_LEN)) + EXTRA_LEN;
+#endif
+
+ alloc_len = FILE_STRUCT_LEN + extra_len + basename_len
+ + linkname_len;
+ if (pool)
+ bp = pool_alloc(pool, alloc_len, "make_file");
+ else {
+ if (!(bp = new_array(char, alloc_len)))
+ out_of_memory("make_file");
+ }
+
+ memset(bp, 0, extra_len + FILE_STRUCT_LEN);
+ bp += extra_len;
+ file = (struct file_struct *)bp;
+ bp += FILE_STRUCT_LEN;
+
+ memcpy(bp, basename, basename_len);
+
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && flist && flist->prev) {
+ if (protocol_version >= 28
+ ? (!S_ISDIR(st.st_mode) && st.st_nlink > 1)
+ : S_ISREG(st.st_mode)) {
+ 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)) {
+ 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;
+#ifdef ST_MTIME_NSEC
+ if (st.ST_MTIME_NSEC && protocol_version >= 31) {
+ file->flags |= FLAG_MOD_NSEC;
+ OPT_EXTRA(file, 0)->unum = st.ST_MTIME_NSEC;
+ }
+#endif
+ 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, NSEC_BUMP(file))->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;
+ if (gid_ndx) /* Check gid_ndx instead of preserve_gid for del support */
+ F_GROUP(file) = st.st_gid;
+
+ if (basename != thisname)
+ file->dirname = lastdir;
+
+#ifdef SUPPORT_LINKS
+ if (linkname_len)
+ memcpy(bp + basename_len, linkname, linkname_len);
+#endif
+
+ if (am_sender)
+ F_PATHNAME(file) = pathname;
+ else if (!pool)
+ F_DEPTH(file) = extra_len / EXTRA_LEN;
+
+ if (basename_len == 0+1) {
+ if (!pool)
+ unmake_file(file);
+ return NULL;
+ }
+
+ if (sender_keeps_checksum && S_ISREG(st.st_mode))
+ memcpy(F_SUM(file), tmp_sum, checksum_len);
+
+ if (unsort_ndx)
+ F_NDX(file) = stats.num_dirs;
+
+ return file;
+}
+
+/* Only called for temporary file_struct entries created by make_file(). */
+void unmake_file(struct file_struct *file)
+{
+ free(REQ_EXTRA(file, F_DEPTH(file)));
+}
+
+static struct file_struct *send_file_name(int f, struct file_list *flist,
+ const char *fname, STRUCT_STAT *stp,
+ int flags, int filter_level)
+{
+ struct file_struct *file;
+
+ file = make_file(fname, flist, stp, flags, filter_level);
+ if (!file)
+ return NULL;
+
+ 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
+ if (ic_send != (iconv_t)-1) {
+ xbuf outbuf, inbuf;
+
+ INIT_CONST_XBUF(outbuf, fbuf);
+
+ if (file->dirname) {
+ INIT_XBUF_STRLEN(inbuf, (char*)file->dirname);
+ outbuf.size -= 2; /* Reserve room for '/' & 1 more char. */
+ if (iconvbufs(ic_send, &inbuf, &outbuf, ICB_INIT) < 0)
+ goto convert_error;
+ outbuf.size += 2;
+ fbuf[outbuf.len++] = '/';
+ }
+
+ INIT_XBUF_STRLEN(inbuf, (char*)file->basename);
+ if (iconvbufs(ic_send, &inbuf, &outbuf, ICB_INIT) < 0) {
+ convert_error:
+ io_error |= IOERR_GENERAL;
+ rprintf(FERROR_XFER,
+ "[%s] cannot convert filename: %s (%s)\n",
+ who_am_i(), f_name(file, fbuf), strerror(errno));
+ return NULL;
+ }
+ 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, ICB_INIT) < 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);
+
+#ifdef SUPPORT_ACLS
+ if (preserve_acls && !S_ISLNK(file->mode)) {
+ sx.st.st_mode = file->mode;
+ if (get_acl(fname, &sx) < 0) {
+ io_error |= IOERR_GENERAL;
+ return NULL;
+ }
+ }
+#endif
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs) {
+ sx.st.st_mode = file->mode;
+ if (get_xattr(fname, &sx) < 0) {
+ io_error |= IOERR_GENERAL;
+ return NULL;
+ }
+ }
+#endif
+
+ 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)) {
+ send_acl(f, &sx);
+ free_acl(&sx);
+ }
+#endif
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs) {
+ F_XATTR(file) = send_xattr(f, &sx);
+ free_xattr(&sx);
+ }
+#endif
+ }
+
+ maybe_emit_filelist_progress(flist->used + flist_count_offset);
+
+ flist_expand(flist, 1);
+ flist->files[flist->used++] = file;
+
+ return file;
+}
+
+static void send_if_directory(int f, struct file_list *flist,
+ struct file_struct *file,
+ char *fbuf, unsigned int ol,
+ int flags)
+{
+ char is_dot_dir = fbuf[ol-1] == '.' && (ol == 1 || fbuf[ol-2] == '/');
+
+ if (S_ISDIR(file->mode)
+ && !(file->flags & FLAG_MOUNT_DIR) && f_name(file, fbuf)) {
+ void *save_filters;
+ unsigned int len = strlen(fbuf);
+ if (len > 1 && fbuf[len-1] == '/')
+ fbuf[--len] = '\0';
+ if (len >= MAXPATHLEN - 1) {
+ io_error |= IOERR_GENERAL;
+ rprintf(FERROR_XFER, "skipping long-named directory: %s\n",
+ full_fname(fbuf));
+ return;
+ }
+ save_filters = push_local_filters(fbuf, len);
+ send_directory(f, flist, fbuf, len, flags);
+ pop_local_filters(save_filters);
+ fbuf[ol] = '\0';
+ if (is_dot_dir)
+ fbuf[ol-1] = '.';
+ }
+}
+
+static int file_compare(const void *file1, const void *file2)
+{
+ return f_name_cmp(*(struct file_struct **)file1,
+ *(struct file_struct **)file2);
+}
+
+/* The guts of a merge-sort algorithm. This was derived from the glibc
+ * version, but I (Wayne) changed the merge code to do less copying and
+ * to require only half the amount of temporary memory. */
+static void fsort_tmp(struct file_struct **fp, size_t num,
+ struct file_struct **tmp)
+{
+ struct file_struct **f1, **f2, **t;
+ size_t n1, n2;
+
+ n1 = num / 2;
+ n2 = num - n1;
+ f1 = fp;
+ f2 = fp + n1;
+
+ if (n1 > 1)
+ fsort_tmp(f1, n1, tmp);
+ if (n2 > 1)
+ fsort_tmp(f2, n2, tmp);
+
+ while (f_name_cmp(*f1, *f2) <= 0) {
+ if (!--n1)
+ return;
+ f1++;
+ }
+
+ t = tmp;
+ memcpy(t, f1, n1 * PTR_SIZE);
+
+ *f1++ = *f2++, n2--;
+
+ while (n1 > 0 && n2 > 0) {
+ if (f_name_cmp(*t, *f2) <= 0)
+ *f1++ = *t++, n1--;
+ else
+ *f1++ = *f2++, n2--;
+ }
+
+ if (n1 > 0)
+ memcpy(f1, t, n1 * PTR_SIZE);
+}
+
+/* This file-struct sorting routine makes sure that any identical names in
+ * the file list stay in the same order as they were in the original list.
+ * This is particularly vital in inc_recurse mode where we expect a sort
+ * on the flist to match the exact order of a sort on the dir_flist. */
+static void fsort(struct file_struct **fp, size_t num)
+{
+ if (num <= 1)
+ return;
+
+ if (use_qsort)
+ qsort(fp, num, PTR_SIZE, file_compare);
+ else {
+ struct file_struct **tmp = new_array(struct file_struct *,
+ (num+1) / 2);
+ fsort_tmp(fp, num, tmp);
+ free(tmp);
+ }
+}
+
+/* We take an entire set of sibling dirs from the sorted flist and link them
+ * into the tree, setting the appropriate parent/child/sibling pointers. */
+static void add_dirs_to_tree(int parent_ndx, struct file_list *from_flist,
+ int dir_cnt)
+{
+ int i;
+ int32 *dp = NULL;
+ int32 *parent_dp = parent_ndx < 0 ? NULL
+ : F_DIR_NODE_P(dir_flist->sorted[parent_ndx]);
+
+ flist_expand(dir_flist, dir_cnt);
+ dir_flist->sorted = dir_flist->files;
+
+ for (i = 0; dir_cnt; i++) {
+ struct file_struct *file = from_flist->sorted[i];
+
+ if (!S_ISDIR(file->mode))
+ continue;
+
+ dir_flist->files[dir_flist->used++] = file;
+ dir_cnt--;
+
+ if (file->basename[0] == '.' && file->basename[1] == '\0')
+ continue;
+
+ if (dp)
+ DIR_NEXT_SIBLING(dp) = dir_flist->used - 1;
+ else if (parent_dp)
+ DIR_FIRST_CHILD(parent_dp) = dir_flist->used - 1;
+ else
+ send_dir_ndx = dir_flist->used - 1;
+
+ dp = F_DIR_NODE_P(file);
+ DIR_PARENT(dp) = parent_ndx;
+ DIR_FIRST_CHILD(dp) = -1;
+ }
+ if (dp)
+ 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()
+ * might call this with f set to -2, which also indicates that local filter
+ * rules should be ignored. */
+static void send_directory(int f, struct file_list *flist, char *fbuf, int len,
+ int flags)
+{
+ struct dirent *di;
+ unsigned remainder;
+ char *p;
+ DIR *d;
+ int divert_dirs = (flags & FLAG_DIVERT_DIRS) != 0;
+ int start = flist->used;
+ int filter_level = f == -2 ? SERVER_FILTERS : ALL_FILTERS;
+
+ 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;
+ }
+
+ p = fbuf + len;
+ if (len != 1 || *fbuf != '/')
+ *p++ = '/';
+ *p = '\0';
+ remainder = MAXPATHLEN - (p - fbuf);
+
+ for (errno = 0, di = readdir(d); di; errno = 0, di = readdir(d)) {
+ char *dname = d_name(di);
+ if (dname[0] == '.' && (dname[1] == '\0'
+ || (dname[1] == '.' && dname[2] == '\0')))
+ continue;
+ if (strlcpy(p, dname, remainder) >= remainder) {
+ io_error |= IOERR_GENERAL;
+ rprintf(FERROR_XFER,
+ "cannot send long-named file %s\n",
+ full_fname(fbuf));
+ continue;
+ }
+ if (dname[0] == '\0') {
+ io_error |= IOERR_GENERAL;
+ rprintf(FERROR_XFER,
+ "cannot send file with empty name in %s\n",
+ full_fname(fbuf));
+ continue;
+ }
+
+ send_file_name(f, flist, fbuf, NULL, flags, filter_level);
+ }
+
+ fbuf[len] = '\0';
+
+ if (errno) {
+ io_error |= IOERR_GENERAL;
+ rsyserr(FERROR_XFER, errno, "readdir(%s)", full_fname(fbuf));
+ }
+
+ closedir(d);
+
+ if (f >= 0 && recurse && !divert_dirs) {
+ int i, end = flist->used - 1;
+ /* send_if_directory() bumps flist->used, so use "end". */
+ for (i = start; i <= end; i++)
+ send_if_directory(f, flist, flist->files[i], fbuf, len, flags);
+ }
+}
+
+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;
+ int len, need_new_dir, depth = 0;
+ 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. */
+
+ if (inc_recurse) {
+ if (lastpath_struct && F_PATHNAME(lastpath_struct) == pathname
+ && lastpath_len == limit - fname
+ && strncmp(lastpath, fname, lastpath_len) == 0)
+ need_new_dir = 0;
+ else
+ need_new_dir = 1;
+ } 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++;
+ }