+ type = TYPE_DIR;
+ } else if (IS_SPECIAL(file->mode))
+ type = TYPE_SPECIAL;
+ else if (IS_DEVICE(file->mode))
+ type = TYPE_DEVICE;
+#ifdef SUPPORT_LINKS
+ else if (S_ISLNK(file->mode))
+ type = TYPE_SYMLINK;
+#endif
+ else {
+ rprintf(FERROR,
+ "internal: try_dests_non() called with invalid mode (%o)\n",
+ (int)file->mode);
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+
+ do {
+ pathjoin(cmpbuf, MAXPATHLEN, basis_dir[j], fname);
+ if (link_stat(cmpbuf, &sxp->st, 0) < 0)
+ continue;
+ switch (type) {
+ case TYPE_DIR:
+ if (!S_ISDIR(sxp->st.st_mode))
+ continue;
+ break;
+ case TYPE_SPECIAL:
+ if (!IS_SPECIAL(sxp->st.st_mode))
+ continue;
+ break;
+ case TYPE_DEVICE:
+ if (!IS_DEVICE(sxp->st.st_mode))
+ continue;
+ break;
+ case TYPE_SYMLINK:
+#ifdef SUPPORT_LINKS
+ if (!S_ISLNK(sxp->st.st_mode))
+ continue;
+ break;
+#else
+ return -1;
+#endif
+ }
+ if (match_level < 1) {
+ match_level = 1;
+ best_match = j;
+ }
+ switch (type) {
+ case TYPE_DIR:
+ case TYPE_SPECIAL:
+ break;
+ case TYPE_DEVICE:
+ devp = F_RDEV_P(file);
+ if (sxp->st.st_rdev != MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp)))
+ continue;
+ break;
+ case TYPE_SYMLINK:
+#ifdef SUPPORT_LINKS
+ if ((len = do_readlink(cmpbuf, lnk, MAXPATHLEN-1)) <= 0)
+ continue;
+ lnk[len] = '\0';
+ if (strcmp(lnk, F_SYMLINK(file)) != 0)
+ continue;
+ break;
+#else
+ return -1;
+#endif
+ }
+ if (match_level < 2) {
+ match_level = 2;
+ best_match = j;
+ }
+ if (unchanged_attrs(cmpbuf, file, sxp)) {
+ match_level = 3;
+ best_match = j;
+ break;
+ }
+ } while (basis_dir[++j] != NULL);
+
+ if (!match_level)
+ return -1;
+
+ if (j != best_match) {
+ j = best_match;
+ pathjoin(cmpbuf, MAXPATHLEN, basis_dir[j], fname);
+ if (link_stat(cmpbuf, &sxp->st, 0) < 0)
+ return -1;
+ }
+
+ if (match_level == 3) {
+#ifdef SUPPORT_HARD_LINKS
+ if (link_dest
+#ifndef CAN_HARDLINK_SYMLINK
+ && !S_ISLNK(file->mode)
+#endif
+#ifndef CAN_HARDLINK_SPECIAL
+ && !IS_SPECIAL(file->mode) && !IS_DEVICE(file->mode)
+#endif
+ && !S_ISDIR(file->mode)) {
+ if (do_link(cmpbuf, fname) < 0) {
+ rsyserr(FERROR_XFER, errno,
+ "failed to hard-link %s with %s",
+ cmpbuf, fname);
+ return j;
+ }
+ if (preserve_hard_links && F_IS_HLINKED(file))
+ finish_hard_link(file, fname, ndx, NULL, itemizing, code, -1);
+ } else
+#endif
+ match_level = 2;
+ if (itemizing && stdout_format_has_i
+ && (INFO_GTE(NAME, 2) || stdout_format_has_i > 1)) {
+ int chg = compare_dest && type != TYPE_DIR ? 0
+ : ITEM_LOCAL_CHANGE + (match_level == 3 ? ITEM_XNAME_FOLLOWS : 0);
+ char *lp = match_level == 3 ? "" : NULL;
+ itemize(cmpbuf, file, ndx, 0, sxp, chg + ITEM_MATCHED, 0, lp);
+ }
+ if (INFO_GTE(NAME, 2) && maybe_ATTRS_REPORT) {
+ rprintf(FCLIENT, "%s%s is uptodate\n",
+ fname, type == TYPE_DIR ? "/" : "");
+ }
+ return -2;
+ }
+
+ return j;
+}
+
+static void list_file_entry(struct file_struct *f)
+{
+ char permbuf[PERMSTRING_SIZE];
+ int64 len;
+ int colwidth = human_readable ? 14 : 11;
+
+ if (!F_IS_ACTIVE(f)) {
+ /* this can happen if duplicate names were removed */
+ return;
+ }
+
+ permstring(permbuf, f->mode);
+ len = F_LENGTH(f);
+
+ /* TODO: indicate '+' if the entry has an ACL. */
+
+#ifdef SUPPORT_LINKS
+ if (preserve_links && S_ISLNK(f->mode)) {
+ rprintf(FINFO, "%s %*s %s %s -> %s\n",
+ permbuf, colwidth, human_num(len),
+ timestring(f->modtime), f_name(f, NULL),
+ F_SYMLINK(f));
+ } else
+#endif
+ if (missing_args == 2 && f->mode == 0) {
+ rprintf(FINFO, "%-*s %s\n",
+ colwidth + 31, "*missing",
+ f_name(f, NULL));
+ } else {
+ rprintf(FINFO, "%s %*s %s %s\n",
+ permbuf, colwidth, human_num(len),
+ timestring(f->modtime), f_name(f, NULL));
+ }
+}
+
+static int phase = 0;
+static int dflt_perms;
+
+static int implied_dirs_are_missing;
+/* Helper for recv_generator's skip_dir and dry_missing_dir tests. */
+static BOOL is_below(struct file_struct *file, struct file_struct *subtree)
+{
+ return F_DEPTH(file) > F_DEPTH(subtree)
+ && (!implied_dirs_are_missing || f_name_has_prefix(file, subtree));
+}
+
+/* Acts on the indicated item in cur_flist whose name is fname. If a dir,
+ * make sure it exists, and has the right permissions/timestamp info. For
+ * all other non-regular files (symlinks, etc.) we create them here. For
+ * regular files that have changed, we try to find a basis file and then
+ * start sending checksums. The ndx is the file's unique index value.
+ *
+ * The fname parameter must point to a MAXPATHLEN buffer! (e.g it gets
+ * passed to delete_item(), which can use it during a recursive delete.)
+ *
+ * Note that f_out is set to -1 when doing final directory-permission and
+ * modification-time repair. */
+static void recv_generator(char *fname, struct file_struct *file, int ndx,
+ int itemizing, enum logcode code, int f_out)
+{
+ static const char *parent_dirname = "";
+ /* Missing dir not created due to --dry-run; will still be scanned. */
+ static struct file_struct *dry_missing_dir = NULL;
+ /* Missing dir whose contents are skipped altogether due to
+ * --ignore-non-existing, daemon exclude, or mkdir failure. */
+ static struct file_struct *skip_dir = NULL;
+ static struct file_list *fuzzy_dirlist = NULL;
+ static int need_fuzzy_dirlist = 0;
+ struct file_struct *fuzzy_file = NULL;
+ int fd = -1, f_copy = -1;
+ stat_x sx, real_sx;
+ STRUCT_STAT partial_st;
+ struct file_struct *back_file = NULL;
+ int statret, real_ret, stat_errno;
+ char *fnamecmp, *partialptr, *backupptr = NULL;
+ char fnamecmpbuf[MAXPATHLEN];
+ uchar fnamecmp_type;
+ int del_opts = delete_mode || force_delete ? DEL_RECURSE : 0;
+ int is_dir = !S_ISDIR(file->mode) ? 0
+ : inc_recurse && ndx != cur_flist->ndx_start - 1 ? -1
+ : 1;
+
+ if (DEBUG_GTE(GENR, 1))
+ rprintf(FINFO, "recv_generator(%s,%d)\n", fname, ndx);
+
+ if (list_only) {
+ if (is_dir < 0
+ || (is_dir && !implied_dirs && file->flags & FLAG_IMPLIED_DIR))
+ return;
+ list_file_entry(file);
+ return;
+ }
+
+ if (skip_dir) {
+ if (is_below(file, skip_dir)) {
+ if (is_dir)
+ file->flags |= FLAG_MISSING_DIR;
+#ifdef SUPPORT_HARD_LINKS
+ else if (F_IS_HLINKED(file))
+ handle_skipped_hlink(file, itemizing, code, f_out);
+#endif
+ return;
+ }
+ skip_dir = NULL;
+ }
+
+ init_stat_x(&sx);
+ if (daemon_filter_list.head && (*fname != '.' || fname[1])) {
+ if (check_filter(&daemon_filter_list, FLOG, fname, is_dir) < 0) {
+ if (is_dir < 0)
+ return;
+#ifdef SUPPORT_HARD_LINKS
+ if (F_IS_HLINKED(file))
+ handle_skipped_hlink(file, itemizing, code, f_out);
+#endif
+ rprintf(FERROR_XFER,
+ "skipping daemon-excluded %s \"%s\"\n",
+ is_dir ? "directory" : "file", fname);
+ if (is_dir)
+ goto skipping_dir_contents;
+ return;
+ }
+ }
+
+ if (dry_run > 1 || (dry_missing_dir && is_below(file, dry_missing_dir))) {
+ parent_is_dry_missing:
+ if (fuzzy_dirlist) {
+ flist_free(fuzzy_dirlist);
+ fuzzy_dirlist = NULL;
+ }
+ parent_dirname = "";
+ statret = -1;
+ stat_errno = ENOENT;
+ } else {
+ const char *dn = file->dirname ? file->dirname : ".";
+ dry_missing_dir = NULL;
+ if (parent_dirname != dn && strcmp(parent_dirname, dn) != 0) {
+ if (relative_paths && !implied_dirs
+ && do_stat(dn, &sx.st) < 0) {
+ if (dry_run)
+ goto parent_is_dry_missing;
+ if (make_path(fname, MKP_DROP_NAME | MKP_SKIP_SLASH) < 0) {
+ rsyserr(FERROR_XFER, errno,
+ "recv_generator: mkdir %s failed",
+ full_fname(dn));
+ }