X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/f7a76b9c450c05abb799e1103d90b51f9f06c0c3..refs/heads/hacks/missing-args-b3.0.x:/options.c diff --git a/options.c b/options.c index e60778da..f5dbecea 100644 --- a/options.c +++ b/options.c @@ -3,7 +3,7 @@ * * Copyright (C) 1998-2001 Andrew Tridgell * Copyright (C) 2000, 2001, 2002 Martin Pool - * Copyright (C) 2002-2007 Wayne Davison + * Copyright (C) 2002-2009 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 @@ -25,10 +25,12 @@ #include "zlib/zlib.h" extern int module_id; +extern int local_server; extern int sanitize_paths; extern int daemon_over_rsh; +extern unsigned int module_dirlen; extern struct filter_list_struct filter_list; -extern struct filter_list_struct server_filter_list; +extern struct filter_list_struct daemon_filter_list; int make_backups = 0; @@ -80,6 +82,7 @@ int am_generator = 0; int am_starting_up = 1; int relative_paths = -1; int implied_dirs = 1; +int missing_args = 0; /* 0 = FERROR_XFER, 1 = ignore, 2 = delete */ int numeric_ids = 0; int allow_8bit_chars = 0; int force_delete = 0; @@ -99,6 +102,7 @@ int xfer_dirs = -1; int am_daemon = 0; int do_stats = 0; int do_progress = 0; +int connect_timeout = 0; int keep_partial = 0; int safe_symlinks = 0; int copy_unsafe_links = 0; @@ -200,7 +204,7 @@ static int itemize_changes = 0; static int refused_delete, refused_archive_part, refused_compress; static int refused_partial, refused_progress, refused_delete_before; static int refused_delete_during; -static int refused_inplace; +static int refused_inplace, refused_no_iconv; static char *max_size_arg, *min_size_arg; static char tmp_partialdir[] = ".~tmp~"; @@ -216,6 +220,7 @@ static void print_rsync_version(enum logcode f) char const *got_socketpair = "no "; char const *have_inplace = "no "; char const *hardlinks = "no "; + char const *symtimes = "no "; char const *acls = "no "; char const *xattrs = "no "; char const *links = "no "; @@ -224,7 +229,8 @@ static void print_rsync_version(enum logcode f) STRUCT_STAT *dumstat; #if SUBPROTOCOL_VERSION != 0 - asprintf(&subprotocol, ".PR%d", SUBPROTOCOL_VERSION); + if (asprintf(&subprotocol, ".PR%d", SUBPROTOCOL_VERSION) < 0) + out_of_memory("print_rsync_version"); #endif #ifdef HAVE_SOCKETPAIR got_socketpair = ""; @@ -250,10 +256,13 @@ static void print_rsync_version(enum logcode f) #ifdef ICONV_OPTION iconv = ""; #endif +#if defined HAVE_LUTIMES && defined HAVE_UTIMES + symtimes = ""; +#endif rprintf(f, "%s version %s protocol version %d%s\n", RSYNC_NAME, RSYNC_VERSION, PROTOCOL_VERSION, subprotocol); - rprintf(f, "Copyright (C) 1996-2007 by Andrew Tridgell, Wayne Davison, and others.\n"); + rprintf(f, "Copyright (C) 1996-2009 by Andrew Tridgell, Wayne Davison, and others.\n"); rprintf(f, "Web site: http://rsync.samba.org/\n"); rprintf(f, "Capabilities:\n"); rprintf(f, " %d-bit files, %d-bit inums, %d-bit timestamps, %d-bit long ints,\n", @@ -263,8 +272,8 @@ static void print_rsync_version(enum logcode f) (int)(sizeof (int64) * 8)); rprintf(f, " %ssocketpairs, %shardlinks, %ssymlinks, %sIPv6, batchfiles, %sinplace,\n", got_socketpair, hardlinks, links, ipv6, have_inplace); - rprintf(f, " %sappend, %sACLs, %sxattrs, %siconv\n", - have_inplace, acls, xattrs, iconv); + rprintf(f, " %sappend, %sACLs, %sxattrs, %siconv, %ssymtimes\n", + have_inplace, acls, xattrs, iconv, symtimes); #ifdef MAINTAINER_MODE rprintf(f, "Panic Action: \"%s\"\n", get_panic_action()); @@ -351,8 +360,8 @@ void usage(enum logcode F) rprintf(F," --fake-super store/recover privileged attrs using xattrs\n"); #endif rprintf(F," -S, --sparse handle sparse files efficiently\n"); - rprintf(F," -n, --dry-run show what would have been transferred\n"); - rprintf(F," -W, --whole-file copy files whole (without rsync algorithm)\n"); + rprintf(F," -n, --dry-run perform a trial run with no changes made\n"); + rprintf(F," -W, --whole-file copy files whole (without delta-xfer algorithm)\n"); rprintf(F," -x, --one-file-system don't cross filesystem boundaries\n"); rprintf(F," -B, --block-size=SIZE force a fixed checksum block-size\n"); rprintf(F," -e, --rsh=COMMAND specify the remote shell to use\n"); @@ -367,6 +376,8 @@ void usage(enum logcode F) rprintf(F," --delete-delay find deletions during, delete after\n"); rprintf(F," --delete-after receiver deletes after transfer, not during\n"); rprintf(F," --delete-excluded also delete excluded files from destination dirs\n"); + rprintf(F," --ignore-missing-args ignore missing source args without error\n"); + rprintf(F," --delete-missing-args delete missing source args from destination\n"); rprintf(F," --ignore-errors delete even if there are I/O errors\n"); rprintf(F," --force force deletion of directories even if not empty\n"); rprintf(F," --max-delete=NUM don't delete more than NUM files\n"); @@ -377,7 +388,8 @@ void usage(enum logcode F) rprintf(F," --delay-updates put all updated files into place at transfer's end\n"); rprintf(F," -m, --prune-empty-dirs prune empty directory chains from the file-list\n"); rprintf(F," --numeric-ids don't map uid/gid values by user/group name\n"); - rprintf(F," --timeout=TIME set I/O timeout in seconds\n"); + rprintf(F," --timeout=SECONDS set I/O timeout in seconds\n"); + rprintf(F," --contimeout=SECONDS set daemon connection timeout in seconds\n"); rprintf(F," -I, --ignore-times don't skip files that match in size and mod-time\n"); rprintf(F," --size-only skip files that match in size\n"); rprintf(F," --modify-window=NUM compare mod-times with reduced accuracy\n"); @@ -438,7 +450,7 @@ enum {OPT_VERSION = 1000, OPT_DAEMON, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM, OPT_FILTER, OPT_COMPARE_DEST, OPT_COPY_DEST, OPT_LINK_DEST, OPT_HELP, OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_MODIFY_WINDOW, OPT_MIN_SIZE, OPT_CHMOD, OPT_READ_BATCH, OPT_WRITE_BATCH, OPT_ONLY_WRITE_BATCH, OPT_MAX_SIZE, - OPT_NO_D, OPT_APPEND, + OPT_NO_D, OPT_APPEND, OPT_NO_ICONV, OPT_SERVER, OPT_REFUSED_BASE = 9000}; static struct poptOption long_options[] = { @@ -467,6 +479,8 @@ static struct poptOption long_options[] = { {"dirs", 'd', POPT_ARG_VAL, &xfer_dirs, 2, 0, 0 }, {"no-dirs", 0, POPT_ARG_VAL, &xfer_dirs, 0, 0, 0 }, {"no-d", 0, POPT_ARG_VAL, &xfer_dirs, 0, 0, 0 }, + {"old-dirs", 0, POPT_ARG_VAL, &xfer_dirs, 4, 0, 0 }, + {"old-d", 0, POPT_ARG_VAL, &xfer_dirs, 4, 0, 0 }, {"perms", 'p', POPT_ARG_VAL, &preserve_perms, 1, 0, 0 }, {"no-perms", 0, POPT_ARG_VAL, &preserve_perms, 0, 0, 0 }, {"no-p", 0, POPT_ARG_VAL, &preserve_perms, 0, 0, 0 }, @@ -521,14 +535,19 @@ static struct poptOption long_options[] = { {"ignore-times", 'I', POPT_ARG_NONE, &ignore_times, 0, 0, 0 }, {"size-only", 0, POPT_ARG_NONE, &size_only, 0, 0, 0 }, {"one-file-system", 'x', POPT_ARG_NONE, 0, 'x', 0, 0 }, + {"no-one-file-system",'x',POPT_ARG_VAL, &one_file_system, 0, 0, 0 }, + {"no-x", 'x', POPT_ARG_VAL, &one_file_system, 0, 0, 0 }, {"update", 'u', POPT_ARG_NONE, &update_only, 0, 0, 0 }, {"existing", 0, POPT_ARG_NONE, &ignore_non_existing, 0, 0, 0 }, {"ignore-non-existing",0,POPT_ARG_NONE, &ignore_non_existing, 0, 0, 0 }, {"ignore-existing", 0, POPT_ARG_NONE, &ignore_existing, 0, 0, 0 }, {"max-size", 0, POPT_ARG_STRING, &max_size_arg, OPT_MAX_SIZE, 0, 0 }, {"min-size", 0, POPT_ARG_STRING, &min_size_arg, OPT_MIN_SIZE, 0, 0 }, - {"sparse", 'S', POPT_ARG_NONE, &sparse_files, 0, 0, 0 }, - {"inplace", 0, POPT_ARG_NONE, &inplace, 0, 0, 0 }, + {"sparse", 'S', POPT_ARG_VAL, &sparse_files, 1, 0, 0 }, + {"no-sparse", 0, POPT_ARG_VAL, &sparse_files, 0, 0, 0 }, + {"no-S", 0, POPT_ARG_VAL, &sparse_files, 0, 0, 0 }, + {"inplace", 0, POPT_ARG_VAL, &inplace, 1, 0, 0 }, + {"no-inplace", 0, POPT_ARG_VAL, &inplace, 0, 0, 0 }, {"append", 0, POPT_ARG_NONE, 0, OPT_APPEND, 0, 0 }, {"append-verify", 0, POPT_ARG_VAL, &append_mode, 2, 0, 0 }, {"no-append", 0, POPT_ARG_VAL, &append_mode, 0, 0, 0 }, @@ -539,10 +558,14 @@ static struct poptOption long_options[] = { {"delete-delay", 0, POPT_ARG_VAL, &delete_during, 2, 0, 0 }, {"delete-after", 0, POPT_ARG_NONE, &delete_after, 0, 0, 0 }, {"delete-excluded", 0, POPT_ARG_NONE, &delete_excluded, 0, 0, 0 }, + {"delete-missing-args",0,POPT_BIT_SET, &missing_args, 2, 0, 0 }, + {"ignore-missing-args",0,POPT_BIT_SET, &missing_args, 1, 0, 0 }, {"remove-sent-files",0, POPT_ARG_VAL, &remove_source_files, 2, 0, 0 }, /* deprecated */ {"remove-source-files",0,POPT_ARG_VAL, &remove_source_files, 1, 0, 0 }, - {"force", 0, POPT_ARG_NONE, &force_delete, 0, 0, 0 }, - {"ignore-errors", 0, POPT_ARG_NONE, &ignore_errors, 0, 0, 0 }, + {"force", 0, POPT_ARG_VAL, &force_delete, 1, 0, 0 }, + {"no-force", 0, POPT_ARG_VAL, &force_delete, 0, 0, 0 }, + {"ignore-errors", 0, POPT_ARG_VAL, &ignore_errors, 1, 0, 0 }, + {"no-ignore-errors", 0, POPT_ARG_VAL, &ignore_errors, 0, 0, 0 }, {"max-delete", 0, POPT_ARG_INT, &max_delete, 0, 0, 0 }, {0, 'F', POPT_ARG_NONE, 0, 'F', 0, 0 }, {"filter", 'f', POPT_ARG_STRING, 0, OPT_FILTER, 0, 0 }, @@ -561,11 +584,13 @@ static struct poptOption long_options[] = { {"compare-dest", 0, POPT_ARG_STRING, 0, OPT_COMPARE_DEST, 0, 0 }, {"copy-dest", 0, POPT_ARG_STRING, 0, OPT_COPY_DEST, 0, 0 }, {"link-dest", 0, POPT_ARG_STRING, 0, OPT_LINK_DEST, 0, 0 }, - {"fuzzy", 'y', POPT_ARG_NONE, &fuzzy_basis, 0, 0, 0 }, + {"fuzzy", 'y', POPT_ARG_VAL, &fuzzy_basis, 1, 0, 0 }, + {"no-fuzzy", 0, POPT_ARG_VAL, &fuzzy_basis, 0, 0, 0 }, + {"no-y", 0, POPT_ARG_VAL, &fuzzy_basis, 0, 0, 0 }, {"compress", 'z', POPT_ARG_NONE, 0, 'z', 0, 0 }, {"no-compress", 0, POPT_ARG_VAL, &do_compression, 0, 0, 0 }, - {"skip-compress", 0, POPT_ARG_STRING, &skip_compress, 0, 0, 0 }, {"no-z", 0, POPT_ARG_VAL, &do_compression, 0, 0, 0 }, + {"skip-compress", 0, POPT_ARG_STRING, &skip_compress, 0, 0, 0 }, {"compress-level", 0, POPT_ARG_INT, &def_compress_level, 'z', 0, 0 }, {0, 'P', POPT_ARG_NONE, 0, 'P', 0, 0 }, {"progress", 0, POPT_ARG_VAL, &do_progress, 1, 0, 0 }, @@ -575,7 +600,9 @@ static struct poptOption long_options[] = { {"partial-dir", 0, POPT_ARG_STRING, &partial_dir, 0, 0, 0 }, {"delay-updates", 0, POPT_ARG_VAL, &delay_updates, 1, 0, 0 }, {"no-delay-updates", 0, POPT_ARG_VAL, &delay_updates, 0, 0, 0 }, - {"prune-empty-dirs",'m', POPT_ARG_NONE, &prune_empty_dirs, 0, 0, 0 }, + {"prune-empty-dirs",'m', POPT_ARG_VAL, &prune_empty_dirs, 1, 0, 0 }, + {"no-prune-empty-dirs",0,POPT_ARG_VAL, &prune_empty_dirs, 0, 0, 0 }, + {"no-m", 0, POPT_ARG_VAL, &prune_empty_dirs, 0, 0, 0 }, {"log-file", 0, POPT_ARG_STRING, &logfile_name, 0, 0, 0 }, {"log-file-format", 0, POPT_ARG_STRING, &logfile_format, 0, 0, 0 }, {"out-format", 0, POPT_ARG_STRING, &stdout_format, 0, 0, 0 }, @@ -603,15 +630,20 @@ static struct poptOption long_options[] = { {"no-numeric-ids", 0, POPT_ARG_VAL, &numeric_ids, 0, 0, 0 }, {"timeout", 0, POPT_ARG_INT, &io_timeout, 0, 0, 0 }, {"no-timeout", 0, POPT_ARG_VAL, &io_timeout, 0, 0, 0 }, + {"contimeout", 0, POPT_ARG_INT, &connect_timeout, 0, 0, 0 }, + {"no-contimeout", 0, POPT_ARG_VAL, &connect_timeout, 0, 0, 0 }, {"rsh", 'e', POPT_ARG_STRING, &shell_cmd, 0, 0, 0 }, {"rsync-path", 0, POPT_ARG_STRING, &rsync_path, 0, 0, 0 }, {"temp-dir", 'T', POPT_ARG_STRING, &tmpdir, 0, 0, 0 }, #ifdef ICONV_OPTION {"iconv", 0, POPT_ARG_STRING, &iconv_opt, 0, 0, 0 }, + {"no-iconv", 0, POPT_ARG_NONE, 0, OPT_NO_ICONV, 0, 0 }, #endif {"ipv4", '4', POPT_ARG_VAL, &default_af_hint, AF_INET, 0, 0 }, {"ipv6", '6', POPT_ARG_VAL, &default_af_hint, AF_INET6, 0, 0 }, - {"8-bit-output", '8', POPT_ARG_NONE, &allow_8bit_chars, 0, 0, 0 }, + {"8-bit-output", '8', POPT_ARG_VAL, &allow_8bit_chars, 1, 0, 0 }, + {"no-8-bit-output", 0, POPT_ARG_VAL, &allow_8bit_chars, 0, 0, 0 }, + {"no-8", 0, POPT_ARG_VAL, &allow_8bit_chars, 0, 0, 0 }, {"qsort", 0, POPT_ARG_NONE, &use_qsort, 0, 0, 0 }, {"address", 0, POPT_ARG_STRING, &bind_address, 0, 0, 0 }, {"port", 0, POPT_ARG_INT, &rsync_port, 0, 0, 0 }, @@ -754,6 +786,8 @@ static void set_refuse_options(char *bp) refused_progress = op->val; else if (wildmatch("inplace", op->longName)) refused_inplace = op->val; + else if (wildmatch("no-iconv", op->longName)) + refused_no_iconv = op->val; break; } if (!is_wild) @@ -864,7 +898,7 @@ static void create_refuse_error(int which) * * @retval 0 on error, with err_buf containing an explanation **/ -int parse_arguments(int *argc_p, const char ***argv_p, int frommain) +int parse_arguments(int *argc_p, const char ***argv_p) { static poptContext pc; char *ref = lp_refuse_options(module_id); @@ -874,8 +908,13 @@ int parse_arguments(int *argc_p, const char ***argv_p, int frommain) if (ref && *ref) set_refuse_options(ref); - if (am_daemon) + if (am_daemon) { set_refuse_options("log-file*"); +#ifdef ICONV_OPTION + if (!*lp_charset(module_id)) + set_refuse_options("iconv"); +#endif + } #ifdef ICONV_OPTION if (!am_daemon && !protect_args && (arg = getenv("RSYNC_ICONV")) != NULL && *arg) @@ -1001,16 +1040,17 @@ int parse_arguments(int *argc_p, const char ***argv_p, int frommain) case OPT_INCLUDE_FROM: arg = poptGetOptArg(pc); if (sanitize_paths) - arg = sanitize_path(NULL, arg, NULL, 0, NULL); - if (server_filter_list.head) { + arg = sanitize_path(NULL, arg, NULL, 0, SP_DEFAULT); + if (daemon_filter_list.head) { int rej; - char *cp = strdup(arg); + char *dir, *cp = strdup(arg); if (!cp) out_of_memory("parse_arguments"); if (!*cp) goto options_rejected; - clean_fname(cp, CFN_COLLAPSE_DOT_DOT_DIRS); - rej = check_filter(&server_filter_list, cp, 0) < 0; + dir = cp + (*cp == '/' ? module_dirlen : 0); + clean_fname(dir, CFN_COLLAPSE_DOT_DOT_DIRS); + rej = check_filter(&daemon_filter_list, FLOG, dir, 0) < 0; free(cp); if (rej) goto options_rejected; @@ -1063,8 +1103,7 @@ int parse_arguments(int *argc_p, const char ***argv_p, int frommain) break; case 'q': - if (frommain) - quiet++; + quiet++; break; case 'x': @@ -1122,6 +1161,12 @@ int parse_arguments(int *argc_p, const char ***argv_p, int frommain) read_batch = 1; break; + case OPT_NO_ICONV: +#ifdef ICONV_OPTION + iconv_opt = NULL; +#endif + break; + case OPT_MAX_SIZE: if ((max_size = parse_size_arg(&max_size_arg, 'b')) <= 0) { snprintf(err_buf, sizeof err_buf, @@ -1236,7 +1281,7 @@ int parse_arguments(int *argc_p, const char ***argv_p, int frommain) } } - if (human_readable && argc == 2) { + if (human_readable && argc == 2 && !am_server) { /* Allow the old meaning of 'h' (--help) on its own. */ usage(FINFO); exit_cleanup(0); @@ -1249,14 +1294,17 @@ int parse_arguments(int *argc_p, const char ***argv_p, int frommain) else need_unsorted_flist = 1; } + if (refused_no_iconv && !iconv_opt) { + create_refuse_error(refused_no_iconv); + return 0; + } #endif - if (protect_args == 1) { - if (!frommain) - protect_args = 0; - else if (am_server) - return 1; - } + if (protect_args == 1 && am_server) + return 1; + + *argv_p = argv = poptGetArgs(pc); + *argc_p = argc = count_args(argv); #ifndef SUPPORT_LINKS if (preserve_links && !am_sender) { @@ -1276,7 +1324,13 @@ int parse_arguments(int *argc_p, const char ***argv_p, int frommain) } #endif -#ifndef SUPPORT_XATTRS +#ifdef SUPPORT_XATTRS + if (am_root < 0 && preserve_xattrs > 1) { + snprintf(err_buf, sizeof err_buf, + "--fake-super conflicts with -XX\n"); + return 0; + } +#else if (am_root < 0) { snprintf(err_buf, sizeof err_buf, "--fake-super requires an rsync with extended attributes enabled\n"); @@ -1301,12 +1355,19 @@ int parse_arguments(int *argc_p, const char ***argv_p, int frommain) batch_name = NULL; } else if (dry_run) write_batch = 0; - } + } else if (write_batch < 0 && dry_run) + write_batch = 0; if (read_batch && files_from) { snprintf(err_buf, sizeof err_buf, "--read-batch cannot be used with --files-from\n"); return 0; } + if (read_batch && remove_source_files) { + snprintf(err_buf, sizeof err_buf, + "--read-batch cannot be used with --remove-%s-files\n", + remove_source_files == 1 ? "source" : "sent"); + return 0; + } if (batch_name && strlen(batch_name) > MAX_BATCH_NAME_LEN) { snprintf(err_buf, sizeof err_buf, "the batch-file name must be %d characters or less.\n", @@ -1338,8 +1399,16 @@ int parse_arguments(int *argc_p, const char ***argv_p, int frommain) xfer_dirs = 1; } - if (xfer_dirs < 1) - xfer_dirs = recurse || list_only; + if (argc < 2 && !read_batch && !am_server) + list_only |= 1; + + if (xfer_dirs >= 4) { + parse_rule(&filter_list, "- /*/*", 0, 0); + recurse = xfer_dirs = 1; + } else if (recurse) + xfer_dirs = 1; + else if (xfer_dirs < 0) + xfer_dirs = list_only ? 1 : 0; if (relative_paths < 0) relative_paths = files_from? 1 : 0; @@ -1372,7 +1441,9 @@ int parse_arguments(int *argc_p, const char ***argv_p, int frommain) return 0; } - if (delete_mode && refused_delete) { + if (missing_args == 3) /* simplify if both options were specified */ + missing_args = 2; + if (refused_delete && (delete_mode || missing_args == 2)) { create_refuse_error(refused_delete); return 0; } @@ -1388,32 +1459,33 @@ int parse_arguments(int *argc_p, const char ***argv_p, int frommain) need_messages_from_generator = 1; } - *argv_p = argv = poptGetArgs(pc); - *argc_p = argc = count_args(argv); - if (sanitize_paths) { int i; for (i = argc; i-- > 0; ) - argv[i] = sanitize_path(NULL, argv[i], "", 0, NULL); + argv[i] = sanitize_path(NULL, argv[i], "", 0, SP_KEEP_DOT_DIRS); if (tmpdir) - tmpdir = sanitize_path(NULL, tmpdir, NULL, 0, NULL); + tmpdir = sanitize_path(NULL, tmpdir, NULL, 0, SP_DEFAULT); if (backup_dir) - backup_dir = sanitize_path(NULL, backup_dir, NULL, 0, NULL); + backup_dir = sanitize_path(NULL, backup_dir, NULL, 0, SP_DEFAULT); } - if (server_filter_list.head && !am_sender) { - struct filter_list_struct *elp = &server_filter_list; + if (daemon_filter_list.head && !am_sender) { + struct filter_list_struct *elp = &daemon_filter_list; if (tmpdir) { + char *dir; if (!*tmpdir) goto options_rejected; - clean_fname(tmpdir, CFN_COLLAPSE_DOT_DOT_DIRS); - if (check_filter(elp, tmpdir, 1) < 0) + dir = tmpdir + (*tmpdir == '/' ? module_dirlen : 0); + clean_fname(dir, CFN_COLLAPSE_DOT_DOT_DIRS); + if (check_filter(elp, FLOG, dir, 1) < 0) goto options_rejected; } if (backup_dir) { + char *dir; if (!*backup_dir) goto options_rejected; - clean_fname(backup_dir, CFN_COLLAPSE_DOT_DOT_DIRS); - if (check_filter(elp, backup_dir, 1) < 0) + dir = backup_dir + (*backup_dir == '/' ? module_dirlen : 0); + clean_fname(dir, CFN_COLLAPSE_DOT_DOT_DIRS); + if (check_filter(elp, FLOG, dir, 1) < 0) goto options_rejected; } } @@ -1602,12 +1674,14 @@ int parse_arguments(int *argc_p, const char ***argv_p, int frommain) } } else { if (sanitize_paths) - files_from = sanitize_path(NULL, files_from, NULL, 0, NULL); - if (server_filter_list.head) { + files_from = sanitize_path(NULL, files_from, NULL, 0, SP_DEFAULT); + if (daemon_filter_list.head) { + char *dir; if (!*files_from) goto options_rejected; - clean_fname(files_from, CFN_COLLAPSE_DOT_DOT_DIRS); - if (check_filter(&server_filter_list, files_from, 0) < 0) + dir = files_from + (*files_from == '/' ? module_dirlen : 0); + clean_fname(dir, CFN_COLLAPSE_DOT_DOT_DIRS); + if (check_filter(&daemon_filter_list, FLOG, dir, 0) < 0) goto options_rejected; } filesfrom_fd = open(files_from, O_RDONLY|O_BINARY); @@ -1677,8 +1751,8 @@ void server_options(char **args, int *argc_p) argstr[x++] = 'n'; if (preserve_links) argstr[x++] = 'l'; - if ((list_only && !recurse) || xfer_dirs > 1 - || (xfer_dirs && !recurse && delete_mode && am_sender)) + if ((xfer_dirs >= 2 && xfer_dirs < 4) + || (xfer_dirs && !recurse && (list_only || (delete_mode && am_sender)))) argstr[x++] = 'd'; if (am_sender) { if (keep_dirlinks) @@ -1748,24 +1822,39 @@ void server_options(char **args, int *argc_p) if (do_compression) argstr[x++] = 'z'; - /* We make use of the -e option to let the server know about any - * pre-release protocol version && our allow_inc_recurse status. */ set_allow_inc_recurse(); + + /* Checking the pre-negotiated value allows --protocol=29 override. */ + if (protocol_version >= 30) { + /* We make use of the -e option to let the server know about + * any pre-release protocol version && some behavior flags. */ + argstr[x++] = 'e'; #if SUBPROTOCOL_VERSION != 0 - if (protocol_version == PROTOCOL_VERSION) { - x += snprintf(argstr+x, sizeof argstr - x, - "e%d.%d%s", PROTOCOL_VERSION, SUBPROTOCOL_VERSION, - allow_inc_recurse ? "i" : ""); - } else + if (protocol_version == PROTOCOL_VERSION) { + x += snprintf(argstr+x, sizeof argstr - x, + "%d.%d", + PROTOCOL_VERSION, SUBPROTOCOL_VERSION); + } else +#endif + argstr[x++] = '.'; + if (allow_inc_recurse) + argstr[x++] = 'i'; +#if defined HAVE_LUTIMES && defined HAVE_UTIMES + argstr[x++] = 'L'; +#endif +#ifdef ICONV_OPTION + argstr[x++] = 's'; #endif - if (allow_inc_recurse) { - argstr[x++] = 'e'; - argstr[x++] = 'i'; + } + + if (x >= (int)sizeof argstr) { /* Not possible... */ + rprintf(FERROR, "argstr overflow in server_options().\n"); + exit_cleanup(RERR_MALLOC); } argstr[x] = '\0'; - if (x != 1) + if (x > 1) args[ac++] = argstr; #ifdef ICONV_OPTION @@ -1781,7 +1870,7 @@ void server_options(char **args, int *argc_p) } #endif - if (protect_args) /* initial args break here */ + if (protect_args && !local_server) /* unprotected args stop here */ args[ac++] = NULL; if (list_only > 1) @@ -1893,6 +1982,13 @@ void server_options(char **args, int *argc_p) } } + /* --delete-missing-args needs the cooperation of both sides, but + * the sender can handle --ignore-missing-args by itself. */ + if (missing_args == 2) + args[ac++] = "--delete-missing-args"; + else if (missing_args == 1 && !am_sender) + args[ac++] = "--ignore-missing-args"; + if (modify_window_set) { if (asprintf(&arg, "--modify-window=%d", modify_window) < 0) goto oom; @@ -1948,7 +2044,6 @@ void server_options(char **args, int *argc_p) * and it may be an older version that doesn't know this * option, so don't send it if client is the sender. */ - int i; for (i = 0; i < basis_dir_cnt; i++) { args[ac++] = dest_option; args[ac++] = basis_dir[i]; @@ -1988,6 +2083,11 @@ void server_options(char **args, int *argc_p) else if (remove_source_files) args[ac++] = "--remove-sent-files"; + if (ac > MAX_SERVER_ARGS) { /* Not possible... */ + rprintf(FERROR, "argc overflow in server_options().\n"); + exit_cleanup(RERR_MALLOC); + } + *argc_p = ac; return; @@ -1995,6 +2095,62 @@ void server_options(char **args, int *argc_p) out_of_memory("server_options"); } +/* If str points to a valid hostspec, return allocated memory containing the + * [USER@]HOST part of the string, and set the path_start_ptr to the part of + * the string after the host part. Otherwise, return NULL. If port_ptr is + * non-NULL, we must be parsing an rsync:// URL hostname, and we will set + * *port_ptr if a port number is found. Note that IPv6 IPs will have their + * (required for parsing) [ and ] chars elided from the returned string. */ +static char *parse_hostspec(char *str, char **path_start_ptr, int *port_ptr) +{ + char *s, *host_start = str; + int hostlen = 0, userlen = 0; + char *ret; + + for (s = str; ; s++) { + if (!*s) { + /* It is only OK if we run out of string with rsync:// */ + if (!port_ptr) + return NULL; + if (!hostlen) + hostlen = s - host_start; + break; + } + if (*s == ':' || *s == '/') { + if (!hostlen) + hostlen = s - host_start; + if (*s++ == '/') { + if (!port_ptr) + return NULL; + } else if (port_ptr) { + *port_ptr = atoi(s); + while (isDigit(s)) s++; + if (*s && *s++ != '/') + return NULL; + } + break; + } + if (*s == '@') { + userlen = s - str + 1; + host_start = s + 1; + } else if (*s == '[') { + if (s != host_start++) + return NULL; + while (*s && *s != ']' && *s != '/') s++; /*SHARED ITERATOR*/ + hostlen = s - host_start; + if (*s != ']' || (s[1] && s[1] != '/' && s[1] != ':') || !hostlen) + return NULL; + } + } + + *path_start_ptr = s; + ret = new_array(char, userlen + hostlen + 1); + if (userlen) + strlcpy(ret, str, userlen + 1); + strlcpy(ret + userlen, host_start, hostlen + 1); + return ret; +} + /* Look for a HOST specfication of the form "HOST:PATH", "HOST::PATH", or * "rsync://HOST:PORT/PATH". If found, *host_ptr will be set to some allocated * memory with the HOST. If a daemon-accessing spec was specified, the value @@ -2004,68 +2160,28 @@ void server_options(char **args, int *argc_p) * "[::ffff:127.0.0.1]") which is returned without the '[' and ']'. */ char *check_for_hostspec(char *s, char **host_ptr, int *port_ptr) { - char *p; - int not_host; - int hostlen; + char *path; if (port_ptr && strncasecmp(URL_PREFIX, s, strlen(URL_PREFIX)) == 0) { - char *path; - s += strlen(URL_PREFIX); - if ((p = strchr(s, '/')) != NULL) { - hostlen = p - s; - path = p + 1; - } else { - hostlen = strlen(s); - path = ""; + *host_ptr = parse_hostspec(s + strlen(URL_PREFIX), &path, port_ptr); + if (*host_ptr) { + if (!*port_ptr) + *port_ptr = RSYNC_PORT; + return path; } - if (*s == '[' && (p = strchr(s, ']')) != NULL) { - s++; - hostlen = p - s; - if (p[1] == ':') - *port_ptr = atoi(p+2); - } else { - if ((p = strchr(s, ':')) != NULL && p < s + hostlen) { - hostlen = p - s; - *port_ptr = atoi(p+1); - } - } - if (!*port_ptr) - *port_ptr = RSYNC_PORT; - *host_ptr = new_array(char, hostlen + 1); - strlcpy(*host_ptr, s, hostlen + 1); - return path; - } - - if (*s == '[' && (p = strchr(s, ']')) != NULL && p[1] == ':') { - s++; - hostlen = p - s; - *p = '\0'; - not_host = strchr(s, '/') || !strchr(s, ':'); - *p = ']'; - if (not_host) - return NULL; - p++; - } else { - if (!(p = strchr(s, ':'))) - return NULL; - hostlen = p - s; - *p = '\0'; - not_host = strchr(s, '/') != NULL; - *p = ':'; - if (not_host) - return NULL; } - *host_ptr = new_array(char, hostlen + 1); - strlcpy(*host_ptr, s, hostlen + 1); + *host_ptr = parse_hostspec(s, &path, NULL); + if (!*host_ptr) + return NULL; - if (p[1] == ':') { + if (*path == ':') { if (port_ptr && !*port_ptr) *port_ptr = RSYNC_PORT; - return p + 2; + return path + 1; } if (port_ptr) *port_ptr = 0; - return p + 1; + return path; }