X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/044ccbaacf2bd1bd502c58b802e33d95c08dbff6..82ad07c4182f744c07b96a15df4572e559ed7dc8:/main.c diff --git a/main.c b/main.c index 0a241a36..28334357 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_sender_files; +extern int remove_source_files; extern int daemon_over_rsh; extern int need_messages_from_generator; extern int kluge_around_eof; @@ -51,6 +51,7 @@ extern int recurse; extern int relative_paths; extern int sanitize_paths; extern int curr_dir_depth; +extern int curr_dir_len; extern int module_id; extern int rsync_port; extern int whole_file; @@ -67,9 +68,11 @@ 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; +int new_root_dir = 0; mode_t orig_umask = 0; struct file_list *the_file_list; @@ -235,7 +238,8 @@ static void output_summary(void) human_num(stats.literal_data)); rprintf(FINFO,"Matched data: %s bytes\n", human_num(stats.matched_data)); - rprintf(FINFO,"File list size: %d\n", stats.flist_size); + rprintf(FINFO,"File list size: %s\n", + human_num(stats.flist_size)); if (stats.flist_buildtime) { rprintf(FINFO, "File list generation time: %.3f seconds\n", @@ -474,20 +478,16 @@ static char *get_local_name(struct file_list *flist, char *dest_path) return NULL; /* See what currently exists at the destination. */ - if ((statret = safe_stat(dest_path, &st)) == 0) { + 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 (sanitize_paths) - die_on_unsafe_path(dest_path, 0); - if (!push_dir(dest_path)) { + if (!push_dir(dest_path, 0)) { rsyserr(FERROR, errno, "push_dir#1 %s failed", full_fname(dest_path)); exit_cleanup(RERR_FILESELECT); } 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" @@ -530,19 +530,17 @@ static char *get_local_name(struct file_list *flist, char *dest_path) exit_cleanup(RERR_FILEIO); } + new_root_dir = 1; + if (verbose) rprintf(FINFO, "created directory %s\n", dest_path); if (dry_run) { - /* Indicate that the destination directory doesn't - * really exist and return mode 1. */ + /* Indicate that dest dir doesn't really exist. */ dry_run++; - return NULL; } - if (sanitize_paths) - die_on_unsafe_path(dest_path, 0); - if (!push_dir(dest_path)) { + if (!push_dir(dest_path, dry_run > 1)) { rsyserr(FERROR, errno, "push_dir#2 %s failed", full_fname(dest_path)); exit_cleanup(RERR_FILESELECT); @@ -562,9 +560,7 @@ 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)) { + if (!push_dir(dest_path, 0)) { rsyserr(FERROR, errno, "push_dir#3 %s failed", full_fname(dest_path)); exit_cleanup(RERR_FILESELECT); @@ -574,6 +570,38 @@ static char *get_local_name(struct file_list *flist, char *dest_path) return cp + 1; } +/* Call this if the destination dir (which is assumed to be in curr_dir) + * does not yet exist and we can't create it due to being in dry-run + * mode. We'll fix dirs that can be relative to the non-existent dir. */ +static void fix_basis_dirs(void) +{ + char **dir, *new, *slash; + int len; + + if (dry_run <= 1) + return; + + slash = strrchr(curr_dir, '/'); + + for (dir = basis_dir; *dir; dir++) { + if (**dir == '/') + continue; + len = curr_dir_len + 1 + strlen(*dir) + 1; + if (!(new = new_array(char, len))) + out_of_memory("fix_basis_dirs"); + if (slash && strncmp(*dir, "../", 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 + 3); + *slash = '/'; + } else + pathjoin(new, len, curr_dir, *dir); + *dir = new; + } +} /* This is only called by the sender. */ static void read_final_goodbye(int f_in, int f_out) @@ -591,7 +619,7 @@ static void read_final_goodbye(int f_in, int f_out) } } - if (i != -1) { + if (i != NDX_DONE) { rprintf(FERROR, "Invalid packet at end of run (%d) [%s]\n", i, who_am_i()); exit_cleanup(RERR_PROTOCOL); @@ -614,18 +642,16 @@ 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_sender_files) { + if (am_daemon && lp_read_only(module_id) && remove_source_files) { rprintf(FERROR, "ERROR: --remove-%s-files cannot be used with a read-only module\n", - remove_sender_files == 1 ? "sender" : "sent"); + remove_source_files == 1 ? "source" : "sent"); exit_cleanup(RERR_SYNTAX); return; } if (!relative_paths) { - if (sanitize_paths) - die_on_unsafe_path(dir, 0); - if (!push_dir(dir)) { + if (!push_dir(dir, 0)) { rsyserr(FERROR, errno, "push_dir#3 %s failed", full_fname(dir)); exit_cleanup(RERR_FILESELECT); @@ -742,7 +768,7 @@ static int do_recv(int f_in,int f_out,struct file_list *flist,char *local_name) io_flush(FULL_FLUSH); if (protocol_version >= 24) { /* send a final goodbye message */ - write_int(f_out, -1); + write_int(f_out, NDX_DONE); } io_flush(FULL_FLUSH); @@ -782,7 +808,7 @@ static void do_server_recv(int f_in, int f_out, int argc,char *argv[]) dir = argv[0]; argc--; argv++; - if (!am_daemon && !push_dir(dir)) { + if (!am_daemon && !push_dir(dir, 0)) { rsyserr(FERROR, errno, "push_dir#4 %s failed", full_fname(dir)); exit_cleanup(RERR_FILESELECT); @@ -819,15 +845,13 @@ static void do_server_recv(int f_in, int f_out, int argc,char *argv[]) char **dir; 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); } } + fix_basis_dirs(); + if (server_filter_list.head) { char **dir; struct filter_list_struct *elp = &server_filter_list; @@ -975,6 +999,8 @@ int client_run(int f_in, int f_out, pid_t pid, int argc, char *argv[]) if (flist && flist->count > 0) { local_name = get_local_name(flist, argv[0]); + fix_basis_dirs(); + exit_code2 = do_recv(f_in, f_out, flist, local_name); } else { handle_stats(-1); @@ -1233,8 +1259,8 @@ static RETSIGTYPE rsync_panic_handler(UNUSED(int whatsig)) char cmd_buf[300]; int ret; - sprintf(cmd_buf, get_panic_action(), - getpid(), getpid()); + snprintf(cmd_buf, sizeof cmd_buf, get_panic_action(), + getpid(), getpid()); /* Unless we failed to execute gdb, we allow the process to * continue. I'm not sure if that's right. */ @@ -1311,7 +1337,7 @@ int main(int argc,char *argv[]) * (implemented by forking "pwd" and reading its output) doesn't * work when there are other child processes. Also, on all systems * that implement getcwd that way "pwd" can't be found after chroot. */ - push_dir(NULL); + push_dir(NULL, 0); init_flist();