+ 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));
+ continue;
+ }
+ send_file_name(f, flist, fbuf, &st, FLAG_TOP_DIR | flags, ALL_FILTERS);
+ } else
+ send_file_name(f, flist, fbuf, NULL, FLAG_TOP_DIR | flags, ALL_FILTERS);
+ }
+
+ free(relname_list);
+}
+
+void send_extra_file_list(int f, int at_least)
+{
+ struct file_list *flist;
+ int64 start_write;
+ uint16 prev_flags;
+ int old_cnt, save_io_error = io_error;
+
+ if (flist_eof)
+ return;
+
+ /* Keep sending data until we have the requested number of
+ * files in the upcoming file-lists. */
+ old_cnt = cur_flist->used;
+ for (flist = first_flist; flist != cur_flist; flist = flist->next)
+ old_cnt += flist->used;
+ while (file_total - old_cnt < at_least) {
+ struct file_struct *file = dir_flist->sorted[send_dir_ndx];
+ int dir_ndx, dstart = dir_count;
+ const char *pathname = F_PATHNAME(file);
+ int32 *dp;
+
+ flist = flist_new(0, "send_extra_file_list");
+ start_write = stats.total_written;
+
+ if (unsort_ndx)
+ dir_ndx = F_NDX(file);
+ else
+ dir_ndx = send_dir_ndx;
+ write_ndx(f, NDX_FLIST_OFFSET - dir_ndx);
+ flist->parent_ndx = dir_ndx;
+
+ send1extra(f, file, flist);
+ prev_flags = file->flags;
+ dp = F_DIR_NODE_P(file);
+
+ /* If there are any duplicate directory names that follow, we
+ * send all the dirs together in one file-list. The dir_flist
+ * tree links all the child subdirs onto the last dup dir. */
+ while ((dir_ndx = DIR_NEXT_SIBLING(dp)) >= 0
+ && dir_flist->sorted[dir_ndx]->flags & FLAG_DUPLICATE) {
+ send_dir_ndx = dir_ndx;
+ file = dir_flist->sorted[dir_ndx];
+ /* Try to avoid some duplicate scanning of identical dirs. */
+ if (F_PATHNAME(file) == pathname && prev_flags & FLAG_CONTENT_DIR)
+ file->flags &= ~FLAG_CONTENT_DIR;
+ send1extra(f, file, flist);
+ prev_flags = file->flags;
+ dp = F_DIR_NODE_P(file);
+ }
+
+ write_byte(f, 0);
+
+ if (need_unsorted_flist) {
+ if (!(flist->sorted = new_array(struct file_struct *, flist->used)))
+ out_of_memory("send_extra_file_list");
+ memcpy(flist->sorted, flist->files,
+ flist->used * sizeof (struct file_struct*));
+ } else
+ flist->sorted = flist->files;
+
+ flist_sort_and_clean(flist, 0);
+
+ add_dirs_to_tree(send_dir_ndx, flist, dir_count - dstart);
+ flist_done_allocating(flist);
+
+ file_total += flist->used;
+ stats.flist_size += stats.total_written - start_write;
+ stats.num_files += flist->used;
+ if (verbose > 3)
+ output_flist(flist);
+
+ if (DIR_FIRST_CHILD(dp) >= 0) {
+ send_dir_ndx = DIR_FIRST_CHILD(dp);
+ send_dir_depth++;
+ } else {
+ while (DIR_NEXT_SIBLING(dp) < 0) {
+ if ((send_dir_ndx = DIR_PARENT(dp)) < 0) {
+ write_ndx(f, NDX_FLIST_EOF);
+ flist_eof = 1;
+ change_local_filter_dir(NULL, 0, 0);
+ goto finish;
+ }
+ send_dir_depth--;
+ file = dir_flist->sorted[send_dir_ndx];
+ dp = F_DIR_NODE_P(file);
+ }
+ send_dir_ndx = DIR_NEXT_SIBLING(dp);
+ }
+ }
+
+ finish:
+ if (io_error != save_io_error && !ignore_errors)
+ send_msg_int(MSG_IO_ERROR, io_error);
+}
+
+struct file_list *send_file_list(int f, int argc, char *argv[])
+{
+ static const char *lastdir;
+ static int lastdir_len = -1;
+ int len, dirlen;
+ STRUCT_STAT st;
+ char *p, *dir;
+ struct file_list *flist;
+ struct timeval start_tv, end_tv;
+ int64 start_write;
+ int use_ff_fd = 0;
+ int disable_buffering;
+ int flags = recurse ? FLAG_CONTENT_DIR : 0;
+ int reading_remotely = filesfrom_host != NULL;
+ int rl_flags = (reading_remotely ? 0 : RL_DUMP_COMMENTS)
+#ifdef ICONV_OPTION
+ | (filesfrom_convert ? RL_CONVERT : 0)
+#endif
+ | (eol_nulls || reading_remotely ? RL_EOL_NULLS : 0);
+ int implied_dot_dir = 0;
+
+ rprintf(FLOG, "building file list\n");
+ if (show_filelist_p())
+ start_filelist_progress("building file list");
+ else if (inc_recurse && verbose && !am_server)
+ rprintf(FCLIENT, "sending incremental file list\n");
+
+ start_write = stats.total_written;
+ gettimeofday(&start_tv, NULL);
+
+ if (relative_paths && protocol_version >= 30)
+ implied_dirs = 1; /* We send flagged implied dirs */
+
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && protocol_version >= 30 && !cur_flist)
+ init_hard_links();
+#endif
+
+ flist = cur_flist = flist_new(0, "send_file_list");
+ if (inc_recurse) {
+ dir_flist = flist_new(FLIST_TEMP, "send_file_list");
+ flags |= FLAG_DIVERT_DIRS;
+ } else
+ dir_flist = cur_flist;
+
+ 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);
+ exit_cleanup(RERR_FILESELECT);
+ }
+ use_ff_fd = 1;
+ }
+
+ while (1) {
+ char fbuf[MAXPATHLEN], *fn, name_type;
+
+ if (use_ff_fd) {
+ if (read_line(filesfrom_fd, fbuf, sizeof fbuf, rl_flags) == 0)
+ break;
+ sanitize_path(fbuf, fbuf, "", 0, SP_KEEP_DOT_DIRS);
+ } else {
+ if (argc-- == 0)
+ break;
+ strlcpy(fbuf, *argv++, MAXPATHLEN);
+ if (sanitize_paths)
+ sanitize_path(fbuf, fbuf, "", 0, SP_KEEP_DOT_DIRS);
+ }
+
+ len = strlen(fbuf);
+ if (relative_paths) {
+ /* We clean up fbuf below. */
+ name_type = NORMAL_NAME;
+ } else if (!len || fbuf[len - 1] == '/') {
+ if (len == 2 && fbuf[0] == '.') {
+ /* Turn "./" into just "." rather than "./." */
+ fbuf[--len] = '\0';
+ } else {
+ if (len + 1 >= MAXPATHLEN)
+ overflow_exit("send_file_list");
+ fbuf[len++] = '.';
+ fbuf[len] = '\0';
+ }
+ name_type = DOT_NAME;
+ } else if (len > 1 && fbuf[len-1] == '.' && fbuf[len-2] == '.'
+ && (len == 2 || fbuf[len-3] == '/')) {
+ if (len + 2 >= MAXPATHLEN)
+ overflow_exit("send_file_list");
+ fbuf[len++] = '/';
+ fbuf[len++] = '.';
+ fbuf[len] = '\0';
+ name_type = DOT_NAME;
+ } else if (fbuf[len-1] == '.' && (len == 1 || fbuf[len-2] == '/'))
+ name_type = DOT_NAME;
+ else
+ name_type = NORMAL_NAME;
+