X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/05278935a3556dc6848d2aed6ae4a450b4f771d7..82ad07c4182f744c07b96a15df4572e559ed7dc8:/main.c diff --git a/main.c b/main.c index 7de34f23..28334357 100644 --- a/main.c +++ b/main.c @@ -1,23 +1,25 @@ -/* -*- c-file-style: "linux" -*- - - Copyright (C) 1996-2001 by Andrew Tridgell - Copyright (C) Paul Mackerras 1996 - Copyright (C) 2001, 2002 by Martin Pool - - 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. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ +/* + * The startup routines, including main(), for rsync. + * + * 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 + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + */ #include "rsync.h" #if defined CONFIG_LOCALE && defined HAVE_LOCALE_H @@ -33,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; @@ -41,11 +43,16 @@ extern int do_stats; extern int log_got_error; extern int module_id; extern int copy_links; +extern int copy_dirlinks; extern int keep_dirlinks; extern int preserve_hard_links; extern int protocol_version; 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; extern int read_batch; @@ -56,11 +63,16 @@ 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; +int new_root_dir = 0; mode_t orig_umask = 0; struct file_list *the_file_list; @@ -167,7 +179,6 @@ static void handle_stats(int f) return; if (am_daemon) { - log_exit(0, __FILE__, __LINE__); if (f == -1 || !am_sender) return; } @@ -188,7 +199,7 @@ static void handle_stats(int f) /* this is the client */ if (f < 0 && !am_sender) /* e.g. when we got an empty file list. */ - ; + ; else if (!am_sender) { /* Read the first two in opposite order because the meaning of * read/write swaps when switching from sender to receiver. */ @@ -215,7 +226,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", @@ -226,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", @@ -242,8 +255,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", @@ -266,7 +280,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", @@ -402,8 +417,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) { @@ -437,19 +452,21 @@ static pid_t do_cmd(char *cmd, char *machine, char *user, char *path, /* The receiving side operates in one of two modes: * - * 1. it enters a directory and receives one or more files, placing them - * according to their names in the file-list. + * 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 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) { @@ -460,13 +477,11 @@ 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 = 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 (!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); @@ -479,35 +494,53 @@ static char *get_local_name(struct file_list *flist, char *dest_path) " 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)) { + 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 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)); 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 (!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); @@ -527,7 +560,7 @@ static char *get_local_name(struct file_list *flist, char *dest_path) dest_path = "/"; *cp = '\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); @@ -537,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) @@ -554,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); @@ -577,17 +642,20 @@ 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 (!push_dir(dir, 0)) { + rsyserr(FERROR, errno, "push_dir#3 %s failed", + full_fname(dir)); + exit_cleanup(RERR_FILESELECT); + } } argc--; argv++; @@ -625,7 +693,7 @@ static int do_recv(int f_in,int f_out,struct file_list *flist,char *local_name) /* The receiving side mustn't obey this, or an existing symlink that * points to an identical file won't be replaced by the referent. */ - copy_links = 0; + copy_links = copy_dirlinks = 0; if (preserve_hard_links) init_hard_links(); @@ -700,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); @@ -740,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); @@ -771,6 +839,36 @@ static void do_server_recv(int f_in, int f_out, int argc,char *argv[]) if (argc > 0) local_name = get_local_name(flist,argv[0]); + /* 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 **dir; + for (dir = basis_dir; *dir; dir++) { + *dir = sanitize_path(NULL, *dir, NULL, curr_dir_depth, NULL); + } + if (partial_dir) { + partial_dir = sanitize_path(NULL, partial_dir, NULL, curr_dir_depth, NULL); + } + } + fix_basis_dirs(); + + 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); exit_cleanup(exit_code); } @@ -901,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); @@ -1159,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. */ @@ -1237,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();