+
+ args[argc++] = rsync_path;
+
+ if (blocking_io < 0) {
+ char *cp;
+ if ((cp = strrchr(cmd, '/')) != NULL)
+ cp++;
+ else
+ cp = cmd;
+ if (strcmp(cp, "rsh") == 0 || strcmp(cp, "remsh") == 0)
+ blocking_io = 1;
+ }
+
+ server_options(args,&argc);
+
+ if (argc >= MAX_ARGS - 2)
+ goto arg_overflow;
+ }
+
+ args[argc++] = ".";
+
+ if (!daemon_over_rsh) {
+ while (remote_argc > 0) {
+ if (argc >= MAX_ARGS - 1) {
+ arg_overflow:
+ rprintf(FERROR, "internal: args[] overflowed in do_cmd()\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ args[argc++] = *remote_argv++;
+ remote_argc--;
+ }
+ }
+
+ args[argc] = NULL;
+
+ if (DEBUG_GTE(CMD, 2)) {
+ for (i = 0; i < argc; i++)
+ rprintf(FCLIENT, "cmd[%d]=%s ", i, args[i]);
+ rprintf(FCLIENT, "\n");
+ }
+
+ if (read_batch) {
+ int from_gen_pipe[2];
+ set_allow_inc_recurse();
+ if (fd_pair(from_gen_pipe) < 0) {
+ rsyserr(FERROR, errno, "pipe");
+ exit_cleanup(RERR_IPC);
+ }
+ batch_gen_fd = from_gen_pipe[0];
+ *f_out_p = from_gen_pipe[1];
+ *f_in_p = batch_fd;
+ pid = (pid_t)-1; /* no child pid */
+#ifdef ICONV_CONST
+ setup_iconv();
+#endif
+ } else if (local_server) {
+ /* If the user didn't request --[no-]whole-file, force
+ * it on, but only if we're not batch processing. */
+ if (whole_file < 0 && !write_batch)
+ whole_file = 1;
+ set_allow_inc_recurse();
+ pid = local_child(argc, args, f_in_p, f_out_p, child_main);
+#ifdef ICONV_CONST
+ setup_iconv();
+#endif
+ } else {
+ pid = piped_child(args, f_in_p, f_out_p);
+#ifdef ICONV_CONST
+ setup_iconv();
+#endif
+ if (protect_args)
+ send_protected_args(*f_out_p, args);
+ }
+
+ return pid;
+
+ oom:
+ out_of_memory("do_cmd");
+ return 0; /* not reached */
+}
+
+/* The receiving side operates in one of two modes:
+ *
+ * 1. it receives any number of files into a destination directory,
+ * placing them according to their names in the file-list.
+ *
+ * 2. it receives a single file and saves it using the name in the
+ * destination path instead of its file-list name. This requires a
+ * "local name" for writing out the destination file.
+ *
+ * So, our task is to figure out what mode/local-name we need.
+ * For mode 1, we change into the destination directory and return NULL.
+ * For mode 2, we change into the directory containing the destination
+ * file (if we aren't already there) and return the local-name. */
+static char *get_local_name(struct file_list *flist, char *dest_path)
+{
+ STRUCT_STAT st;
+ int statret;
+ char *cp;
+
+ if (DEBUG_GTE(RECV, 1)) {
+ rprintf(FINFO, "get_local_name count=%d %s\n",
+ file_total, NS(dest_path));
+ }
+
+ if (!dest_path || list_only)
+ return NULL;
+
+ if (daemon_filter_list.head) {
+ char *slash = strrchr(dest_path, '/');
+ if (slash && (slash[1] == '\0' || (slash[1] == '.' && slash[2] == '\0')))
+ *slash = '\0';
+ else
+ slash = NULL;
+ if ((*dest_path != '.' || dest_path[1] != '\0')
+ && (check_filter(&daemon_filter_list, FLOG, dest_path, 0) < 0
+ || check_filter(&daemon_filter_list, FLOG, dest_path, 1) < 0)) {
+ rprintf(FERROR, "skipping daemon-excluded destination \"%s\"\n",
+ dest_path);
+ exit_cleanup(RERR_FILESELECT);
+ }
+ if (slash)
+ *slash = '/';
+ }
+
+ /* See what currently exists at the destination. */
+ if ((statret = do_stat(dest_path, &st)) == 0) {
+ /* If the destination is a dir, enter it and use mode 1. */
+ if (S_ISDIR(st.st_mode)) {
+ if (!change_dir(dest_path, CD_NORMAL)) {
+ rsyserr(FERROR, errno, "change_dir#1 %s failed",
+ full_fname(dest_path));
+ exit_cleanup(RERR_FILESELECT);
+ }
+ return NULL;
+ }
+ if (file_total > 1) {
+ rprintf(FERROR,
+ "ERROR: destination must be a directory when"
+ " copying more than 1 file\n");
+ exit_cleanup(RERR_FILESELECT);
+ }
+ if (file_total == 1 && S_ISDIR(flist->files[0]->mode)) {
+ rprintf(FERROR,
+ "ERROR: cannot overwrite non-directory"
+ " with a directory\n");
+ exit_cleanup(RERR_FILESELECT);
+ }
+ } else if (errno != ENOENT) {
+ /* If we don't know what's at the destination, fail. */
+ rsyserr(FERROR, errno, "ERROR: cannot stat destination %s",
+ full_fname(dest_path));
+ exit_cleanup(RERR_FILESELECT);
+ }
+
+ cp = strrchr(dest_path, '/');
+
+ /* If we need a destination directory because the transfer is not
+ * of a single non-directory or the user has requested one via a
+ * destination path ending in a slash, create one and use mode 1. */
+ if (file_total > 1 || (cp && !cp[1])) {
+ /* Lop off the final slash (if any). */
+ if (cp && !cp[1])
+ *cp = '\0';
+
+ if (statret == 0) {
+ rprintf(FERROR,
+ "ERROR: destination path is not a directory\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+
+ if (mkdir_defmode(dest_path) != 0) {
+ rsyserr(FERROR, errno, "mkdir %s failed",
+ full_fname(dest_path));
+ exit_cleanup(RERR_FILEIO);
+ }
+
+ if (flist->high >= flist->low
+ && strcmp(flist->files[flist->low]->basename, ".") == 0)
+ flist->files[0]->flags |= FLAG_DIR_CREATED;
+
+ if (INFO_GTE(NAME, 1))
+ rprintf(FINFO, "created directory %s\n", dest_path);
+
+ if (dry_run) {
+ /* Indicate that dest dir doesn't really exist. */
+ dry_run++;
+ }
+
+ if (!change_dir(dest_path, dry_run > 1 ? CD_SKIP_CHDIR : CD_NORMAL)) {
+ rsyserr(FERROR, errno, "change_dir#2 %s failed",
+ full_fname(dest_path));
+ exit_cleanup(RERR_FILESELECT);
+ }
+
+ return NULL;
+ }
+
+ /* Otherwise, we are writing a single file, possibly on top of an
+ * existing non-directory. Change to the item's parent directory
+ * (if it has a path component), return the basename of the
+ * destination file as the local name, and use mode 2. */
+ if (!cp)
+ return dest_path;
+
+ if (cp == dest_path)
+ dest_path = "/";
+
+ *cp = '\0';
+ if (!change_dir(dest_path, CD_NORMAL)) {
+ rsyserr(FERROR, errno, "change_dir#3 %s failed",
+ full_fname(dest_path));
+ exit_cleanup(RERR_FILESELECT);
+ }
+ *cp = '/';
+
+ return cp + 1;
+}
+
+/* This function checks on our alternate-basis directories. If we're in
+ * dry-run mode and the destination dir does not yet exist, we'll try to
+ * tweak any dest-relative paths to make them work for a dry-run (the
+ * destination dir must be in curr_dir[] when this function is called).
+ * We also warn about any arg that is non-existent or not a directory. */
+static void check_alt_basis_dirs(void)
+{
+ STRUCT_STAT st;
+ char **dir_p, *slash = strrchr(curr_dir, '/');
+
+ for (dir_p = basis_dir; *dir_p; dir_p++) {
+ if (dry_run > 1 && **dir_p != '/') {
+ int len = curr_dir_len + 1 + strlen(*dir_p) + 1;
+ char *new = new_array(char, len);
+ if (!new)
+ out_of_memory("check_alt_basis_dirs");
+ if (slash && strncmp(*dir_p, "../", 3) == 0) {
+ /* We want to remove only one leading "../" prefix for
+ * the directory we couldn't create in dry-run mode:
+ * this ensures that any other ".." references get
+ * evaluated the same as they would for a live copy. */
+ *slash = '\0';
+ pathjoin(new, len, curr_dir, *dir_p + 3);
+ *slash = '/';
+ } else
+ pathjoin(new, len, curr_dir, *dir_p);
+ *dir_p = new;
+ }
+ if (do_stat(*dir_p, &st) < 0) {
+ rprintf(FWARNING, "%s arg does not exist: %s\n",
+ dest_option, *dir_p);
+ } else if (!S_ISDIR(st.st_mode)) {
+ rprintf(FWARNING, "%s arg is not a dir: %s\n",
+ dest_option, *dir_p);
+ }
+ }
+}
+
+/* This is only called by the sender. */
+static void read_final_goodbye(int f_in)
+{
+ int i, iflags, xlen;
+ uchar fnamecmp_type;
+ char xname[MAXPATHLEN];
+
+ if (protocol_version < 29)
+ i = read_int(f_in);
+ else {
+ i = read_ndx_and_attrs(f_in, &iflags, &fnamecmp_type,
+ xname, &xlen);
+ }
+
+ if (i != NDX_DONE) {
+ rprintf(FERROR, "Invalid packet at end of run (%d) [%s]\n",
+ i, who_am_i());
+ exit_cleanup(RERR_PROTOCOL);
+ }