X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/615a5415c9f3b156f3f456688f8b702fa05deb8d..16edf86595a5a990a942fa045dfb523dae1fe6cb:/main.c diff --git a/main.c b/main.c index ed0ce0ad..1df9e2fa 100644 --- a/main.c +++ b/main.c @@ -4,12 +4,11 @@ * Copyright (C) 1996-2001 Andrew Tridgell * Copyright (C) 1996 Paul Mackerras * Copyright (C) 2001, 2002 Martin Pool - * Copyright (C) 2003, 2004, 2005, 2006 Wayne Davison + * Copyright (C) 2003-2007 Wayne Davison * * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -34,6 +33,7 @@ extern int am_server; extern int am_sender; extern int am_generator; extern int am_daemon; +extern int inc_recurse; extern int blocking_io; extern int remove_source_files; extern int daemon_over_rsh; @@ -47,6 +47,7 @@ extern int copy_dirlinks; extern int keep_dirlinks; extern int preserve_hard_links; extern int protocol_version; +extern int file_total; extern int recurse; extern int relative_paths; extern int sanitize_paths; @@ -58,7 +59,6 @@ extern int whole_file; extern int read_batch; extern int write_batch; extern int batch_fd; -extern int batch_gen_fd; extern int filesfrom_fd; extern pid_t cleanup_child_pid; extern struct stats stats; @@ -68,12 +68,14 @@ extern char *basis_dir[]; extern char *rsync_path; extern char *shell_cmd; extern char *batch_name; +extern char *password_file; 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; +int batch_gen_fd = -1; /* There's probably never more than at most 2 outstanding child processes, * but set it higher, just in case. */ @@ -102,7 +104,11 @@ static void show_malloc_stats(void); * remember_children(), we succeed instead of returning an error. */ pid_t wait_process(pid_t pid, int *status_ptr, int flags) { - pid_t waited_pid = waitpid(pid, status_ptr, flags); + pid_t waited_pid; + + do { + waited_pid = waitpid(pid, status_ptr, flags); + } while (waited_pid == -1 && errno == EINTR); if (waited_pid == -1 && errno == ECHILD) { /* Status of requested child no longer available: check to @@ -237,7 +243,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", @@ -469,18 +476,16 @@ static char *get_local_name(struct file_list *flist, char *dest_path) if (verbose > 2) { rprintf(FINFO, "get_local_name count=%d %s\n", - flist->count, NS(dest_path)); + file_total, NS(dest_path)); } if (!dest_path || list_only) 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, 0)) { rsyserr(FERROR, errno, "push_dir#1 %s failed", full_fname(dest_path)); @@ -488,16 +493,13 @@ 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) { + if (file_total > 1) { rprintf(FERROR, "ERROR: destination must be a directory when" " copying more than 1 file\n"); exit_cleanup(RERR_FILESELECT); } - /* Caution: flist->count could be 0! */ - if (flist->count == 1 && S_ISDIR(flist->files[0]->mode)) { + if (file_total == 1 && S_ISDIR(flist->files[0]->mode)) { rprintf(FERROR, "ERROR: cannot overwrite non-directory" " with a directory\n"); @@ -515,7 +517,7 @@ static char *get_local_name(struct file_list *flist, char *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 (flist->count > 1 || (cp && !cp[1])) { + if (file_total > 1 || (cp && !cp[1])) { /* Lop off the final slash (if any). */ if (cp && !cp[1]) *cp = '\0'; @@ -532,6 +534,8 @@ 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); @@ -540,8 +544,6 @@ static char *get_local_name(struct file_list *flist, char *dest_path) dry_run++; } - if (sanitize_paths) - die_on_unsafe_path(dest_path, 0); if (!push_dir(dest_path, dry_run > 1)) { rsyserr(FERROR, errno, "push_dir#2 %s failed", full_fname(dest_path)); @@ -562,8 +564,6 @@ 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, 0)) { rsyserr(FERROR, errno, "push_dir#3 %s failed", full_fname(dest_path)); @@ -579,45 +579,55 @@ static char *get_local_name(struct file_list *flist, char *dest_path) * mode. We'll fix dirs that can be relative to the non-existent dir. */ static void fix_basis_dirs(void) { - char **dir, *new; + 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"); - pathjoin(new, len, curr_dir, *dir); - clean_fname(new, 1); + 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) +static void read_final_goodbye(int f_in) { - int i; + int i, iflags, xlen; + uchar fnamecmp_type; + char xname[MAXPATHLEN]; if (protocol_version < 29) i = read_int(f_in); else { - while ((i = read_int(f_in)) == the_file_list->count - && read_shortint(f_in) == ITEM_IS_NEW) { - /* Forward the keep-alive (no-op) to the receiver. */ - write_int(f_out, the_file_list->count); - write_shortint(f_out, ITEM_IS_NEW); - } + i = read_ndx_and_attrs(f_in, &iflags, &fnamecmp_type, + xname, &xlen); } - 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); } } - static void do_server_sender(int f_in, int f_out, int argc, char *argv[]) { struct file_list *flist; @@ -642,8 +652,6 @@ static void do_server_sender(int f_in, int f_out, int argc, char *argv[]) } if (!relative_paths) { - if (sanitize_paths) - die_on_unsafe_path(dir, 0); if (!push_dir(dir, 0)) { rsyserr(FERROR, errno, "push_dir#3 %s failed", full_fname(dir)); @@ -660,25 +668,22 @@ static void do_server_sender(int f_in, int f_out, int argc, char *argv[]) } flist = send_file_list(f_out,argc,argv); - if (!flist || flist->count == 0) { + if (!flist || flist->count == 0) exit_cleanup(0); - } - the_file_list = flist; - io_start_buffering_in(); - io_start_buffering_out(); + io_start_buffering_in(f_in); - send_files(flist,f_out,f_in); + send_files(f_in, f_out); io_flush(FULL_FLUSH); handle_stats(f_out); if (protocol_version >= 24) - read_final_goodbye(f_in, f_out); + read_final_goodbye(f_in); io_flush(FULL_FLUSH); exit_cleanup(0); } -static int do_recv(int f_in,int f_out,struct file_list *flist,char *local_name) +static int do_recv(int f_in, int f_out, char *local_name) { int pid; int exit_code = 0; @@ -688,8 +693,10 @@ static int do_recv(int f_in,int f_out,struct file_list *flist,char *local_name) * points to an identical file won't be replaced by the referent. */ copy_links = copy_dirlinks = 0; +#ifdef SUPPORT_HARD_LINKS if (preserve_hard_links) - init_hard_links(); + match_hard_links(); +#endif if (fd_pair(error_pipe) < 0) { rsyserr(FERROR, errno, "pipe failed in do_recv"); @@ -709,26 +716,32 @@ static int do_recv(int f_in,int f_out,struct file_list *flist,char *local_name) close(f_out); /* we can't let two processes write to the socket at one time */ - close_multiplexing_out(); + io_end_multiplex_out(); /* set place to send errors */ set_msg_fd_out(error_pipe[1]); + io_start_buffering_out(error_pipe[1]); - recv_files(f_in, flist, local_name); + recv_files(f_in, local_name); io_flush(FULL_FLUSH); handle_stats(f_in); - send_msg(MSG_DONE, "", 0); + send_msg(MSG_DONE, "", 1); + write_longint(error_pipe[1], stats.total_read); io_flush(FULL_FLUSH); /* Handle any keep-alive packets from the post-processing work * that the generator does. */ if (protocol_version >= 29) { + int iflags, xlen; + uchar fnamecmp_type; + char xname[MAXPATHLEN]; + kluge_around_eof = -1; /* This should only get stopped via a USR2 signal. */ - while (read_int(f_in) == flist->count - && read_shortint(f_in) == ITEM_IS_NEW) {} + read_ndx_and_attrs(f_in, &iflags, &fnamecmp_type, + xname, &xlen); rprintf(FERROR, "Invalid packet at end of run [%s]\n", who_am_i()); @@ -743,7 +756,8 @@ static int do_recv(int f_in,int f_out,struct file_list *flist,char *local_name) } am_generator = 1; - close_multiplexing_in(); + + io_end_multiplex_in(); if (write_batch && !am_server) stop_write_batch(); @@ -751,17 +765,18 @@ static int do_recv(int f_in,int f_out,struct file_list *flist,char *local_name) if (f_in != f_out) close(f_in); - io_start_buffering_out(); + io_start_buffering_out(f_out); set_msg_fd_in(error_pipe[0]); + io_start_buffering_in(error_pipe[0]); - generate_files(f_out, flist, local_name); + generate_files(f_out, local_name); handle_stats(-1); io_flush(FULL_FLUSH); if (protocol_version >= 24) { /* send a final goodbye message */ - write_int(f_out, -1); + write_ndx(f_out, NDX_DONE); } io_flush(FULL_FLUSH); @@ -771,8 +786,7 @@ static int do_recv(int f_in,int f_out,struct file_list *flist,char *local_name) return exit_code; } - -static void do_server_recv(int f_in, int f_out, int argc,char *argv[]) +static void do_server_recv(int f_in, int f_out, int argc, char *argv[]) { int exit_code; struct file_list *flist; @@ -808,7 +822,10 @@ static void do_server_recv(int f_in, int f_out, int argc,char *argv[]) } } - io_start_buffering_in(); + if (protocol_version >= 30) + io_start_multiplex_in(); + else + io_start_buffering_in(f_in); recv_filter_list(f_in); if (filesfrom_fd >= 0) { @@ -822,12 +839,13 @@ static void do_server_recv(int f_in, int f_out, int argc,char *argv[]) } flist = recv_file_list(f_in); - verbose = save_verbose; if (!flist) { rprintf(FERROR,"server_recv: recv_file_list error\n"); exit_cleanup(RERR_FILESELECT); } - the_file_list = flist; + if (inc_recurse && file_total == 1) + recv_additional_file_list(f_in); + verbose = save_verbose; if (argc > 0) local_name = get_local_name(flist,argv[0]); @@ -838,17 +856,12 @@ 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); } } - if (dry_run > 1) - fix_basis_dirs(); + fix_basis_dirs(); if (server_filter_list.head) { char **dir; @@ -867,7 +880,7 @@ static void do_server_recv(int f_in, int f_out, int argc,char *argv[]) } } - exit_code = do_recv(f_in,f_out,flist,local_name); + exit_code = do_recv(f_in, f_out, local_name); exit_cleanup(exit_code); } @@ -897,12 +910,10 @@ void start_server(int f_in, int f_out, int argc, char *argv[]) keep_dirlinks = 0; /* Must be disabled on the sender. */ if (need_messages_from_generator) io_start_multiplex_in(); - recv_filter_list(f_in); do_server_sender(f_in, f_out, argc, argv); - } else { + } else do_server_recv(f_in, f_out, argc, argv); - } exit_cleanup(0); } @@ -929,9 +940,6 @@ int client_run(int f_in, int f_out, pid_t pid, int argc, char *argv[]) setup_iconv(); #endif - if (protocol_version >= 23 && !read_batch) - io_start_multiplex_in(); - /* We set our stderr file handle to blocking because ssh might have * set it to non-blocking. This can be particularly troublesome if * stderr is a clone of stdout, because ssh would have set our stdout @@ -944,7 +952,10 @@ int client_run(int f_in, int f_out, pid_t pid, int argc, char *argv[]) if (am_sender) { keep_dirlinks = 0; /* Must be disabled on the sender. */ - io_start_buffering_out(); + if (protocol_version >= 30) + io_start_multiplex_out(); + else + io_start_buffering_out(f_out); if (!filesfrom_host) set_msg_fd_in(f_in); send_filter_list(f_out); @@ -957,14 +968,16 @@ int client_run(int f_in, int f_out, pid_t pid, int argc, char *argv[]) set_msg_fd_in(-1); if (verbose > 3) rprintf(FINFO,"file list sent\n"); - the_file_list = flist; + + if (protocol_version >= 23) + io_start_multiplex_in(); io_flush(NORMAL_FLUSH); - send_files(flist,f_out,f_in); + send_files(f_in, f_out); io_flush(FULL_FLUSH); handle_stats(-1); if (protocol_version >= 24) - read_final_goodbye(f_in, f_out); + read_final_goodbye(f_in); if (pid != -1) { if (verbose > 3) rprintf(FINFO,"client_run waiting on %d\n", (int) pid); @@ -976,8 +989,12 @@ int client_run(int f_in, int f_out, pid_t pid, int argc, char *argv[]) exit_cleanup(exit_code); } - if (need_messages_from_generator && !read_batch) - io_start_multiplex_out(); + if (!read_batch) { + if (protocol_version >= 23) + io_start_multiplex_in(); + if (need_messages_from_generator) + io_start_multiplex_out(); + } if (argc == 0) list_only |= 1; @@ -992,15 +1009,15 @@ int client_run(int f_in, int f_out, pid_t pid, int argc, char *argv[]) if (write_batch && !am_server) start_write_batch(f_in); flist = recv_file_list(f_in); - the_file_list = flist; + if (inc_recurse && file_total == 1) + recv_additional_file_list(f_in); if (flist && flist->count > 0) { local_name = get_local_name(flist, argv[0]); - if (dry_run > 1) - fix_basis_dirs(); + fix_basis_dirs(); - exit_code2 = do_recv(f_in, f_out, flist, local_name); + exit_code2 = do_recv(f_in, f_out, local_name); } else { handle_stats(-1); output_summary(); @@ -1016,7 +1033,7 @@ int client_run(int f_in, int f_out, pid_t pid, int argc, char *argv[]) return MAX(exit_code, exit_code2); } -static int copy_argv (char *argv[]) +static int copy_argv(char *argv[]) { int i; @@ -1128,6 +1145,12 @@ static int start_client(int argc, char *argv[]) } } + if (password_file && !daemon_over_rsh) { + rprintf(FERROR, "The --password-file option may only be " + "used when accessing an rsync daemon.\n"); + exit_cleanup(RERR_SYNTAX); + } + if (shell_machine) { p = strrchr(shell_machine,'@'); if (p) { @@ -1258,8 +1281,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. */ @@ -1358,6 +1381,9 @@ int main(int argc,char *argv[]) } if (read_batch) read_stream_flags(batch_fd); + else + write_stream_flags(batch_fd); + } if (write_batch < 0) dry_run = 1;