+ if (sanitize_paths)
+ sanitize_path(thisname, thisname, "", 0, NULL);
+
+ 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;
+ lastdir_depth = count_dir_elements(lastdir);
+ }
+ } else
+ basename = thisname;
+ basename_len = strlen(basename) + 1; /* count the '\0' */
+
+#ifdef SUPPORT_HARD_LINKS
+ if (protocol_version >= 30
+ && BITS_SETnUNSET(flags, XMIT_HLINKED, XMIT_HLINK_FIRST)) {
+ struct file_struct *first;
+ first_hlink_ndx = read_varint30(f);
+ if (first_hlink_ndx < 0 || first_hlink_ndx >= flist->count) {
+ rprintf(FERROR,
+ "hard-link reference out of range: %d (%d)\n",
+ first_hlink_ndx, flist->count);
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ first = flist->files[first_hlink_ndx];
+ file_length = F_LENGTH(first);
+ modtime = first->modtime;
+ mode = first->mode;
+ if (preserve_uid)
+ uid = F_UID(first);
+ if (preserve_gid)
+ gid = F_GID(first);
+ if ((preserve_devices && IS_DEVICE(mode))
+ || (preserve_specials && IS_SPECIAL(mode))) {
+ uint32 *devp = F_RDEV_P(first);
+ rdev = MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp));
+ extra_len += 2 * EXTRA_LEN;
+ }
+ if (preserve_links && S_ISLNK(mode))
+ linkname_len = strlen(F_SYMLINK(first)) + 1;
+ else
+ linkname_len = 0;
+ goto create_object;
+ }
+#endif
+
+ file_length = read_varlong30(f, 3);
+ if (!(flags & XMIT_SAME_TIME)) {
+ if (protocol_version >= 30) {
+ modtime = read_varlong(f, 4);
+#if SIZEOF_TIME_T < SIZEOF_INT64
+ if ((modtime > INT_MAX || modtime < INT_MIN) && !am_generator) {
+ rprintf(FERROR,
+ "Time value of %s truncated on receiver.\n",
+ lastname);
+ }
+#endif
+ } else
+ modtime = read_int(f);
+ }
+ if (!(flags & XMIT_SAME_MODE))
+ mode = from_wire_mode(read_int(f));
+
+ if (chmod_modes && !S_ISLNK(mode))
+ mode = tweak_mode(mode, chmod_modes);
+
+ if (preserve_uid && !(flags & XMIT_SAME_UID)) {
+ if (protocol_version < 30)
+ uid = (uid_t)read_int(f);
+ else {
+ uid = (uid_t)read_varint(f);
+ if (flags & XMIT_USER_NAME_FOLLOWS)
+ uid = recv_user_name(f, uid);
+ else if (inc_recurse && am_root && !numeric_ids)
+ uid = match_uid(uid);
+ }
+ }
+ if (preserve_gid && !(flags & XMIT_SAME_GID)) {
+ if (protocol_version < 30)
+ gid = (gid_t)read_int(f);
+ else {
+ gid = (gid_t)read_varint(f);
+ if (flags & XMIT_GROUP_NAME_FOLLOWS)
+ gid = recv_group_name(f, gid);
+ else if (inc_recurse && (!am_root || !numeric_ids))
+ gid = match_gid(gid);
+ }
+ }
+
+ if ((preserve_devices && IS_DEVICE(mode))
+ || (preserve_specials && IS_SPECIAL(mode))) {
+ if (protocol_version < 28) {
+ if (!(flags & XMIT_SAME_RDEV_pre28))
+ rdev = (dev_t)read_int(f);
+ } else {
+ uint32 rdev_minor;
+ if (!(flags & XMIT_SAME_RDEV_MAJOR))
+ rdev_major = read_varint30(f);
+ if (protocol_version >= 30)
+ rdev_minor = read_varint(f);
+ else if (flags & XMIT_RDEV_MINOR_8_pre30)
+ rdev_minor = read_byte(f);
+ else
+ rdev_minor = read_int(f);
+ rdev = MAKEDEV(rdev_major, rdev_minor);
+ }
+ extra_len += 2 * 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");
+ }
+ }
+ else
+#endif
+ linkname_len = 0;
+
+#ifdef SUPPORT_HARD_LINKS
+ create_object:
+ if (preserve_hard_links) {
+ if (protocol_version < 28 && S_ISREG(mode))
+ flags |= XMIT_HLINKED;
+ if (flags & XMIT_HLINKED)
+ extra_len += EXTRA_LEN;
+ }
+#endif
+
+#ifdef SUPPORT_ACLS
+ /* We need one or two index int32s when we're preserving ACLs. */
+ if (preserve_acls)
+ extra_len += (S_ISDIR(mode) ? 2 : 1) * EXTRA_LEN;
+#endif
+
+ if (always_checksum && S_ISREG(mode))
+ extra_len += SUM_EXTRA_CNT * EXTRA_LEN;
+
+ if (file_length > 0xFFFFFFFFu && S_ISREG(mode))
+ extra_len += EXTRA_LEN;
+
+#if EXTRA_ROUNDING > 0
+ if (extra_len & (EXTRA_ROUNDING * EXTRA_LEN))
+ extra_len = (extra_len | (EXTRA_ROUNDING * EXTRA_LEN)) + EXTRA_LEN;
+#endif
+
+ if (inc_recurse && S_ISDIR(mode)) {
+ if (one_file_system) {
+ /* Room to save the dir's device for -x */
+ extra_len += 2 * EXTRA_LEN;
+ }
+ pool = dir_flist->file_pool;
+ } else
+ pool = flist->file_pool;
+
+ 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);
+ bp += basename_len + linkname_len; /* skip space for symlink too */
+
+#ifdef SUPPORT_HARD_LINKS
+ if (flags & XMIT_HLINKED)
+ file->flags |= FLAG_HLINKED;
+#endif
+ file->modtime = (time_t)modtime;
+ file->len32 = (uint32)file_length;
+ if (file_length > 0xFFFFFFFFu && S_ISREG(mode)) {
+ file->flags |= FLAG_LENGTH64;
+ OPT_EXTRA(file, 0)->unum = (uint32)(file_length >> 32);
+ }
+ file->mode = mode;
+ if (preserve_uid)
+ F_OWNER(file) = uid;
+ if (preserve_gid)
+ F_GROUP(file) = gid;
+#ifdef ICONV_OPTION
+ if (ic_ndx)
+ F_NDX(file) = flist->count + flist->ndx_start;
+#endif
+
+ 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 (flags & 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_XFER_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_XFER_DIR;
+ else
+ in_del_hier = 0;
+ }
+ }
+
+ if ((preserve_devices && IS_DEVICE(mode))
+ || (preserve_specials && IS_SPECIAL(mode))) {
+ uint32 *devp = F_RDEV_P(file);
+ DEV_MAJOR(devp) = major(rdev);
+ DEV_MINOR(devp) = minor(rdev);
+ }
+
+#ifdef SUPPORT_LINKS
+ if (linkname_len) {
+ bp = (char*)file->basename + basename_len;
+ if (first_hlink_ndx >= 0) {
+ struct file_struct *first = flist->files[first_hlink_ndx];
+ memcpy(bp, F_SYMLINK(first), linkname_len);
+ } else
+ read_sbuf(f, bp, linkname_len - 1);
+ if (sanitize_paths)
+ sanitize_path(bp, bp, "", lastdir_depth, NULL);
+ }
+#endif
+
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && flags & XMIT_HLINKED) {
+ if (protocol_version >= 30) {
+ F_HL_GNUM(file) = flags & XMIT_HLINK_FIRST
+ ? flist->count : first_hlink_ndx;
+ } else {
+ static int32 cnt = 0;
+ struct idev_node *np;
+ int64 ino;
+ int32 ndx;
+ if (protocol_version < 26) {
+ dev = read_int(f);
+ ino = read_int(f);
+ } else {
+ if (!(flags & XMIT_SAME_DEV_pre30))
+ dev = read_longint(f);
+ ino = read_longint(f);
+ }
+ np = idev_node(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 = (char*)F_SUM(file);
+ else {
+ /* Prior to 28, we get a useless set of nulls. */
+ bp = tmp_sum;
+ }
+ if (first_hlink_ndx >= 0) {
+ struct file_struct *first = flist->files[first_hlink_ndx];
+ 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(file, f);
+#endif
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs)
+ receive_xattr(file, f );
+#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.
+ *
+ * @return the new file, or NULL if there was an error or this file
+ * should be excluded.
+ *
+ * @todo There is a small optimization opportunity here to avoid
+ * stat()ing the file in some circumstances, which has a certain cost.
+ * We are called immediately after doing readdir(), and so we may
+ * already know the d_type of the file. We could for example avoid
+ * statting directories if we're not recursing, but this is not a very
+ * important case. Some systems may not have d_type.
+ **/
+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;
+ STRUCT_STAT st;
+ char thisname[MAXPATHLEN];
+ char linkname[MAXPATHLEN];
+ int alloc_len, basename_len, linkname_len;
+ int extra_len = file_extra_cnt * EXTRA_LEN;
+ const char *basename;
+ char *bp;
+
+ if (strlcpy(thisname, fname, sizeof thisname)
+ >= sizeof thisname - pathname_len) {
+ rprintf(FINFO, "skipping overly long name: %s\n", fname);
+ return NULL;
+ }
+ clean_fname(thisname, 0);
+ if (sanitize_paths)
+ sanitize_path(thisname, thisname, "", 0, NULL);
+
+ if (stp && S_ISDIR(stp->st_mode)) {
+ st = *stp; /* Needed for "symlink/." with --relative. */
+ *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
+ /* Avoid "vanished" error if symlink points nowhere. */
+ if (copy_links && x_lstat(thisname, &st, NULL) == 0
+ && S_ISLNK(st.st_mode)) {
+ io_error |= IOERR_GENERAL;
+ rprintf(FERROR, "symlink has no referent: %s\n",
+ full_fname(thisname));
+ } else
+#endif
+ {
+ enum logcode c = am_daemon && protocol_version < 28
+ ? FERROR : FINFO;
+ io_error |= IOERR_VANISHED;
+ rprintf(c, "file has vanished: %s\n",
+ full_fname(thisname));
+ }
+ } else {
+ io_error |= IOERR_GENERAL;
+ rsyserr(FERROR, save_errno, "readlink %s failed",
+ full_fname(thisname));
+ }
+ return NULL;
+ }
+
+ /* backup.c calls us with filter_level set to NO_FILTERS. */
+ if (filter_level == NO_FILTERS)
+ goto skip_filters;
+
+ if (S_ISDIR(st.st_mode) && !xfer_dirs) {
+ rprintf(FINFO, "skipping directory %s\n", thisname);
+ return NULL;
+ }
+
+ /* -x only affects directories 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
+ && S_ISDIR(st.st_mode)) {
+ if (one_file_system > 1) {
+ if (verbose > 2) {
+ rprintf(FINFO, "skipping mount-point dir %s\n",
+ thisname);
+ }
+ return NULL;
+ }
+ flags |= FLAG_MOUNT_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 && flist->prev && S_ISDIR(st.st_mode)
+ && flags & FLAG_DIVERT_DIRS) {
+ flist = dir_flist;
+ /* Room for parent/sibling/next-child info. */
+ extra_len += 3 * EXTRA_LEN;
+ }
+
+ if (verbose > 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
+
+ if (st.st_size > 0xFFFFFFFFu && S_ISREG(st.st_mode))
+ extra_len += 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 (flist)
+ bp = pool_alloc(flist->file_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);
+ bp += basename_len + linkname_len; /* skip space for symlink too */
+
+#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 = st.st_dev;
+ tmp_ino = 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)) {
+ tmp_rdev = st.st_rdev;
+ st.st_size = 0;
+ }
+#endif
+
+ file->flags = flags;
+ file->modtime = st.st_mtime;
+ file->len32 = (uint32)st.st_size;
+ if (st.st_size > 0xFFFFFFFFu && S_ISREG(st.st_mode)) {
+ file->flags |= FLAG_LENGTH64;
+ OPT_EXTRA(file, 0)->unum = (uint32)(st.st_size >> 32);
+ }
+ file->mode = st.st_mode;
+ if (preserve_uid)
+ F_OWNER(file) = st.st_uid;
+ if (preserve_gid)
+ F_GROUP(file) = st.st_gid;
+
+ if (basename != thisname)
+ file->dirname = lastdir;
+
+#ifdef SUPPORT_LINKS
+ if (linkname_len) {
+ bp = (char*)file->basename + basename_len;
+ memcpy(bp, linkname, linkname_len);
+ }
+#endif
+
+ if (always_checksum && am_sender && S_ISREG(st.st_mode))
+ file_checksum(thisname, tmp_sum, st.st_size);
+
+ F_PATHNAME(file) = pathname;
+
+ /* 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 (preserve_uid)
+ F_OWNER(file) = st2.st_uid;
+ if (preserve_gid)
+ F_GROUP(file) = st2.st_gid;
+ } else
+ file->mode = save_mode;
+ }
+
+ if (basename_len == 0+1)
+ return NULL;
+
+ if (inc_recurse && flist == dir_flist) {
+ flist_expand(dir_flist);
+#ifdef ICONV_OPTION
+ if (ic_ndx)
+ F_NDX(file) = dir_flist->count;
+ if (need_unsorted_flist)
+ dir_flist->sorted[dir_flist->count] = file;
+#endif
+ dir_flist->files[dir_flist->count++] = file;
+ }
+
+ return file;
+}
+
+/* Only called for temporary file_struct entries created by make_file(). */
+void unmake_file(struct file_struct *file)
+{
+ int extra_cnt = file_extra_cnt + LEN64_BUMP(file);
+#if EXTRA_ROUNDING > 0
+ if (extra_cnt & EXTRA_ROUNDING)
+ extra_cnt = (extra_cnt | EXTRA_ROUNDING) + 1;
+#endif
+ free(REQ_EXTRA(file, extra_cnt));
+}
+
+static struct file_struct *send_file_name(int f, struct file_list *flist,
+ char *fname, STRUCT_STAT *stp,
+ int flags, int filter_flags)
+{
+ struct file_struct *file;
+#if defined SUPPORT_ACLS || defined SUPPORT_XATTRS
+ statx sx;
+#endif
+
+ file = make_file(fname, flist, stp, flags, filter_flags);
+ if (!file)
+ return NULL;
+
+ if (chmod_modes && !S_ISLNK(file->mode))
+ file->mode = tweak_mode(file->mode, chmod_modes);
+
+#ifdef SUPPORT_ACLS
+ if (preserve_acls && !S_ISLNK(file->mode) && f >= 0) {
+ sx.st.st_mode = file->mode;
+ sx.acc_acl = sx.def_acl = NULL;
+ if (get_acl(fname, &sx) < 0)
+ return NULL;
+ }
+#endif
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs && f >= 0) {
+ sx.xattr = NULL;
+ if (get_xattr(fname, &sx) < 0)
+ return NULL;
+ }
+#endif
+
+ maybe_emit_filelist_progress(flist->count + flist_count_offset);
+
+ flist_expand(flist);
+ flist->files[flist->count++] = file;
+ if (f >= 0) {
+ send_file_entry(f, file, flist->count - 1);
+#ifdef SUPPORT_ACLS
+ if (preserve_acls && !S_ISLNK(file->mode)) {
+ send_acl(&sx, f);
+ free_acl(&sx);
+ }
+#endif
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs) {
+ F_XATTR(file) = send_xattr(&sx, f);
+ free_xattr(&sx);
+ }
+#endif
+ }
+ 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, "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] = '.';
+ }
+}