X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/4db88e5b8f73731d52f853d78fe7dffbb14a2ec4..3610c4583a6b187fa6fc98ad0025340c6f840d53:/options.c diff --git a/options.c b/options.c index 5aae00d9..97e1daed 100644 --- a/options.c +++ b/options.c @@ -23,8 +23,8 @@ extern int sanitize_paths; extern int select_timeout; -extern char curr_dir[MAXPATHLEN]; extern struct exclude_list_struct exclude_list; +extern struct exclude_list_struct server_exclude_list; int make_backups = 0; @@ -118,6 +118,7 @@ unsigned int backup_dir_remainder; char *backup_suffix = NULL; char *tmpdir = NULL; +char *partial_dir = NULL; char *compare_dest = NULL; char *config_file = NULL; char *shell_cmd = NULL; @@ -197,8 +198,9 @@ static void print_rsync_version(enum logcode f) get_panic_action()); #endif -#ifdef NO_INT64 - rprintf(f, "WARNING: no 64-bit integers on this platform!\n"); +#ifdef INT64_IS_OFF_T + if (sizeof (int64) < 8) + rprintf(f, "WARNING: no 64-bit integers on this platform!\n"); #endif rprintf(f, @@ -230,7 +232,7 @@ void usage(enum logcode F) rprintf(F," -v, --verbose increase verbosity\n"); rprintf(F," -q, --quiet decrease verbosity\n"); rprintf(F," -c, --checksum always checksum\n"); - rprintf(F," -a, --archive archive mode, equivalent to -rlptgoD\n"); + rprintf(F," -a, --archive archive mode, equivalent to -rlptgoD (no -H)\n"); rprintf(F," -r, --recursive recurse into directories\n"); rprintf(F," -R, --relative use relative path names\n"); rprintf(F," --no-relative turn off --relative\n"); @@ -256,7 +258,7 @@ void usage(enum logcode F) rprintf(F," -W, --whole-file copy whole files, no incremental checks\n"); rprintf(F," --no-whole-file turn off --whole-file\n"); rprintf(F," -x, --one-file-system don't cross filesystem boundaries\n"); - rprintf(F," -B, --block-size=SIZE checksum blocking size (default %d)\n",BLOCK_SIZE); + rprintf(F," -B, --block-size=SIZE force a fixed checksum block-size\n"); rprintf(F," -e, --rsh=COMMAND specify the remote shell\n"); rprintf(F," --rsync-path=PATH specify path to rsync on the remote machine\n"); rprintf(F," --existing only update files that already exist\n"); @@ -267,6 +269,7 @@ void usage(enum logcode F) rprintf(F," --ignore-errors delete even if there are I/O errors\n"); rprintf(F," --max-delete=NUM don't delete more than NUM files\n"); rprintf(F," --partial keep partially transferred files\n"); + rprintf(F," --partial-dir=DIR put a partially transferred file into DIR\n"); rprintf(F," --force force deletion of directories even if not empty\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"); @@ -286,11 +289,6 @@ void usage(enum logcode F) rprintf(F," --files-from=FILE read FILE for list of source-file names\n"); rprintf(F," -0, --from0 all *-from file lists are delimited by nulls\n"); rprintf(F," --version print version number\n"); - rprintf(F," --daemon run as an rsync daemon\n"); - rprintf(F," --no-detach do not detach from the parent\n"); - rprintf(F," --address=ADDRESS bind to the specified address\n"); - rprintf(F," --config=FILE specify alternate rsyncd.conf file\n"); - rprintf(F," --port=PORT specify alternate rsyncd port number\n"); rprintf(F," --blocking-io use blocking I/O for the remote shell\n"); rprintf(F," --no-blocking-io turn off --blocking-io\n"); rprintf(F," --stats give some file transfer stats\n"); @@ -300,20 +298,18 @@ void usage(enum logcode F) rprintf(F," --bwlimit=KBPS limit I/O bandwidth, KBytes per second\n"); rprintf(F," --write-batch=FILE write a batch to FILE\n"); rprintf(F," --read-batch=FILE read a batch from FILE\n"); - rprintf(F," --checksum-seed=NUM set block/file checksum seed\n"); #ifdef INET6 rprintf(F," -4, --ipv4 prefer IPv4\n"); rprintf(F," -6, --ipv6 prefer IPv6\n"); #endif rprintf(F," -h, --help show this help screen\n"); - rprintf(F,"\n"); - - rprintf(F,"\nPlease see the rsync(1) and rsyncd.conf(5) man pages for full documentation\n"); + rprintf(F,"\nUse \"rsync --daemon --help\" to see the daemon-mode command-line options.\n"); + rprintf(F,"Please see the rsync(1) and rsyncd.conf(5) man pages for full documentation.\n"); rprintf(F,"See http://rsync.samba.org/ for updates, bug reports, and answers\n"); } -enum {OPT_VERSION = 1000, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM, +enum {OPT_VERSION = 1000, OPT_DAEMON, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM, OPT_DELETE_AFTER, OPT_DELETE_EXCLUDED, OPT_LINK_DEST, OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_MODIFY_WINDOW, OPT_READ_BATCH, OPT_WRITE_BATCH, OPT_TIMEOUT, @@ -321,7 +317,7 @@ enum {OPT_VERSION = 1000, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM, static struct poptOption long_options[] = { /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - {"version", 0, POPT_ARG_NONE, 0, OPT_VERSION, 0, 0}, + {"version", 0, POPT_ARG_NONE, 0, OPT_VERSION, 0, 0}, {"suffix", 0, POPT_ARG_STRING, &backup_suffix, 0, 0, 0 }, {"rsync-path", 0, POPT_ARG_STRING, &rsync_path, 0, 0, 0 }, {"password-file", 0, POPT_ARG_STRING, &password_file, 0, 0, 0 }, @@ -332,16 +328,16 @@ static struct poptOption long_options[] = { {"delete", 0, POPT_ARG_NONE, &delete_mode, 0, 0, 0 }, {"existing", 0, POPT_ARG_NONE, &only_existing, 0, 0, 0 }, {"ignore-existing", 0, POPT_ARG_NONE, &opt_ignore_existing, 0, 0, 0 }, - {"delete-after", 0, POPT_ARG_NONE, 0, OPT_DELETE_AFTER, 0, 0 }, - {"delete-excluded", 0, POPT_ARG_NONE, 0, OPT_DELETE_EXCLUDED, 0, 0 }, + {"delete-after", 0, POPT_ARG_NONE, 0, OPT_DELETE_AFTER, 0, 0 }, + {"delete-excluded", 0, POPT_ARG_NONE, 0, OPT_DELETE_EXCLUDED, 0, 0 }, {"force", 0, POPT_ARG_NONE, &force_delete, 0, 0, 0 }, {"numeric-ids", 0, POPT_ARG_NONE, &numeric_ids, 0, 0, 0 }, - {"exclude", 0, POPT_ARG_STRING, 0, OPT_EXCLUDE, 0, 0 }, - {"include", 0, POPT_ARG_STRING, 0, OPT_INCLUDE, 0, 0 }, - {"exclude-from", 0, POPT_ARG_STRING, 0, OPT_EXCLUDE_FROM, 0, 0 }, - {"include-from", 0, POPT_ARG_STRING, 0, OPT_INCLUDE_FROM, 0, 0 }, + {"exclude", 0, POPT_ARG_STRING, 0, OPT_EXCLUDE, 0, 0 }, + {"include", 0, POPT_ARG_STRING, 0, OPT_INCLUDE, 0, 0 }, + {"exclude-from", 0, POPT_ARG_STRING, 0, OPT_EXCLUDE_FROM, 0, 0 }, + {"include-from", 0, POPT_ARG_STRING, 0, OPT_INCLUDE_FROM, 0, 0 }, {"safe-links", 0, POPT_ARG_NONE, &safe_symlinks, 0, 0, 0 }, - {"help", 'h', POPT_ARG_NONE, 0, 'h', 0, 0 }, + {"help", 'h', POPT_ARG_NONE, 0, 'h', 0, 0 }, {"backup", 'b', POPT_ARG_NONE, &make_backups, 0, 0, 0 }, {"dry-run", 'n', POPT_ARG_NONE, &dry_run, 0, 0, 0 }, {"sparse", 'S', POPT_ARG_NONE, &sparse_files, 0, 0, 0 }, @@ -360,11 +356,11 @@ static struct poptOption long_options[] = { {"devices", 'D', POPT_ARG_NONE, &preserve_devices, 0, 0, 0 }, {"times", 't', POPT_ARG_NONE, &preserve_times, 0, 0, 0 }, {"checksum", 'c', POPT_ARG_NONE, &always_checksum, 0, 0, 0 }, - {"verbose", 'v', POPT_ARG_NONE, 0, 'v', 0, 0 }, - {"quiet", 'q', POPT_ARG_NONE, 0, 'q', 0, 0 }, + {"verbose", 'v', POPT_ARG_NONE, 0, 'v', 0, 0 }, + {"quiet", 'q', POPT_ARG_NONE, 0, 'q', 0, 0 }, {"archive", 'a', POPT_ARG_NONE, &archive_mode, 0, 0, 0 }, {"server", 0, POPT_ARG_NONE, &am_server, 0, 0, 0 }, - {"sender", 0, POPT_ARG_NONE, 0, OPT_SENDER, 0, 0 }, + {"sender", 0, POPT_ARG_NONE, 0, OPT_SENDER, 0, 0 }, {"recursive", 'r', POPT_ARG_NONE, &recurse, 0, 0, 0 }, {"relative", 'R', POPT_ARG_VAL, &relative_paths, 1, 0, 0 }, {"no-relative", 0, POPT_ARG_VAL, &relative_paths, 0, 0, 0 }, @@ -374,27 +370,23 @@ static struct poptOption long_options[] = { {"timeout", 0, POPT_ARG_INT, &io_timeout, OPT_TIMEOUT, 0, 0 }, {"temp-dir", 'T', POPT_ARG_STRING, &tmpdir, 0, 0, 0 }, {"compare-dest", 0, POPT_ARG_STRING, &compare_dest, 0, 0, 0 }, - {"link-dest", 0, POPT_ARG_STRING, &compare_dest, OPT_LINK_DEST, 0, 0 }, + {"link-dest", 0, POPT_ARG_STRING, &compare_dest, OPT_LINK_DEST, 0, 0 }, /* TODO: Should this take an optional int giving the compression level? */ {"compress", 'z', POPT_ARG_NONE, &do_compression, 0, 0, 0 }, - {"daemon", 0, POPT_ARG_NONE, &daemon_opt, 0, 0, 0 }, - {"no-detach", 0, POPT_ARG_NONE, &no_detach, 0, 0, 0 }, {"stats", 0, POPT_ARG_NONE, &do_stats, 0, 0, 0 }, {"progress", 0, POPT_ARG_NONE, &do_progress, 0, 0, 0 }, {"partial", 0, POPT_ARG_NONE, &keep_partial, 0, 0, 0 }, + {"partial-dir", 0, POPT_ARG_STRING, &partial_dir, 0, 0, 0 }, {"ignore-errors", 0, POPT_ARG_NONE, &ignore_errors, 0, 0, 0 }, {"blocking-io", 0, POPT_ARG_VAL, &blocking_io, 1, 0, 0 }, {"no-blocking-io", 0, POPT_ARG_VAL, &blocking_io, 0, 0, 0 }, - {0, 'P', POPT_ARG_NONE, 0, 'P', 0, 0 }, - {"config", 0, POPT_ARG_STRING, &config_file, 0, 0, 0 }, - {"port", 0, POPT_ARG_INT, &rsync_port, 0, 0, 0 }, + {0, 'P', POPT_ARG_NONE, 0, 'P', 0, 0 }, {"log-format", 0, POPT_ARG_STRING, &log_format, 0, 0, 0 }, {"bwlimit", 0, POPT_ARG_INT, &bwlimit, 0, 0, 0 }, - {"address", 0, POPT_ARG_STRING, &bind_address, 0, 0, 0 }, {"backup-dir", 0, POPT_ARG_STRING, &backup_dir, 0, 0, 0 }, {"hard-links", 'H', POPT_ARG_NONE, &preserve_hard_links, 0, 0, 0 }, - {"read-batch", 0, POPT_ARG_STRING, &batch_name, OPT_READ_BATCH, 0, 0 }, - {"write-batch", 0, POPT_ARG_STRING, &batch_name, OPT_WRITE_BATCH, 0, 0 }, + {"read-batch", 0, POPT_ARG_STRING, &batch_name, OPT_READ_BATCH, 0, 0 }, + {"write-batch", 0, POPT_ARG_STRING, &batch_name, OPT_WRITE_BATCH, 0, 0 }, {"files-from", 0, POPT_ARG_STRING, &files_from, 0, 0, 0 }, {"from0", '0', POPT_ARG_NONE, &eol_nulls, 0, 0, 0}, {"no-implied-dirs", 0, POPT_ARG_VAL, &implied_dirs, 0, 0, 0 }, @@ -404,6 +396,48 @@ static struct poptOption long_options[] = { {"ipv4", '4', POPT_ARG_VAL, &default_af_hint, AF_INET, 0, 0 }, {"ipv6", '6', POPT_ARG_VAL, &default_af_hint, AF_INET6, 0, 0 }, #endif + /* All these options switch us into daemon-mode option-parsing. */ + {"address", 0, POPT_ARG_STRING, 0, OPT_DAEMON, 0, 0 }, + {"config", 0, POPT_ARG_STRING, 0, OPT_DAEMON, 0, 0 }, + {"daemon", 0, POPT_ARG_NONE, 0, OPT_DAEMON, 0, 0 }, + {"no-detach", 0, POPT_ARG_NONE, 0, OPT_DAEMON, 0, 0 }, + {"port", 0, POPT_ARG_INT, 0, OPT_DAEMON, 0, 0 }, + {0,0,0,0, 0, 0, 0} +}; + +static void daemon_usage(enum logcode F) +{ + print_rsync_version(F); + + rprintf(F,"\nUsage: rsync --daemon [OPTION]...\n"); + rprintf(F," --address=ADDRESS bind to the specified address\n"); + rprintf(F," --config=FILE specify alternate rsyncd.conf file\n"); + rprintf(F," --no-detach do not detach from the parent\n"); + rprintf(F," --port=PORT specify alternate rsyncd port number\n"); +#ifdef INET6 + rprintf(F," -4, --ipv4 prefer IPv4\n"); + rprintf(F," -6, --ipv6 prefer IPv6\n"); +#endif + rprintf(F," -h, --help show this help screen\n"); + + rprintf(F,"\nIf you were not trying to invoke rsync as a daemon, avoid using any of the\n"); + rprintf(F,"daemon-specific rsync options. See also the rsyncd.conf(5) man page.\n"); +} + +static struct poptOption long_daemon_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"address", 0, POPT_ARG_STRING, &bind_address, 0, 0, 0 }, + {"config", 0, POPT_ARG_STRING, &config_file, 0, 0, 0 }, + {"daemon", 0, POPT_ARG_NONE, &daemon_opt, 0, 0, 0 }, +#ifdef INET6 + {"ipv4", '4', POPT_ARG_VAL, &default_af_hint, AF_INET, 0, 0 }, + {"ipv6", '6', POPT_ARG_VAL, &default_af_hint, AF_INET6, 0, 0 }, +#endif + {"no-detach", 0, POPT_ARG_NONE, &no_detach, 0, 0, 0 }, + {"port", 0, POPT_ARG_INT, &rsync_port, 0, 0, 0 }, + {"protocol", 0, POPT_ARG_INT, &protocol_version, 0, 0, 0 }, + {"server", 0, POPT_ARG_NONE, &am_server, 0, 0, 0 }, + {"help", 'h', POPT_ARG_NONE, 0, 'h', 0, 0 }, {0,0,0,0, 0, 0, 0} }; @@ -434,23 +468,38 @@ void option_error(void) static void set_refuse_options(char *bp) { struct poptOption *op; - char *cp; + char *cp, shortname[2]; + int is_wild, found_match; + + shortname[1] = '\0'; while (1) { + while (*bp == ' ') bp++; + if (!*bp) + break; if ((cp = strchr(bp, ' ')) != NULL) *cp= '\0'; + /* If they specify "delete", reject all delete options. */ + if (strcmp(bp, "delete") == 0) + bp = "delete*"; + is_wild = strpbrk(bp, "*?[") != NULL; + found_match = 0; for (op = long_options; ; op++) { - if (!op->longName) { - rprintf(FLOG, - "Unknown option %s in \"refuse options\" setting\n", - bp); - break; - } - if (strcmp(bp, op->longName) == 0) { - op->val = (op - long_options)+OPT_REFUSED_BASE; + *shortname = op->shortName; + if (!op->longName && !*shortname) break; + if ((op->longName && wildmatch(bp, op->longName)) + || (*shortname && wildmatch(bp, shortname))) { + op->val = (op - long_options) + OPT_REFUSED_BASE; + found_match = 1; + if (!is_wild) + break; } } + if (!found_match) { + rprintf(FLOG, "No match for refuse-options string \"%s\"\n", + bp); + } if (!cp) break; *cp = ' '; @@ -459,12 +508,14 @@ static void set_refuse_options(char *bp) } -static int count_args(char const **argv) +static int count_args(const char **argv) { int i = 0; - while (argv[i] != NULL) - i++; + if (argv) { + while (argv[i] != NULL) + i++; + } return i; } @@ -503,6 +554,41 @@ int parse_arguments(int *argc, const char ***argv, int frommain) print_rsync_version(FINFO); exit_cleanup(0); + case OPT_DAEMON: + if (am_daemon) { + strcpy(err_buf, "Attempt to hack rsync thwarted!\n"); + return 0; + } + poptFreeContext(pc); + pc = poptGetContext(RSYNC_NAME, *argc, *argv, + long_daemon_options, 0); + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case 'h': + daemon_usage(FINFO); + exit_cleanup(0); + + default: + rprintf(FERROR, + "rsync: %s: %s (in daemon mode)\n", + poptBadOption(pc, POPT_BADOPTION_NOALIAS), + poptStrerror(opt)); + goto daemon_error; + } + } + if (!daemon_opt) { + rprintf(FERROR, "Daemon option(s) used without --daemon.\n"); + daemon_error: + rprintf(FERROR, + "(Type \"rsync --daemon --help\" for assistance with daemon mode.)\n"); + exit_cleanup(RERR_SYNTAX); + } + *argv = poptGetArgs(pc); + *argc = count_args(*argv); + daemon_opt = 0; + am_daemon = 1; + return 1; + case OPT_MODIFY_WINDOW: /* The value has already been set by popt, but * we need to remember that we're using a @@ -521,32 +607,28 @@ int parse_arguments(int *argc, const char ***argv, int frommain) break; case OPT_EXCLUDE: - if (am_server || sanitize_paths) - return 0; /* Impossible... */ add_exclude(&exclude_list, poptGetOptArg(pc), 0); break; case OPT_INCLUDE: - if (am_server || sanitize_paths) - return 0; /* Impossible... */ add_exclude(&exclude_list, poptGetOptArg(pc), XFLG_DEF_INCLUDE); break; case OPT_EXCLUDE_FROM: - if (am_server || sanitize_paths) - return 0; /* Impossible... */ - arg = poptGetOptArg(pc); - add_exclude_file(&exclude_list, arg, - XFLG_FATAL_ERRORS); - break; - case OPT_INCLUDE_FROM: - if (am_server || sanitize_paths) - return 0; /* Impossible... */ arg = poptGetOptArg(pc); - add_exclude_file(&exclude_list, arg, - XFLG_FATAL_ERRORS | XFLG_DEF_INCLUDE); + if (sanitize_paths) + arg = sanitize_path(NULL, arg, NULL, 0); + if (server_exclude_list.head) { + char *cp = (char *)arg; + clean_fname(cp, 1); + if (check_exclude(&server_exclude_list, cp, 0) < 0) + goto options_rejected; + } + add_exclude_file(&exclude_list, arg, XFLG_FATAL_ERRORS + | (opt == OPT_INCLUDE_FROM + ? XFLG_DEF_INCLUDE : 0)); break; case 'h': @@ -608,15 +690,14 @@ int parse_arguments(int *argc, const char ***argv, int frommain) struct poptOption *op = &long_options[opt-OPT_REFUSED_BASE]; int n = snprintf(err_buf, sizeof err_buf, - "This server does not support --%s\n", + "The server is configured to refuse --%s\n", op->longName) - 1; if (op->shortName) { snprintf(err_buf+n, sizeof err_buf-n, " (-%c)\n", op->shortName); } } else { - snprintf(err_buf, sizeof err_buf, - "%s%s: %s\n", + snprintf(err_buf, sizeof err_buf, "%s%s: %s\n", am_server ? "on remote machine: " : "", poptBadOption(pc, POPT_BADOPTION_NOALIAS), poptStrerror(opt)); @@ -643,23 +724,17 @@ int parse_arguments(int *argc, const char ***argv, int frommain) } #endif - if (block_size > MAX_MAP_SIZE) { - rprintf(FINFO, "limiting block-size to %d bytes\n", - MAX_MAP_SIZE); - block_size = MAX_MAP_SIZE; - } - if (write_batch && read_batch) { - rprintf(FERROR, + snprintf(err_buf, sizeof err_buf, "--write-batch and --read-batch can not be used together\n"); - exit_cleanup(RERR_SYNTAX); + return 0; } if (write_batch || read_batch) { if (dry_run) { - rprintf(FERROR, + snprintf(err_buf, sizeof err_buf, "--%s-batch cannot be used with --dry_run (-n)\n", write_batch ? "write" : "read"); - exit_cleanup(RERR_SYNTAX); + return 0; } if (am_server) { rprintf(FINFO, @@ -673,20 +748,21 @@ int parse_arguments(int *argc, const char ***argv, int frommain) } } if (read_batch && files_from) { - rprintf(FERROR, + snprintf(err_buf, sizeof err_buf, "--read-batch cannot be used with --files-from\n"); - exit_cleanup(RERR_SYNTAX); + return 0; } if (batch_name && strlen(batch_name) > MAX_BATCH_NAME_LEN) { - rprintf(FERROR, + snprintf(err_buf, sizeof err_buf, "the batch-file name must be %d characters or less.\n", MAX_BATCH_NAME_LEN); - exit_cleanup(RERR_SYNTAX); + return 0; } if (tmpdir && strlen(tmpdir) >= MAXPATHLEN - 10) { - rprintf(FERROR, "the --temp-dir path is WAY too long.\n"); - exit_cleanup(RERR_SYNTAX); + snprintf(err_buf, sizeof err_buf, + "the --temp-dir path is WAY too long.\n"); + return 0; } if (archive_mode) { @@ -706,45 +782,72 @@ int parse_arguments(int *argc, const char ***argv, int frommain) relative_paths = files_from? 1 : 0; *argv = poptGetArgs(pc); - if (*argv) - *argc = count_args(*argv); - else - *argc = 0; + *argc = count_args(*argv); if (sanitize_paths) { int i; for (i = *argc; i-- > 0; ) - (*argv)[i] = alloc_sanitize_path((*argv)[i], NULL); + (*argv)[i] = sanitize_path(NULL, (*argv)[i], "", 0); if (tmpdir) - tmpdir = alloc_sanitize_path(tmpdir, curr_dir); + tmpdir = sanitize_path(NULL, tmpdir, NULL, 0); + if (partial_dir) + partial_dir = sanitize_path(NULL, partial_dir, NULL, 0); if (compare_dest) - compare_dest = alloc_sanitize_path(compare_dest, curr_dir); + compare_dest = sanitize_path(NULL, compare_dest, NULL, 0); if (backup_dir) - backup_dir = alloc_sanitize_path(backup_dir, curr_dir); + backup_dir = sanitize_path(NULL, backup_dir, NULL, 0); if (files_from) - files_from = alloc_sanitize_path(files_from, curr_dir); + files_from = sanitize_path(NULL, files_from, NULL, 0); } - - if (daemon_opt) { - daemon_opt = 0; - am_daemon = 1; - return 1; + if (server_exclude_list.head && !am_sender) { + struct exclude_list_struct *elp = &server_exclude_list; + if (tmpdir) { + clean_fname(tmpdir, 1); + if (check_exclude(elp, tmpdir, 1) < 0) + goto options_rejected; + } + if (partial_dir) { + clean_fname(partial_dir, 1); + if (check_exclude(elp, partial_dir, 1) < 0) + goto options_rejected; + } + if (compare_dest) { + clean_fname(compare_dest, 1); + if (check_exclude(elp, compare_dest, 1) < 0) + goto options_rejected; + } + if (backup_dir) { + clean_fname(backup_dir, 1); + if (check_exclude(elp, backup_dir, 1) < 0) + goto options_rejected; + } + } + if (server_exclude_list.head && files_from) { + clean_fname(files_from, 1); + if (check_exclude(&server_exclude_list, files_from, 0) < 0) { + options_rejected: + snprintf(err_buf, sizeof err_buf, + "Your options have been rejected by the server.\n"); + return 0; + } } if (!backup_suffix) backup_suffix = backup_dir ? "" : BACKUP_SUFFIX; backup_suffix_len = strlen(backup_suffix); if (strchr(backup_suffix, '/') != NULL) { - rprintf(FERROR, "--suffix cannot contain slashes: %s\n", + snprintf(err_buf, sizeof err_buf, + "--suffix cannot contain slashes: %s\n", backup_suffix); - exit_cleanup(RERR_SYNTAX); + return 0; } if (backup_dir) { backup_dir_len = strlcpy(backup_dir_buf, backup_dir, sizeof backup_dir_buf); backup_dir_remainder = sizeof backup_dir_buf - backup_dir_len; if (backup_dir_remainder < 32) { - rprintf(FERROR, "the --backup-dir path is WAY too long.\n"); - exit_cleanup(RERR_SYNTAX); + snprintf(err_buf, sizeof err_buf, + "the --backup-dir path is WAY too long.\n"); + return 0; } if (backup_dir_buf[backup_dir_len - 1] != '/') { backup_dir_buf[backup_dir_len++] = '/'; @@ -753,9 +856,9 @@ int parse_arguments(int *argc, const char ***argv, int frommain) if (verbose > 1 && !am_sender) rprintf(FINFO, "backup_dir is %s\n", backup_dir_buf); } else if (!backup_suffix_len && (!am_server || !am_sender)) { - rprintf(FERROR, + snprintf(err_buf, sizeof err_buf, "--suffix cannot be a null string without --backup-dir\n"); - exit_cleanup(RERR_SYNTAX); + return 0; } if (do_progress && !verbose) @@ -769,6 +872,11 @@ int parse_arguments(int *argc, const char ***argv, int frommain) if (inplace) { #if HAVE_FTRUNCATE + if (partial_dir) { + snprintf(err_buf, sizeof err_buf, + "--inplace cannot be used with --partial-dir\n"); + return 0; + } keep_partial = 0; #else snprintf(err_buf, sizeof err_buf, @@ -776,11 +884,29 @@ int parse_arguments(int *argc, const char ***argv, int frommain) am_server ? "server" : "client"); return 0; #endif + if (compare_dest) { + snprintf(err_buf, sizeof err_buf, + "--inplace does not yet work with %s\n", + link_dest ? "--link-dest" : "--compare-dest"); + return 0; + } + } else { + if (keep_partial && !partial_dir) + partial_dir = getenv("RSYNC_PARTIAL_DIR"); + if (partial_dir) { + if (!*partial_dir || strcmp(partial_dir, ".") == 0) + partial_dir = NULL; + else if (*partial_dir != '/') { + add_exclude(&exclude_list, partial_dir, + XFLG_DIRECTORY); + } + keep_partial = 1; + } } if (files_from) { char *colon; - if (*argc != 2 && !(am_server && am_sender && *argc == 1)) { + if (*argc > 2 || (!am_daemon && *argc == 1)) { usage(FERROR); exit_cleanup(RERR_SYNTAX); } @@ -796,16 +922,17 @@ int parse_arguments(int *argc, const char ***argv, int frommain) } remote_filesfrom_file = colon+1 + (colon[1] == ':'); if (strcmp(remote_filesfrom_file, "-") == 0) { - rprintf(FERROR, "Invalid --files-from remote filename\n"); - exit_cleanup(RERR_SYNTAX); + snprintf(err_buf, sizeof err_buf, + "Invalid --files-from remote filename\n"); + return 0; } } else { filesfrom_fd = open(files_from, O_RDONLY|O_BINARY); if (filesfrom_fd < 0) { - rsyserr(FERROR, errno, - "failed to open files-from file %s", - files_from); - exit_cleanup(RERR_FILEIO); + snprintf(err_buf, sizeof err_buf, + "failed to open files-from file %s: %s\n", + files_from, strerror(errno)); + return 0; } } } @@ -948,10 +1075,18 @@ void server_options(char **args,int *argc) args[ac++] = arg; } - if (delete_excluded) - args[ac++] = "--delete-excluded"; - else if (delete_mode) - args[ac++] = "--delete"; + if (am_sender) { + if (delete_excluded) + args[ac++] = "--delete-excluded"; + else if (delete_mode) + args[ac++] = "--delete"; + + if (delete_after) + args[ac++] = "--delete-after"; + + if (force_delete) + args[ac++] = "--force"; + } if (size_only) args[ac++] = "--size-only"; @@ -968,15 +1103,12 @@ void server_options(char **args,int *argc) args[ac++] = arg; } - if (keep_partial) + if (partial_dir && am_sender) { + args[ac++] = "--partial-dir"; + args[ac++] = partial_dir; + } else if (keep_partial) args[ac++] = "--partial"; - if (force_delete) - args[ac++] = "--force"; - - if (delete_after) - args[ac++] = "--delete-after"; - if (ignore_errors) args[ac++] = "--ignore-errors";