X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/7c58c991840380a34ae9b4cd7befa7da4540683b..47c1197534c3247a2669df86fbc36d4094d6ba93:/main.c diff --git a/main.c b/main.c index 75a3ca04..c37b3c79 100644 --- a/main.c +++ b/main.c @@ -35,7 +35,7 @@ extern int am_sender; extern int am_generator; extern int am_daemon; extern int blocking_io; -extern int remove_sent_files; +extern int remove_source_files; extern int daemon_over_rsh; extern int need_messages_from_generator; extern int kluge_around_eof; @@ -50,6 +50,7 @@ extern int protocol_version; extern int recurse; extern int relative_paths; extern int sanitize_paths; +extern int curr_dir_depth; extern int module_id; extern int rsync_port; extern int whole_file; @@ -61,12 +62,12 @@ extern int filesfrom_fd; extern pid_t cleanup_child_pid; extern struct stats stats; extern char *filesfrom_host; +extern char *partial_dir; extern char *basis_dir[]; extern char *rsync_path; extern char *shell_cmd; extern char *batch_name; - -extern char curr_dir[MAXPATHLEN]; +extern struct filter_list_struct server_filter_list; int local_server = 0; mode_t orig_umask = 0; @@ -175,7 +176,6 @@ static void handle_stats(int f) return; if (am_daemon) { - log_exit(0, __FILE__, __LINE__); if (f == -1 || !am_sender) return; } @@ -223,7 +223,8 @@ static void handle_stats(int f) static void output_summary(void) { if (do_stats) { - rprintf(FINFO,"\nNumber of files: %d\n", stats.num_files); + rprintf(FCLIENT, "\n"); + rprintf(FINFO,"Number of files: %d\n", stats.num_files); rprintf(FINFO,"Number of files transferred: %d\n", stats.num_transferred_files); rprintf(FINFO,"Total file size: %s bytes\n", @@ -250,8 +251,9 @@ static void output_summary(void) } if (verbose || do_stats) { + rprintf(FCLIENT, "\n"); rprintf(FINFO, - "\nsent %s bytes received %s bytes %s bytes/sec\n", + "sent %s bytes received %s bytes %s bytes/sec\n", human_num(total_written), human_num(total_read), human_dnum((total_written + total_read)/(0.5 + (endtime - starttime)), 2)); rprintf(FINFO, "total size is %s speedup is %.2f\n", @@ -274,7 +276,8 @@ static void show_malloc_stats(void) mi = mallinfo(); - rprintf(FINFO, "\n" RSYNC_NAME "[%d] (%s%s%s) heap statistics:\n", + rprintf(FCLIENT, "\n"); + rprintf(FINFO, RSYNC_NAME "[%d] (%s%s%s) heap statistics:\n", getpid(), am_server ? "server " : "", am_daemon ? "daemon " : "", who_am_i()); rprintf(FINFO, " arena: %10ld (bytes from sbrk)\n", @@ -410,8 +413,8 @@ static pid_t do_cmd(char *cmd, char *machine, char *user, char *path, if (verbose > 3) { for (i = 0; i < argc; i++) - rprintf(FINFO, "cmd[%d]=%s ", i, args[i]); - rprintf(FINFO, "\n"); + rprintf(FCLIENT, "cmd[%d]=%s ", i, args[i]); + rprintf(FCLIENT, "\n"); } if (read_batch) { @@ -452,12 +455,14 @@ static pid_t do_cmd(char *cmd, char *machine, char *user, char *path, * 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 and return - * either a NULL for mode 1, or the local-name for mode 2. We also - * change directory if there are any path components in dest_path. */ + * 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 (verbose > 2) { @@ -468,12 +473,12 @@ static char *get_local_name(struct file_list *flist, char *dest_path) if (!dest_path || list_only) return NULL; - /* If the destination path refers to an existing directory, enter - * it and use mode 1. If there is something other than a directory - * at the destination path, we must be transferring one file - * (anything at the destination will be overwritten). */ - if (do_stat(dest_path, &st) == 0) { + /* See what currently exists at the destination. */ + if ((statret = safe_stat(dest_path, &st)) == 0) { + /* If the destination is a dir, enter it and use mode 1. */ if (S_ISDIR(st.st_mode)) { + if (sanitize_paths) + die_on_unsafe_path(dest_path, 0); if (!push_dir(dest_path)) { rsyserr(FERROR, errno, "push_dir#1 %s failed", full_fname(dest_path)); @@ -481,6 +486,8 @@ static char *get_local_name(struct file_list *flist, char *dest_path) } return NULL; } + if (sanitize_paths && S_ISLNK(st.st_mode)) + die_on_unsafe_path(dest_path, 0); if (flist->count > 1) { rprintf(FERROR, "ERROR: destination must be a directory when" @@ -495,21 +502,28 @@ static char *get_local_name(struct file_list *flist, char *dest_path) exit_cleanup(RERR_FILESELECT); } } else if (errno != ENOENT) { - rsyserr(FERROR, errno, "cannot stat destination %s", + /* 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 the destination path ends in a slash or we are transferring - * multiple files, create a directory at the destination path, - * enter the new directory, and use mode 1. */ + /* 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 (flist->count > 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)); @@ -526,6 +540,8 @@ static char *get_local_name(struct file_list *flist, char *dest_path) return NULL; } + if (sanitize_paths) + die_on_unsafe_path(dest_path, 0); if (!push_dir(dest_path)) { rsyserr(FERROR, errno, "push_dir#2 %s failed", full_fname(dest_path)); @@ -546,6 +562,8 @@ static char *get_local_name(struct file_list *flist, char *dest_path) dest_path = "/"; *cp = '\0'; + if (sanitize_paths) + die_on_unsafe_path(dest_path, 0); if (!push_dir(dest_path)) { rsyserr(FERROR, errno, "push_dir#3 %s failed", full_fname(dest_path)); @@ -596,17 +614,22 @@ static void do_server_sender(int f_in, int f_out, int argc, char *argv[]) exit_cleanup(RERR_SYNTAX); return; } - if (am_daemon && lp_read_only(module_id) && remove_sent_files) { + if (am_daemon && lp_read_only(module_id) && remove_source_files) { rprintf(FERROR, - "ERROR: --remove-sent-files cannot be used with a read-only module\n"); + "ERROR: --remove-%s-files cannot be used with a read-only module\n", + remove_source_files == 1 ? "source" : "sent"); exit_cleanup(RERR_SYNTAX); return; } - if (!relative_paths && !push_dir(dir)) { - rsyserr(FERROR, errno, "push_dir#3 %s failed", - full_fname(dir)); - exit_cleanup(RERR_FILESELECT); + if (!relative_paths) { + if (sanitize_paths) + die_on_unsafe_path(dir, 0); + if (!push_dir(dir)) { + rsyserr(FERROR, errno, "push_dir#3 %s failed", + full_fname(dir)); + exit_cleanup(RERR_FILESELECT); + } } argc--; argv++; @@ -793,11 +816,33 @@ static void do_server_recv(int f_in, int f_out, int argc,char *argv[]) /* Now that we know what our destination directory turned out to be, * we can sanitize the --link-/copy-/compare-dest args correctly. */ if (sanitize_paths) { - char *dest_path = curr_dir + strlen(lp_path(module_id)); - int dest_depth = count_dir_elements(dest_path); char **dir; - for (dir = basis_dir; *dir; dir++) - *dir = sanitize_path(NULL, *dir, NULL, dest_depth); + for (dir = basis_dir; *dir; dir++) { + *dir = sanitize_path(NULL, *dir, NULL, curr_dir_depth, NULL); + die_on_unsafe_path(*dir, 0); + } + if (partial_dir) { + partial_dir = sanitize_path(NULL, partial_dir, NULL, curr_dir_depth, NULL); + /* A relative path gets this checked at every dir change. */ + if (*partial_dir == '/') + die_on_unsafe_path(partial_dir, 0); + } + } + if (server_filter_list.head) { + char **dir; + struct filter_list_struct *elp = &server_filter_list; + + for (dir = basis_dir; *dir; dir++) { + if (check_filter(elp, *dir, 1) < 0) + goto options_rejected; + } + if (partial_dir && *partial_dir == '/' + && check_filter(elp, partial_dir, 1) < 0) { + options_rejected: + rprintf(FERROR, + "Your options have been rejected by the server.\n"); + exit_cleanup(RERR_SYNTAX); + } } exit_code = do_recv(f_in,f_out,flist,local_name);