+ 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);
+
+#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;
+ flags = 0;
+ }
+
+ disable_buffering = io_start_buffering_out(f);
+ if (filesfrom_fd >= 0) {
+ if (argv[0] && !push_dir(argv[0], 0)) {
+ rsyserr(FERROR, 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];
+ char *fn;
+ int is_dot_dir;
+
+ if (use_ff_fd) {
+ if (read_filesfrom_line(filesfrom_fd, fbuf) == 0)
+ break;
+ sanitize_path(fbuf, fbuf, "", 0, NULL);
+ } else {
+ if (argc-- == 0)
+ break;
+ strlcpy(fbuf, *argv++, MAXPATHLEN);
+ if (sanitize_paths)
+ sanitize_path(fbuf, fbuf, "", 0, NULL);
+ }
+
+ len = strlen(fbuf);
+ if (relative_paths) {
+ /* We clean up fbuf below. */
+ is_dot_dir = 0;
+ } else if (!len || fbuf[len - 1] == '/') {
+ if (len == 2 && fbuf[0] == '.') {
+ /* Turn "./" into just "." rather than "./." */
+ fbuf[1] = '\0';
+ } else {
+ if (len + 1 >= MAXPATHLEN)
+ overflow_exit("send_file_list");
+ fbuf[len++] = '.';
+ fbuf[len] = '\0';
+ }
+ is_dot_dir = 1;
+ } 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';
+ is_dot_dir = 1;
+ } else {
+ is_dot_dir = fbuf[len-1] == '.'
+ && (len == 1 || fbuf[len-2] == '/');
+ }
+
+ dir = NULL;
+
+ if (!relative_paths) {
+ p = strrchr(fbuf, '/');
+ if (p) {
+ *p = '\0';
+ if (p == fbuf)
+ dir = "/";
+ else
+ dir = fbuf;
+ len -= p - fbuf + 1;
+ fn = p + 1;
+ } else
+ fn = fbuf;
+ } else {
+ if ((p = strstr(fbuf, "/./")) != NULL) {
+ *p = '\0';
+ if (p == fbuf)
+ dir = "/";
+ else
+ dir = fbuf;
+ len -= p - fbuf + 3;
+ fn = p + 3;
+ } else
+ fn = fbuf;
+ /* Get rid of trailing "/" and "/.". */
+ while (len) {
+ if (fn[len - 1] == '/') {
+ is_dot_dir = 1;
+ if (!--len && !dir) {
+ len++;
+ break;
+ }
+ }
+ else if (len >= 2 && fn[len - 1] == '.'
+ && fn[len - 2] == '/') {
+ is_dot_dir = 1;
+ if (!(len -= 2) && !dir) {
+ len++;
+ break;
+ }
+ } else
+ break;
+ }
+ if (len == 1 && fn[0] == '/')
+ fn[len++] = '.';
+ fn[len] = '\0';
+ /* Reject a ".." dir in the active part of the path. */
+ for (p = fn; (p = strstr(p, "..")) != NULL; p += 2) {
+ if ((p[2] == '/' || p[2] == '\0')
+ && (p == fn || p[-1] == '/')) {
+ rprintf(FERROR,
+ "found \"..\" dir in relative path: %s\n",
+ fbuf);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ }
+ }
+
+ if (!*fn) {
+ len = 1;
+ fn = ".";
+ }
+
+ dirlen = dir ? strlen(dir) : 0;
+ if (dirlen != lastdir_len || memcmp(lastdir, dir, dirlen) != 0) {
+ if (!push_pathname(dir ? strdup(dir) : NULL, dirlen))
+ goto push_error;
+ lastdir = pathname;
+ lastdir_len = pathname_len;
+ } else if (!push_pathname(lastdir, lastdir_len)) {
+ push_error:
+ io_error |= IOERR_GENERAL;
+ rsyserr(FERROR, errno, "push_dir %s failed in %s",
+ full_fname(dir), curr_dir);
+ continue;
+ }
+
+ if (fn != fbuf)
+ memmove(fbuf, fn, len + 1);
+
+ if (link_stat(fbuf, &st, copy_dirlinks) != 0) {
+ io_error |= IOERR_GENERAL;
+ rsyserr(FERROR, errno, "link_stat %s failed",
+ full_fname(fbuf));
+ continue;
+ }
+
+ if (S_ISDIR(st.st_mode) && !xfer_dirs) {
+ rprintf(FINFO, "skipping directory %s\n", fbuf);
+ continue;
+ }
+
+ 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 != '/')) {
+ int save_copy_links = copy_links;
+ int save_xfer_dirs = xfer_dirs;
+ int dir_flags = inc_recurse ? FLAG_DIVERT_DIRS : 0;
+ copy_links |= copy_unsafe_links;
+ xfer_dirs = 1;
+ while ((slash = strchr(slash+1, '/')) != 0) {
+ *slash = '\0';
+ send_file_name(f, flist, fbuf, NULL,
+ dir_flags, ALL_FILTERS);
+ *slash = '/';
+ }
+ copy_links = save_copy_links;
+ xfer_dirs = save_xfer_dirs;
+ *p = '\0';
+ strlcpy(lastpath, fbuf, sizeof lastpath);
+ *p = '/';
+ }
+ }
+
+ if (one_file_system)
+ filesystem_dev = st.st_dev;
+
+ if (recurse || (xfer_dirs && is_dot_dir)) {
+ struct file_struct *file;
+ int top_flags = FLAG_TOP_DIR | FLAG_XFER_DIR | flags;
+ file = send_file_name(f, flist, fbuf, &st,
+ top_flags, ALL_FILTERS);
+ if (file && !inc_recurse)
+ send_if_directory(f, flist, file, fbuf, len, flags);
+ } else
+ send_file_name(f, flist, fbuf, &st, flags, ALL_FILTERS);
+ }
+
+ gettimeofday(&end_tv, NULL);
+ stats.flist_buildtime = (int64)(end_tv.tv_sec - start_tv.tv_sec) * 1000
+ + (end_tv.tv_usec - start_tv.tv_usec) / 1000;
+ if (stats.flist_buildtime == 0)
+ stats.flist_buildtime = 1;
+ start_tv = end_tv;
+
+ write_byte(f, 0); /* Indicate end of file list */
+
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && protocol_version >= 30 && !inc_recurse)
+ idev_destroy();
+#endif
+
+ if (show_filelist_p())
+ finish_filelist_progress(flist);
+
+ gettimeofday(&end_tv, NULL);
+ stats.flist_xfertime = (int64)(end_tv.tv_sec - start_tv.tv_sec) * 1000
+ + (end_tv.tv_usec - start_tv.tv_usec) / 1000;
+
+ /* When converting names, both sides keep an unsorted file-list array
+ * because the names will differ on the sending and receiving sides
+ * (both sides will use the unsorted index number for each item). */
+
+ /* Sort the list without removing any duplicates. This allows the
+ * receiving side to ask for whatever name it kept. For incremental
+ * recursion mode, the sender marks duplicate dirs so that it can
+ * send them together in a single file-list. */
+#ifdef ICONV_OPTION
+ if (need_unsorted_flist) {
+ if (inc_recurse) {
+ if (!(flist->sorted = new_array(struct file_struct *, flist->count)))
+ out_of_memory("send_file_list");
+ memcpy(flist->sorted, flist->files,
+ flist->count * sizeof (struct file_struct*));
+ clean_flist(flist, 0);
+ } else
+ flist->sorted = flist->files;
+ } else
+#endif
+ {
+ flist->sorted = flist->files;
+ clean_flist(flist, 0);
+ }
+ file_total += flist->count;
+
+ if (!numeric_ids && !inc_recurse)
+ send_id_list(f);
+
+ /* send the io_error flag */
+ if (protocol_version < 30)
+ write_int(f, ignore_errors ? 0 : io_error);
+ else if (io_error && !ignore_errors)
+ send_msg_int(MSG_IO_ERROR, io_error);
+
+ if (disable_buffering)
+ io_end_buffering_out();
+
+ stats.flist_size = stats.total_written - start_write;
+ stats.num_files = flist->count;
+
+ if (verbose > 3)
+ output_flist(flist);
+
+ if (verbose > 2)
+ rprintf(FINFO, "send_file_list done\n");
+
+ if (inc_recurse) {
+#ifdef ICONV_OPTION
+ if (!need_unsorted_flist)
+#endif
+ dir_flist->sorted = dir_flist->files;
+ add_dirs_to_tree(-1, 0, dir_flist->count - 1);
+ if (send_dir_ndx < 0) {
+ write_ndx(f, NDX_FLIST_EOF);
+ flist_eof = 1;
+ }
+ else if (file_total == 1) {
+ /* If we're creating incremental file-lists and there
+ * was just 1 item in the first file-list, send 1 more
+ * file-list to check if this is a 1-file xfer. */
+ send_extra_file_list(f, 1);
+ }
+ }
+
+ return flist;
+}
+
+struct file_list *recv_file_list(int f)
+{
+ struct file_list *flist;
+ int dstart, flags;
+ int64 start_read;
+
+ if (!first_flist)
+ rprintf(FLOG, "receiving file list\n");
+ if (show_filelist_p())
+ start_filelist_progress("receiving file list");
+ else if (inc_recurse && verbose && !am_server && !first_flist)
+ rprintf(FCLIENT, "receiving incremental file list\n");
+
+ start_read = stats.total_read;
+
+ flist = flist_new(0, "recv_file_list");
+
+#ifdef SUPPORT_HARD_LINKS
+ if (preserve_hard_links && protocol_version < 30)
+ init_hard_links();
+#endif
+
+ if (inc_recurse) {
+ if (flist->ndx_start == 0)
+ dir_flist = flist_new(FLIST_TEMP, "recv_file_list");
+ dstart = dir_flist->count;
+ } else {
+ dir_flist = flist;
+ dstart = 0;
+ }
+
+ while ((flags = read_byte(f)) != 0) {
+ struct file_struct *file;
+
+ flist_expand(flist);
+
+ if (protocol_version >= 28 && (flags & XMIT_EXTENDED_FLAGS))
+ flags |= read_byte(f) << 8;
+ file = recv_file_entry(flist, flags, f);
+
+ if (inc_recurse && S_ISDIR(file->mode)) {
+ flist_expand(dir_flist);
+#ifdef ICONV_OPTION
+ if (need_unsorted_flist)
+ dir_flist->sorted[dir_flist->count] = file;
+#endif
+ dir_flist->files[dir_flist->count++] = file;
+ }
+
+ flist->files[flist->count++] = file;
+
+ maybe_emit_filelist_progress(flist->count);
+
+ if (verbose > 2) {
+ rprintf(FINFO, "recv_file_name(%s)\n",
+ f_name(file, NULL));
+ }
+ }
+ file_total += flist->count;
+
+ if (verbose > 2)
+ rprintf(FINFO, "received %d names\n", flist->count);
+
+ if (show_filelist_p())
+ finish_filelist_progress(flist);
+
+#ifdef ICONV_OPTION
+ if (need_unsorted_flist) {
+ /* Create an extra array of index pointers that we can sort for
+ * the generator's use (for wading through the files in sorted
+ * order and for calling flist_find()). We keep the "files"
+ * list unsorted for our exchange of index numbers with the
+ * other side (since their names may not sort the same). */
+ if (!(flist->sorted = new_array(struct file_struct *, flist->count)))
+ out_of_memory("recv_file_list");
+ memcpy(flist->sorted, flist->files,
+ flist->count * sizeof (struct file_struct*));
+ if (inc_recurse) {
+ qsort(dir_flist->sorted + dstart, dir_flist->count - dstart,
+ sizeof (struct file_struct*), (int (*)())file_compare);
+ }
+ } else
+#endif
+ {
+ flist->sorted = flist->files;
+ if (inc_recurse) {
+ dir_flist->sorted = dir_flist->files;
+ qsort(dir_flist->sorted + dstart, dir_flist->count - dstart,
+ sizeof (struct file_struct*), (int (*)())file_compare);
+ }
+ }
+
+ if (!inc_recurse && f >= 0)
+ recv_id_list(f, flist);
+
+ clean_flist(flist, relative_paths);
+
+ if (protocol_version < 30) {
+ /* Recv the io_error flag */
+ if (ignore_errors)
+ read_int(f);
+ else
+ io_error |= read_int(f);
+ }
+
+ if (verbose > 3)
+ output_flist(flist);
+
+ if (list_only) {
+ int i;
+ for (i = 0; i < flist->count; i++)
+ list_file_entry(flist->files[i]);
+ }
+
+ if (verbose > 2)
+ rprintf(FINFO, "recv_file_list done\n");
+
+ stats.flist_size += stats.total_read - start_read;
+ stats.num_files += flist->count;
+
+ return flist;
+}
+
+/* This is only used once by the receiver if the very first file-list
+ * has exactly one item in it. */
+void recv_additional_file_list(int f)