X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=options.c;h=5b3d6dea92de69ca32a65c178a412d812b5a6b72;hb=5183c0d6f0bf6786d5e9fa149d0d00f664533441;hp=73d5ed47c93635500644c0c5c929918ee5c1c3b0;hpb=3094552311b76a7316f6987ad5f21e28318b4313;p=rsync.git diff --git a/options.c b/options.c index 73d5ed47..5b3d6dea 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-2020 Wayne Davison + * Copyright (C) 2002-2022 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 @@ -21,16 +21,15 @@ #include "rsync.h" #include "itypes.h" -#include "latest-year.h" +#include "ifuncs.h" #include extern int module_id; extern int local_server; extern int sanitize_paths; -extern int daemon_over_rsh; +extern int trust_sender_args; +extern int trust_sender_filter; extern unsigned int module_dirlen; -extern struct name_num_obj valid_checksums; -extern struct name_num_obj valid_compressions; extern filter_rule_list filter_list; extern filter_rule_list daemon_filter_list; @@ -50,6 +49,7 @@ int append_mode = 0; int keep_dirlinks = 0; int copy_dirlinks = 0; int copy_links = 0; +int copy_devices = 0; int write_devices = 0; int preserve_links = 0; int preserve_hard_links = 0; @@ -61,13 +61,18 @@ int preserve_devices = 0; int preserve_specials = 0; int preserve_uid = 0; int preserve_gid = 0; -int preserve_times = 0; +int preserve_mtimes = 0; int preserve_atimes = 0; +int preserve_crtimes = 0; +int omit_dir_times = 0; +int omit_link_times = 0; +int trust_sender = 0; int update_only = 0; int open_noatime = 0; int cvs_exclude = 0; int dry_run = 0; int do_xfers = 1; +int do_fsync = 0; int ignore_times = 0; int delete_mode = 0; int delete_during = 0; @@ -89,7 +94,8 @@ int relative_paths = -1; int implied_dirs = 1; int missing_args = 0; /* 0 = FERROR_XFER, 1 = ignore, 2 = delete */ int numeric_ids = 0; -int msgs2stderr = 0; +int msgs2stderr = 2; /* Default: send errors to stderr for local & remote-shell transfers */ +int saw_stderr_opt = 0; int allow_8bit_chars = 0; int force_delete = 0; int io_timeout = 0; @@ -100,8 +106,10 @@ int filesfrom_fd = -1; char *filesfrom_host = NULL; int eol_nulls = 0; int protect_args = -1; +int old_style_args = -1; int human_readable = 1; int recurse = 0; +int mkpath_dest_arg = 0; int allow_inc_recurse = 1; int xfer_dirs = -1; int am_daemon = 0; @@ -127,7 +135,8 @@ int blocking_io = -1; int checksum_seed = 0; int inplace = 0; int delay_updates = 0; -long block_size = 0; /* "long" because popt can't set an int32. */ +int32 block_size = 0; +time_t stop_at_utime = 0; char *skip_compress = NULL; char *copy_as = NULL; item_list dparam_list = EMPTY_ITEM_LIST; @@ -181,6 +190,10 @@ int rsync_port = 0; int alt_dest_type = 0; int basis_dir_cnt = 0; +#define DEFAULT_MAX_ALLOC (1024L * 1024 * 1024) +size_t max_alloc = DEFAULT_MAX_ALLOC; +char *max_alloc_arg; + static int version_opt_cnt = 0; static int remote_option_alloc = 0; int remote_option_cnt = 0; @@ -188,10 +201,6 @@ const char **remote_options = NULL; const char *checksum_choice = NULL; const char *compress_choice = NULL; -#ifndef __APPLE__ /* Do we need a configure check for this? */ -#define SUPPORT_ATIMES 1 -#endif - int quiet = 0; int output_motd = 1; int log_before_transfer = 0; @@ -227,7 +236,7 @@ static const char *debug_verbosity[] = { #define MAX_VERBOSITY ((int)(sizeof debug_verbosity / sizeof debug_verbosity[0]) - 1) static const char *info_verbosity[1+MAX_VERBOSITY] = { - /*0*/ NULL, + /*0*/ "NONREG", /*1*/ "COPY,DEL,FLIST,MISC,NAME,STATS,SYMSAFE", /*2*/ "BACKUP,MISC2,MOUNT,NAME2,REMOVE,SKIP", }; @@ -265,9 +274,10 @@ static struct output_struct info_words[COUNT_INFO+1] = { INFO_WORD(MISC, W_SND|W_REC, "Mention miscellaneous information (levels 1-2)"), INFO_WORD(MOUNT, W_SND|W_REC, "Mention mounts that were found or skipped"), INFO_WORD(NAME, W_SND|W_REC, "Mention 1) updated file/dir names, 2) unchanged names"), + INFO_WORD(NONREG, W_REC, "Mention skipped non-regular files (default 1, 0 disables)"), INFO_WORD(PROGRESS, W_CLI, "Mention 1) per-file progress or 2) total transfer progress"), INFO_WORD(REMOVE, W_SND, "Mention files removed on the sending side"), - INFO_WORD(SKIP, W_REC, "Mention files that are skipped due to options used"), + INFO_WORD(SKIP, W_REC, "Mention files skipped due to transfer overrides (levels 1-2)"), INFO_WORD(STATS, W_CLI|W_SRV, "Mention statistics at end of run (levels 1-3)"), INFO_WORD(SYMSAFE, W_SND|W_REC, "Mention symlinks that are unsafe"), { NULL, "--info", 0, 0, 0, 0 } @@ -286,7 +296,7 @@ static struct output_struct debug_words[COUNT_DEBUG+1] = { DEBUG_WORD(DELTASUM, W_SND|W_REC, "Debug delta-transfer checksumming (levels 1-4)"), DEBUG_WORD(DUP, W_REC, "Debug weeding of duplicate names"), DEBUG_WORD(EXIT, W_CLI|W_SRV, "Debug exit events (levels 1-3)"), - DEBUG_WORD(FILTER, W_SND|W_REC, "Debug filter actions (levels 1-2)"), + DEBUG_WORD(FILTER, W_SND|W_REC, "Debug filter actions (levels 1-3)"), DEBUG_WORD(FLIST, W_SND|W_REC, "Debug file-list operations (levels 1-4)"), DEBUG_WORD(FUZZY, W_REC, "Debug fuzzy scoring (levels 1-2)"), DEBUG_WORD(GENR, W_REC, "Debug generator functions"), @@ -307,8 +317,6 @@ static int verbose = 0; static int do_stats = 0; static int do_progress = 0; static int daemon_opt; /* sets am_daemon after option error-reporting */ -static int omit_dir_times = 0; -static int omit_link_times = 0; static int F_option_cnt = 0; static int modify_window_set; static int itemize_changes = 0; @@ -382,8 +390,7 @@ static char *make_output_option(struct output_struct *words, short *levels, ucha return NULL; len++; - if (!(buf = new_array(char, len))) - out_of_memory("make_output_option"); + buf = new_array(char, len); pos = 0; if (skipped || max < 5) @@ -488,9 +495,9 @@ static void output_item_help(struct output_struct *words) rprintf(FINFO, fmt, "HELP", "Output this help message"); rprintf(FINFO, "\n"); - rprintf(FINFO, "Options added for each increase in verbose level:\n"); + rprintf(FINFO, "Options added at each level of verbosity:\n"); - for (j = 1; j <= MAX_VERBOSITY; j++) { + for (j = 0; j <= MAX_VERBOSITY; j++) { parse_output_words(words, levels, verbosity[j], HELP_PRIORITY); opt = make_output_option(words, levels, W_CLI|W_SRV|W_SND|W_REC); if (opt) { @@ -509,7 +516,7 @@ static void set_output_verbosity(int level, uchar priority) if (level > MAX_VERBOSITY) level = MAX_VERBOSITY; - for (j = 1; j <= level; j++) { + for (j = 0; j <= level; j++) { parse_output_words(info_words, info_levels, info_verbosity[j], priority); parse_output_words(debug_words, debug_levels, debug_verbosity[j], priority); } @@ -528,7 +535,7 @@ void limit_output_verbosity(int level) memset(debug_limits, 0, sizeof debug_limits); /* Compute the level limits in the above arrays. */ - for (j = 1; j <= level; j++) { + for (j = 0; j <= level; j++) { parse_output_words(info_words, info_limits, info_verbosity[j], LIMIT_PRIORITY); parse_output_words(debug_words, debug_limits, debug_verbosity[j], LIMIT_PRIORITY); } @@ -569,218 +576,14 @@ void negate_output_levels(void) debug_levels[j] *= -1; } -static char *istring(const char *fmt, int val) -{ - char *str; - if (asprintf(&str, fmt, val) < 0) - out_of_memory("istring"); - return str; -} - -static void print_capabilities(enum logcode f) -{ - STRUCT_STAT *dumstat; - char line_buf[75]; - int line_len, j; - char *capabilities[] = { - istring("%d-bit files", (int)(sizeof (OFF_T) * 8)), - istring("%d-bit inums", (int)(sizeof dumstat->st_ino * 8)), /* Don't check ino_t! */ - istring("%d-bit timestamps", (int)(sizeof (time_t) * 8)), - istring("%d-bit long ints", (int)(sizeof (int64) * 8)), - -#ifndef HAVE_SOCKETPAIR - "no " -#endif - "socketpairs", - -#ifndef SUPPORT_HARD_LINKS - "no " -#endif - "hardlinks", - -#ifndef SUPPORT_LINKS - "no " -#endif - "symlinks", - -#ifndef INET6 - "no " -#endif - "IPv6", - -#ifndef SUPPORT_ATIMES - "no " -#endif - "atimes", - - "batchfiles", - -#ifndef HAVE_FTRUNCATE - "no " -#endif - "inplace", - -#ifndef HAVE_FTRUNCATE - "no " -#endif - "append", - -#ifndef SUPPORT_ACLS - "no " -#endif - "ACLs", - -#ifndef SUPPORT_XATTRS - "no " -#endif - "xattrs", - -#ifdef RSYNC_USE_PROTECTED_ARGS - "default " -#else - "optional " -#endif - "protect-args", - -#ifndef ICONV_OPTION - "no " -#endif - "iconv", - -#ifndef CAN_SET_SYMLINK_TIMES - "no " -#endif - "symtimes", - -#ifndef SUPPORT_PREALLOCATION - "no " -#endif - "prealloc", - - "*" /* All options after this point are hidden w/o -V -V */ -#ifndef HAVE_SIMD - "no " -#endif - "SIMD", - -#ifndef HAVE_ASM - "no " -#endif - "asm", - -#ifndef USE_OPENSSL - "no " -#endif - "openssl-crypto", - - NULL - }; - - for (line_len = 0, j = 0; ; j++) { - char *cap = capabilities[j]; - if (!cap) - break; - if (*cap == '*') { - if (version_opt_cnt >= 2) - capabilities[j]++; - else - capabilities[j] = NULL; - break; - } - } - - for (line_len = 0, j = 0; ; j++) { - char *cap = capabilities[j]; - int cap_len = cap ? strlen(cap) : 1000; - int need_comma = cap && capabilities[j+1] != NULL ? 1 : 0; - if (line_len + 1 + cap_len + need_comma >= (int)sizeof line_buf) { - rprintf(f, " %s\n", line_buf); - line_len = 0; - } - if (!cap) - break; - line_len += snprintf(line_buf+line_len, sizeof line_buf - line_len, " %s%s", cap, need_comma ? "," : ""); - } -} - -static void print_rsync_version(enum logcode f) -{ - char tmpbuf[256], *subprotocol = ""; - -#if SUBPROTOCOL_VERSION != 0 - subprotocol = istring(".PR%d", SUBPROTOCOL_VERSION); -#endif - rprintf(f, "%s version %s protocol version %d%s\n", - RSYNC_NAME, RSYNC_VERSION, PROTOCOL_VERSION, subprotocol); - - rprintf(f, "Copyright (C) 1996-" LATEST_YEAR " by Andrew Tridgell, Wayne Davison, and others.\n"); - rprintf(f, "Web site: http://rsync.samba.org/\n"); - - rprintf(f, "Capabilities:\n"); - print_capabilities(f); - - rprintf(f, "Checksum list:\n"); - get_default_nno_list(&valid_checksums, tmpbuf, sizeof tmpbuf, '('); - rprintf(f, " %s\n", tmpbuf); - - rprintf(f, "Compress list:\n"); - get_default_nno_list(&valid_compressions, tmpbuf, sizeof tmpbuf, '('); - rprintf(f, " %s\n", tmpbuf); - -#ifdef MAINTAINER_MODE - rprintf(f, "Panic Action: \"%s\"\n", get_panic_action()); -#endif - -#if SIZEOF_INT64 < 8 - rprintf(f, "WARNING: no 64-bit integers on this platform!\n"); -#endif - if (sizeof (int64) != SIZEOF_INT64) { - rprintf(f, - "WARNING: size mismatch in SIZEOF_INT64 define (%d != %d)\n", - (int) SIZEOF_INT64, (int) sizeof (int64)); - } - - rprintf(f,"\n"); - rprintf(f,"rsync comes with ABSOLUTELY NO WARRANTY. This is free software, and you\n"); - rprintf(f,"are welcome to redistribute it under certain conditions. See the GNU\n"); - rprintf(f,"General Public Licence for details.\n"); -} - - -void usage(enum logcode F) -{ - print_rsync_version(F); - - rprintf(F,"\n"); - rprintf(F,"rsync is a file transfer program capable of efficient remote update\n"); - rprintf(F,"via a fast differencing algorithm.\n"); - - rprintf(F,"\n"); - rprintf(F,"Usage: rsync [OPTION]... SRC [SRC]... DEST\n"); - rprintf(F," or rsync [OPTION]... SRC [SRC]... [USER@]HOST:DEST\n"); - rprintf(F," or rsync [OPTION]... SRC [SRC]... [USER@]HOST::DEST\n"); - rprintf(F," or rsync [OPTION]... SRC [SRC]... rsync://[USER@]HOST[:PORT]/DEST\n"); - rprintf(F," or rsync [OPTION]... [USER@]HOST:SRC [DEST]\n"); - rprintf(F," or rsync [OPTION]... [USER@]HOST::SRC [DEST]\n"); - rprintf(F," or rsync [OPTION]... rsync://[USER@]HOST[:PORT]/SRC [DEST]\n"); - rprintf(F,"The ':' usages connect via remote shell, while '::' & 'rsync://' usages connect\n"); - rprintf(F,"to an rsync daemon, and require SRC or DEST to start with a module name.\n"); - rprintf(F,"\n"); - rprintf(F,"Options\n"); -#include "help-rsync.h" - rprintf(F,"\n"); - rprintf(F,"Use \"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_SERVER = 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_ICONV, OPT_INFO, OPT_DEBUG, - OPT_USERMAP, OPT_GROUPMAP, OPT_CHOWN, OPT_BWLIMIT, - OPT_OLD_COMPRESS, OPT_NEW_COMPRESS, OPT_NO_COMPRESS, + OPT_NO_D, OPT_APPEND, OPT_NO_ICONV, OPT_INFO, OPT_DEBUG, OPT_BLOCK_SIZE, + OPT_USERMAP, OPT_GROUPMAP, OPT_CHOWN, OPT_BWLIMIT, OPT_STDERR, + OPT_OLD_COMPRESS, OPT_NEW_COMPRESS, OPT_NO_COMPRESS, OPT_OLD_ARGS, + OPT_STOP_AFTER, OPT_STOP_AT, OPT_REFUSED_BASE = 9000}; static struct poptOption long_options[] = { @@ -792,7 +595,8 @@ static struct poptOption long_options[] = { {"no-v", 0, POPT_ARG_VAL, &verbose, 0, 0, 0 }, {"info", 0, POPT_ARG_STRING, 0, OPT_INFO, 0, 0 }, {"debug", 0, POPT_ARG_STRING, 0, OPT_DEBUG, 0, 0 }, - {"msgs2stderr", 0, POPT_ARG_NONE, &msgs2stderr, 0, 0, 0 }, + {"stderr", 0, POPT_ARG_STRING, 0, OPT_STDERR, 0, 0 }, + {"msgs2stderr", 0, POPT_ARG_VAL, &msgs2stderr, 1, 0, 0 }, {"no-msgs2stderr", 0, POPT_ARG_VAL, &msgs2stderr, 0, 0, 0 }, {"quiet", 'q', POPT_ARG_NONE, 0, 'q', 0, 0 }, {"motd", 0, POPT_ARG_VAL, &output_motd, 1, 0, 0 }, @@ -825,14 +629,17 @@ static struct poptOption long_options[] = { {"xattrs", 'X', POPT_ARG_NONE, 0, 'X', 0, 0 }, {"no-xattrs", 0, POPT_ARG_VAL, &preserve_xattrs, 0, 0, 0 }, {"no-X", 0, POPT_ARG_VAL, &preserve_xattrs, 0, 0, 0 }, - {"times", 't', POPT_ARG_VAL, &preserve_times, 1, 0, 0 }, - {"no-times", 0, POPT_ARG_VAL, &preserve_times, 0, 0, 0 }, - {"no-t", 0, POPT_ARG_VAL, &preserve_times, 0, 0, 0 }, + {"times", 't', POPT_ARG_VAL, &preserve_mtimes, 1, 0, 0 }, + {"no-times", 0, POPT_ARG_VAL, &preserve_mtimes, 0, 0, 0 }, + {"no-t", 0, POPT_ARG_VAL, &preserve_mtimes, 0, 0, 0 }, {"atimes", 'U', POPT_ARG_NONE, 0, 'U', 0, 0 }, {"no-atimes", 0, POPT_ARG_VAL, &preserve_atimes, 0, 0, 0 }, {"no-U", 0, POPT_ARG_VAL, &preserve_atimes, 0, 0, 0 }, {"open-noatime", 0, POPT_ARG_VAL, &open_noatime, 1, 0, 0 }, {"no-open-noatime", 0, POPT_ARG_VAL, &open_noatime, 0, 0, 0 }, + {"crtimes", 'N', POPT_ARG_VAL, &preserve_crtimes, 1, 0, 0 }, + {"no-crtimes", 0, POPT_ARG_VAL, &preserve_crtimes, 0, 0, 0 }, + {"no-N", 0, POPT_ARG_VAL, &preserve_crtimes, 0, 0, 0 }, {"omit-dir-times", 'O', POPT_ARG_VAL, &omit_dir_times, 1, 0, 0 }, {"no-omit-dir-times",0, POPT_ARG_VAL, &omit_dir_times, 0, 0, 0 }, {"no-O", 0, POPT_ARG_VAL, &omit_dir_times, 0, 0, 0 }, @@ -853,6 +660,7 @@ static struct poptOption long_options[] = { {"no-D", 0, POPT_ARG_NONE, 0, OPT_NO_D, 0, 0 }, {"devices", 0, POPT_ARG_VAL, &preserve_devices, 1, 0, 0 }, {"no-devices", 0, POPT_ARG_VAL, &preserve_devices, 0, 0, 0 }, + {"copy-devices", 0, POPT_ARG_NONE, ©_devices, 0, 0, 0 }, {"write-devices", 0, POPT_ARG_VAL, &write_devices, 1, 0, 0 }, {"no-write-devices", 0, POPT_ARG_VAL, &write_devices, 0, 0, 0 }, {"specials", 0, POPT_ARG_VAL, &preserve_specials, 1, 0, 0 }, @@ -889,6 +697,7 @@ static struct poptOption long_options[] = { {"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 }, + {"max-alloc", 0, POPT_ARG_STRING, &max_alloc_arg, 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 }, @@ -929,7 +738,7 @@ static struct poptOption long_options[] = { {"no-c", 0, POPT_ARG_VAL, &always_checksum, 0, 0, 0 }, {"checksum-choice", 0, POPT_ARG_STRING, &checksum_choice, 0, 0, 0 }, {"cc", 0, POPT_ARG_STRING, &checksum_choice, 0, 0, 0 }, - {"block-size", 'B', POPT_ARG_LONG, &block_size, 0, 0, 0 }, + {"block-size", 'B', POPT_ARG_STRING, 0, OPT_BLOCK_SIZE, 0, 0 }, {"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 }, @@ -977,9 +786,12 @@ static struct poptOption long_options[] = { {"files-from", 0, POPT_ARG_STRING, &files_from, 0, 0, 0 }, {"from0", '0', POPT_ARG_VAL, &eol_nulls, 1, 0, 0}, {"no-from0", 0, POPT_ARG_VAL, &eol_nulls, 0, 0, 0}, + {"old-args", 0, POPT_ARG_NONE, 0, OPT_OLD_ARGS, 0, 0}, + {"no-old-args", 0, POPT_ARG_VAL, &old_style_args, 0, 0, 0}, {"protect-args", 's', POPT_ARG_VAL, &protect_args, 1, 0, 0}, {"no-protect-args", 0, POPT_ARG_VAL, &protect_args, 0, 0, 0}, {"no-s", 0, POPT_ARG_VAL, &protect_args, 0, 0, 0}, + {"trust-sender", 0, POPT_ARG_VAL, &trust_sender, 1, 0, 0}, {"numeric-ids", 0, POPT_ARG_VAL, &numeric_ids, 1, 0, 0 }, {"no-numeric-ids", 0, POPT_ARG_VAL, &numeric_ids, 0, 0, 0 }, {"usermap", 0, POPT_ARG_STRING, 0, OPT_USERMAP, 0, 0 }, @@ -989,6 +801,10 @@ static struct poptOption long_options[] = { {"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 }, + {"fsync", 0, POPT_ARG_NONE, &do_fsync, 0, 0, 0 }, + {"stop-after", 0, POPT_ARG_STRING, 0, OPT_STOP_AFTER, 0, 0 }, + {"time-limit", 0, POPT_ARG_STRING, 0, OPT_STOP_AFTER, 0, 0 }, /* earlier stop-after name */ + {"stop-at", 0, POPT_ARG_STRING, 0, OPT_STOP_AT, 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 }, @@ -999,6 +815,8 @@ static struct poptOption long_options[] = { {"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 }, + {"mkpath", 0, POPT_ARG_VAL, &mkpath_dest_arg, 1, 0, 0 }, + {"no-mkpath", 0, POPT_ARG_VAL, &mkpath_dest_arg, 0, 0, 0 }, {"qsort", 0, POPT_ARG_NONE, &use_qsort, 0, 0, 0 }, {"copy-as", 0, POPT_ARG_STRING, ©_as, 0, 0, 0 }, {"address", 0, POPT_ARG_STRING, &bind_address, 0, 0, 0 }, @@ -1023,18 +841,6 @@ static struct poptOption long_options[] = { {0,0,0,0, 0, 0, 0} }; -static void daemon_usage(enum logcode F) -{ - print_rsync_version(F); - - rprintf(F,"\n"); - rprintf(F,"Usage: rsync --daemon [OPTION]...\n"); -#include "help-rsyncd.h" - rprintf(F,"\n"); - rprintf(F,"If 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 }, @@ -1149,6 +955,7 @@ static void set_refuse_options(void) || strcmp("iconv", longName) == 0 || strcmp("no-iconv", longName) == 0 || strcmp("checksum-seed", longName) == 0 + || strcmp("copy-devices", longName) == 0 /* disable wild-match (it gets refused below) */ || strcmp("write-devices", longName) == 0 /* disable wild-match (it gets refused below) */ || strcmp("log-format", longName) == 0 /* aka out-format (NOT log-file-format) */ || strcmp("sender", longName) == 0 @@ -1160,6 +967,7 @@ static void set_refuse_options(void) assert(list_end != NULL); if (am_daemon) { /* Refused by default, but can be accepted via a negated exact match. */ + parse_one_refuse_match(0, "copy-devices", list_end); parse_one_refuse_match(0, "write-devices", list_end); } @@ -1193,12 +1001,18 @@ static void set_refuse_options(void) #ifndef SUPPORT_HARD_LINKS parse_one_refuse_match(0, "link-dest", list_end); #endif +#ifndef HAVE_MKTIME + parse_one_refuse_match(0, "stop-at", list_end); +#endif #ifndef ICONV_OPTION parse_one_refuse_match(0, "iconv", list_end); #endif #ifndef HAVE_SETVBUF parse_one_refuse_match(0, "outbuf", list_end); #endif +#ifndef SUPPORT_CRTIMES + parse_one_refuse_match(0, "crtimes", list_end); +#endif /* Now we use the descrip values to actually mark the options for refusal. */ for (op = long_options; op != list_end; op++) { @@ -1251,15 +1065,18 @@ static int count_args(const char **argv) return i; } - -static OFF_T parse_size_arg(char **size_arg, char def_suf) +/* If the size_arg is an invalid string or the value is < min_value, an error + * is put into err_buf & the return is -1. Note that this parser does NOT + * support negative numbers, so a min_value < 0 doesn't make any sense. */ +static ssize_t parse_size_arg(const char *size_arg, char def_suf, const char *opt_name, + ssize_t min_value, ssize_t max_value, BOOL unlimited_0) { - int reps, mult, make_compatible = 0; - const char *arg; - OFF_T size = 1; + int reps, mult, len; + const char *arg, *err = "invalid", *min_max = NULL; + ssize_t limit = -1, size = 1; - for (arg = *size_arg; isDigit(arg); arg++) {} - if (*arg == '.') + for (arg = size_arg; isDigit(arg); arg++) {} + if (*arg == '.' || *arg == get_decimal_point()) /* backward compatibility: always allow '.' */ for (arg++; isDigit(arg); arg++) {} switch (*arg && *arg != '+' && *arg != '-' ? *arg++ : def_suf) { case 'b': case 'B': @@ -1274,40 +1091,197 @@ static OFF_T parse_size_arg(char **size_arg, char def_suf) case 'g': case 'G': reps = 3; break; + case 't': case 'T': + reps = 4; + break; + case 'p': case 'P': + reps = 5; + break; default: - return -1; + goto failure; } if (*arg == 'b' || *arg == 'B') - mult = 1000, make_compatible = 1, arg++; + mult = 1000, arg++; else if (!*arg || *arg == '+' || *arg == '-') mult = 1024; else if (strncasecmp(arg, "ib", 2) == 0) mult = 1024, arg += 2; else - return -1; + goto failure; while (reps--) size *= mult; - size *= atof(*size_arg); - if ((*arg == '+' || *arg == '-') && arg[1] == '1') - size += atoi(arg), make_compatible = 1, arg += 2; + size *= atof(size_arg); + if ((*arg == '+' || *arg == '-') && arg[1] == '1' && arg != size_arg) + size += atoi(arg), arg += 2; if (*arg) - return -1; - if (size > 0 && make_compatible && def_suf == 'b') { - /* We convert this manually because we may need %lld precision, - * and that's not a portable sprintf() escape. */ - char buf[128], *s = buf + sizeof buf - 1; - OFF_T num = size; - *s = '\0'; - while (num) { - *--s = (char)(num % 10) + '0'; - num /= 10; - } - if (!(*size_arg = strdup(s))) - out_of_memory("parse_size_arg"); + goto failure; + if (size < 0 || (max_value >= 0 && size > max_value)) { + err = "too large"; + min_max = "max"; + limit = max_value; + goto failure; + } + if (size < min_value && (!unlimited_0 || size != 0)) { + err = "too small"; + min_max = "min"; + limit = min_value; + goto failure; } return size; + +failure: + len = snprintf(err_buf, sizeof err_buf - 1, "--%s=%s is %s", opt_name, size_arg, err); + if (min_max && limit >= 0 && len < (int)sizeof err_buf - 10) { + len += snprintf(err_buf + len, sizeof err_buf - len - 1, " (%s: %s%s)", + min_max, do_big_num(limit, 3, NULL), + unlimited_0 && min_max[1] == 'i' ? " or 0 for unlimited" : ""); + } + err_buf[len] = '\n'; + err_buf[len+1] = '\0'; + return -1; } +#ifdef HAVE_MKTIME +/* Allow the user to specify a time in the format yyyy-mm-ddThh:mm while + * also allowing abbreviated data. For instance, if the time is omitted, + * it defaults to midnight. If the date is omitted, it defaults to the + * next possible date in the future with the specified time. Even the + * year or year-month can be omitted, again defaulting to the next date + * in the future that matches the specified information. A 2-digit year + * is also OK, as is using '/' instead of '-'. */ +static time_t parse_time(const char *arg) +{ + const char *cp; + time_t val, now = time(NULL); + struct tm t, *today = localtime(&now); + int in_date, old_mday, n; + + memset(&t, 0, sizeof t); + t.tm_year = t.tm_mon = t.tm_mday = -1; + t.tm_hour = t.tm_min = t.tm_isdst = -1; + cp = arg; + if (*cp == 'T' || *cp == 't' || *cp == ':') { + in_date = *cp == ':' ? 0 : -1; + cp++; + } else + in_date = 1; + for ( ; ; cp++) { + if (!isDigit(cp)) + return (time_t)-1; + n = 0; + do { + n = n * 10 + *cp++ - '0'; + } while (isDigit(cp)); + if (*cp == ':') + in_date = 0; + if (in_date > 0) { + if (t.tm_year != -1) + return (time_t)-1; + t.tm_year = t.tm_mon; + t.tm_mon = t.tm_mday; + t.tm_mday = n; + if (!*cp) + break; + if (*cp == 'T' || *cp == 't') { + if (!cp[1]) + break; + in_date = -1; + } else if (*cp != '-' && *cp != '/') + return (time_t)-1; + continue; + } + if (t.tm_hour != -1) + return (time_t)-1; + t.tm_hour = t.tm_min; + t.tm_min = n; + if (!*cp) { + if (in_date < 0) + return (time_t)-1; + break; + } + if (*cp != ':') + return (time_t)-1; + in_date = 0; + } + + in_date = 0; + if (t.tm_year < 0) { + t.tm_year = today->tm_year; + in_date = 1; + } else if (t.tm_year < 100) { + while (t.tm_year < today->tm_year) + t.tm_year += 100; + } else + t.tm_year -= 1900; + if (t.tm_mon < 0) { + t.tm_mon = today->tm_mon; + in_date = 2; + } else + t.tm_mon--; + if (t.tm_mday < 0) { + t.tm_mday = today->tm_mday; + in_date = 3; + } + + n = 0; + if (t.tm_min < 0) { + t.tm_hour = t.tm_min = 0; + } else if (t.tm_hour < 0) { + if (in_date != 3) + return (time_t)-1; + in_date = 0; + t.tm_hour = today->tm_hour; + n = 60*60; + } + + /* Note that mktime() might change a too-large tm_mday into the start of + * the following month which we need to undo in the following code! */ + old_mday = t.tm_mday; + if (t.tm_hour > 23 || t.tm_min > 59 + || t.tm_mon < 0 || t.tm_mon >= 12 + || t.tm_mday < 1 || t.tm_mday > 31 + || (val = mktime(&t)) == (time_t)-1) + return (time_t)-1; + + while (in_date && (val <= now || t.tm_mday < old_mday)) { + switch (in_date) { + case 3: + old_mday = ++t.tm_mday; + break; + case 2: + if (t.tm_mday < old_mday) + t.tm_mday = old_mday; /* The month already got bumped forward */ + else if (++t.tm_mon == 12) { + t.tm_mon = 0; + t.tm_year++; + } + break; + case 1: + if (t.tm_mday < old_mday) { + /* mon==1 mday==29 got bumped to mon==2 */ + if (t.tm_mon != 2 || old_mday != 29) + return (time_t)-1; + t.tm_mon = 1; + t.tm_mday = 29; + } + t.tm_year++; + break; + } + if ((val = mktime(&t)) == (time_t)-1) { + /* This code shouldn't be needed, as mktime() should auto-round to the next month. */ + if (in_date != 3 || t.tm_mday <= 28) + return (time_t)-1; + t.tm_mday = old_mday = 1; + in_date = 2; + } + } + if (n) { + while (val <= now) + val += n; + } + return val; +} +#endif static void create_refuse_error(int which) { @@ -1357,7 +1331,7 @@ char *alt_dest_opt(int type) case LINK_DEST: return "--link-dest"; default: - assert(0); + NOISY_DEATH("Unknown alt_dest_opt type"); } } @@ -1530,8 +1504,6 @@ int parse_arguments(int *argc_p, const char ***argv_p) if (daemon_filter_list.head) { int rej; char *cp = strdup(arg); - if (!cp) - out_of_memory("parse_arguments"); if (!*cp) rej = 1; else { @@ -1559,7 +1531,7 @@ int parse_arguments(int *argc_p, const char ***argv_p) preserve_links = 1; #endif preserve_perms = 1; - preserve_times = 1; + preserve_mtimes = 1; preserve_gid = 1; preserve_uid = 1; preserve_devices = 1; @@ -1644,6 +1616,13 @@ int parse_arguments(int *argc_p, const char ***argv_p) compress_choice = NULL; break; + case OPT_OLD_ARGS: + if (old_style_args <= 0) + old_style_args = 1; + else + old_style_args++; + break; + case 'M': arg = poptGetOptArg(pc); if (*arg != '-') { @@ -1655,8 +1634,6 @@ int parse_arguments(int *argc_p, const char ***argv_p) remote_option_alloc += 16; remote_options = realloc_array(remote_options, const char *, remote_option_alloc); - if (!remote_options) - out_of_memory("parse_arguments"); if (!remote_option_cnt) remote_options[0] = "ARG0"; } @@ -1685,40 +1662,39 @@ int parse_arguments(int *argc_p, const char ***argv_p) #endif break; + case OPT_BLOCK_SIZE: { + /* We may not know the real protocol_version at this point if this is the client + * option parsing, but we still want to check it so that the client can specify + * a --protocol=29 option with a larger block size. */ + int max_blength = protocol_version < 30 ? OLD_MAX_BLOCK_SIZE : MAX_BLOCK_SIZE; + ssize_t size; + arg = poptGetOptArg(pc); + if ((size = parse_size_arg(arg, 'b', "block-size", 0, max_blength, False)) < 0) + return 0; + block_size = (int32)size; + break; + } + case OPT_MAX_SIZE: - if ((max_size = parse_size_arg(&max_size_arg, 'b')) < 0) { - snprintf(err_buf, sizeof err_buf, - "--max-size value is invalid: %s\n", - max_size_arg); + if ((max_size = parse_size_arg(max_size_arg, 'b', "max-size", 0, -1, False)) < 0) return 0; - } + max_size_arg = strdup(do_big_num(max_size, 0, NULL)); break; case OPT_MIN_SIZE: - if ((min_size = parse_size_arg(&min_size_arg, 'b')) < 0) { - snprintf(err_buf, sizeof err_buf, - "--min-size value is invalid: %s\n", - min_size_arg); + if ((min_size = parse_size_arg(min_size_arg, 'b', "min-size", 0, -1, False)) < 0) return 0; - } + min_size_arg = strdup(do_big_num(min_size, 0, NULL)); break; - case OPT_BWLIMIT: - { - OFF_T limit = parse_size_arg(&bwlimit_arg, 'K'); - if (limit < 0) { - snprintf(err_buf, sizeof err_buf, - "--bwlimit value is invalid: %s\n", bwlimit_arg); - return 0; - } - bwlimit = (limit + 512) / 1024; - if (limit && !bwlimit) { - snprintf(err_buf, sizeof err_buf, - "--bwlimit value is too small: %s\n", bwlimit_arg); - return 0; - } - } + case OPT_BWLIMIT: { + ssize_t size = parse_size_arg(bwlimit_arg, 'K', "bwlimit", 512, -1, True); + if (size < 0) + return 0; + bwlimit_arg = strdup(do_big_num(size, 0, NULL)); + bwlimit = (size + 512) / 1024; break; + } case OPT_APPEND: if (am_server) @@ -1791,6 +1767,7 @@ int parse_arguments(int *argc_p, const char ***argv_p) } usermap = (char *)poptGetOptArg(pc); usermap_via_chown = False; + preserve_uid = 1; break; case OPT_GROUPMAP: @@ -1806,6 +1783,7 @@ int parse_arguments(int *argc_p, const char ***argv_p) } groupmap = (char *)poptGetOptArg(pc); groupmap_via_chown = False; + preserve_gid = 1; break; case OPT_CHOWN: { @@ -1829,6 +1807,7 @@ int parse_arguments(int *argc_p, const char ***argv_p) if (asprintf(&usermap, "*:%.*s", len, chown) < 0) out_of_memory("parse_arguments"); usermap_via_chown = True; + preserve_uid = 1; } if (arg && *arg) { if (groupmap) { @@ -1844,6 +1823,7 @@ int parse_arguments(int *argc_p, const char ***argv_p) if (asprintf(&groupmap, "*:%s", arg) < 0) out_of_memory("parse_arguments"); groupmap_via_chown = True; + preserve_gid = 1; } break; } @@ -1880,6 +1860,51 @@ int parse_arguments(int *argc_p, const char ***argv_p) return 0; #endif + case OPT_STOP_AFTER: { + long val; + arg = poptGetOptArg(pc); + stop_at_utime = time(NULL); + if ((val = atol(arg) * 60) <= 0 || LONG_MAX - val < stop_at_utime || (long)(time_t)val != val) { + snprintf(err_buf, sizeof err_buf, "invalid --stop-after value: %s\n", arg); + return 0; + } + stop_at_utime += val; + break; + } + +#ifdef HAVE_MKTIME + case OPT_STOP_AT: + arg = poptGetOptArg(pc); + if ((stop_at_utime = parse_time(arg)) == (time_t)-1) { + snprintf(err_buf, sizeof err_buf, "invalid --stop-at format: %s\n", arg); + return 0; + } + if (stop_at_utime <= time(NULL)) { + snprintf(err_buf, sizeof err_buf, "--stop-at time is not in the future: %s\n", arg); + return 0; + } + break; +#endif + + case OPT_STDERR: { + int len; + arg = poptGetOptArg(pc); + len = strlen(arg); + if (len && strncmp("errors", arg, len) == 0) + msgs2stderr = 2; + else if (len && strncmp("all", arg, len) == 0) + msgs2stderr = 1; + else if (len && strncmp("client", arg, len) == 0) + msgs2stderr = 0; + else { + snprintf(err_buf, sizeof err_buf, + "--stderr mode \"%s\" is not one of errors, all, or client\n", arg); + return 0; + } + saw_stderr_opt = 1; + break; + } + default: /* A large opt value means that set_refuse_options() * turned this option off. */ @@ -1895,11 +1920,41 @@ int parse_arguments(int *argc_p, const char ***argv_p) } } + if (msgs2stderr != 2) + saw_stderr_opt = 1; + if (version_opt_cnt) { print_rsync_version(FINFO); exit_cleanup(0); } + if (!max_alloc_arg) { + max_alloc_arg = getenv("RSYNC_MAX_ALLOC"); + if (max_alloc_arg && !*max_alloc_arg) + max_alloc_arg = NULL; + } + if (max_alloc_arg) { + ssize_t size = parse_size_arg(max_alloc_arg, 'B', "max-alloc", 1024*1024, -1, True); + if (size < 0) + return 0; + max_alloc = size; + } + + if (old_style_args < 0) { + if (!am_server && protect_args <= 0 && (arg = getenv("RSYNC_OLD_ARGS")) != NULL && *arg) { + protect_args = 0; + old_style_args = atoi(arg); + } else + old_style_args = 0; + } else if (old_style_args) { + if (protect_args > 0) { + snprintf(err_buf, sizeof err_buf, + "--protect-args conflicts with --old-args.\n"); + return 0; + } + protect_args = 0; + } + if (protect_args < 0) { if (am_server) protect_args = 0; @@ -1970,7 +2025,7 @@ int parse_arguments(int *argc_p, const char ***argv_p) setvbuf(stdout, (char *)NULL, mode, 0); } - if (msgs2stderr) { + if (msgs2stderr == 1) { /* Are all messages going to stderr? */ /* Make stderr line buffered for better sharing of the stream. */ fflush(stderr); /* Just in case... */ setvbuf(stderr, (char *)NULL, _IOLBF, 0); @@ -2042,19 +2097,6 @@ int parse_arguments(int *argc_p, const char ***argv_p) } #endif - if (block_size) { - /* We may not know the real protocol_version at this point if this is the client - * option parsing, but we still want to check it so that the client can specify - * a --protocol=29 option with a larger block size. */ - int32 max_blength = protocol_version < 30 ? OLD_MAX_BLOCK_SIZE : MAX_BLOCK_SIZE; - - if (block_size > max_blength) { - snprintf(err_buf, sizeof err_buf, - "--block-size=%lu is too large (max: %u)\n", block_size, max_blength); - return 0; - } - } - if (write_batch && read_batch) { snprintf(err_buf, sizeof err_buf, "--write-batch and --read-batch can not be used together\n"); @@ -2258,20 +2300,8 @@ int parse_arguments(int *argc_p, const char ***argv_p) parse_filter_str(&filter_list, backup_dir_buf, rule_template(0), 0); } - if (preserve_times) { - preserve_times = PRESERVE_FILE_TIMES; - if (!omit_dir_times) - preserve_times |= PRESERVE_DIR_TIMES; -#ifdef CAN_SET_SYMLINK_TIMES - if (!omit_link_times) - preserve_times |= PRESERVE_LINK_TIMES; -#endif - } - - if (make_backups && !backup_dir) { - omit_dir_times = 0; /* Implied, so avoid -O to sender. */ - preserve_times &= ~PRESERVE_DIR_TIMES; - } + if (make_backups && !backup_dir) + omit_dir_times = -1; /* Implied, so avoid -O to sender. */ if (stdout_format) { if (am_server && log_format_has(stdout_format, 'I')) @@ -2439,6 +2469,11 @@ int parse_arguments(int *argc_p, const char ***argv_p) } } + if (trust_sender || am_server || read_batch) + trust_sender_args = trust_sender_filter = 1; + else if (old_style_args || filesfrom_host != NULL) + trust_sender_args = 1; + am_starting_up = 0; return 1; @@ -2450,6 +2485,73 @@ int parse_arguments(int *argc_p, const char ***argv_p) } +static char SPLIT_ARG_WHEN_OLD[1]; + +/** + * Do backslash quoting of any weird chars in "arg", append the resulting + * string to the end of the "opt" (which gets a "=" appended if it is not + * an empty or NULL string), and return the (perhaps malloced) result. + * If opt is NULL, arg is considered a filename arg that allows wildcards. + * If it is "" or any other value, it is considered an option. + **/ +char *safe_arg(const char *opt, const char *arg) +{ +#define SHELL_CHARS "!#$&;|<>(){}\"' \t\\" +#define WILD_CHARS "*?[]" /* We don't allow remote brace expansion */ + BOOL is_filename_arg = !opt; + char *escapes = is_filename_arg ? SHELL_CHARS : WILD_CHARS SHELL_CHARS; + BOOL escape_leading_dash = is_filename_arg && *arg == '-'; + BOOL escape_leading_tilde = 0; + int len1 = opt && *opt ? strlen(opt) + 1 : 0; + int len2 = strlen(arg); + int extras = escape_leading_dash ? 2 : 0; + char *ret; + if (!protect_args && old_style_args < 2 && (!old_style_args || (!is_filename_arg && opt != SPLIT_ARG_WHEN_OLD))) { + const char *f; + if (!trust_sender_args && *arg == '~' + && ((relative_paths && !strstr(arg, "/./")) + || !strchr(arg, '/'))) { + extras++; + escape_leading_tilde = 1; + } + for (f = arg; *f; f++) { + if (strchr(escapes, *f)) + extras++; + } + } + if (!len1 && !extras) + return (char*)arg; + ret = new_array(char, len1 + len2 + extras + 1); + if (len1) { + memcpy(ret, opt, len1-1); + ret[len1-1] = '='; + } + if (escape_leading_dash) { + ret[len1++] = '.'; + ret[len1++] = '/'; + extras -= 2; + } + if (!extras) + memcpy(ret + len1, arg, len2); + else { + const char *f = arg; + char *t = ret + len1; + if (escape_leading_tilde) + *t++ = '\\'; + while (*f) { + if (*f == '\\') { + if (!is_filename_arg || !strchr(WILD_CHARS, f[1])) + *t++ = '\\'; + } else if (strchr(escapes, *f)) + *t++ = '\\'; + *t++ = *f++; + } + } + ret[len1+len2+extras] = '\0'; + return ret; +} + + /** * Construct a filtered list of options to pass through from the * client to the server. @@ -2469,13 +2571,6 @@ void server_options(char **args, int *argc_p) /* This should always remain first on the server's command-line. */ args[ac++] = "--server"; - if (daemon_over_rsh > 0) { - args[ac++] = "--daemon"; - *argc_p = ac; - /* if we're passing --daemon, we're done */ - return; - } - if (!am_sender) args[ac++] = "--sender"; @@ -2506,7 +2601,7 @@ void server_options(char **args, int *argc_p) argstr[x++] = 'K'; if (prune_empty_dirs) argstr[x++] = 'm'; - if (omit_dir_times) + if (omit_dir_times > 0) argstr[x++] = 'O'; if (omit_link_times) argstr[x++] = 'J'; @@ -2539,13 +2634,17 @@ void server_options(char **args, int *argc_p) argstr[x++] = 'g'; if (preserve_devices) /* ignore preserve_specials here */ argstr[x++] = 'D'; - if (preserve_times) + if (preserve_mtimes) argstr[x++] = 't'; if (preserve_atimes) { argstr[x++] = 'U'; if (preserve_atimes > 1) argstr[x++] = 'U'; } +#ifdef SUPPORT_CRTIMES + if (preserve_crtimes) + argstr[x++] = 'N'; +#endif if (preserve_perms) argstr[x++] = 'p'; else if (preserve_executability && am_sender) @@ -2583,46 +2682,8 @@ void server_options(char **args, int *argc_p) set_allow_inc_recurse(); - /* We don't really know the actual protocol_version at this point, - * but checking the pre-negotiated value allows the user to use a - * --protocol=29 override to avoid the use of this -eFLAGS opt. */ - if (protocol_version >= 30) { - /* Use "eFlags" alias so that cull_options doesn't think that these are no-arg option letters. */ -#define eFlags argstr - /* We make use of the -e option to let the server know about - * any pre-release protocol version && some behavior flags. */ - eFlags[x++] = 'e'; -#if SUBPROTOCOL_VERSION != 0 - if (protocol_version == PROTOCOL_VERSION) { - x += snprintf(argstr+x, sizeof argstr - x, - "%d.%d", - PROTOCOL_VERSION, SUBPROTOCOL_VERSION); - } else -#endif - eFlags[x++] = '.'; - if (allow_inc_recurse) - eFlags[x++] = 'i'; -#ifdef CAN_SET_SYMLINK_TIMES - eFlags[x++] = 'L'; /* symlink time-setting support */ -#endif -#ifdef ICONV_OPTION - eFlags[x++] = 's'; /* symlink iconv translation support */ -#endif - eFlags[x++] = 'f'; /* flist I/O-error safety support */ - eFlags[x++] = 'x'; /* xattr hardlink optimization not desired */ - eFlags[x++] = 'C'; /* support checksum seed order fix */ - eFlags[x++] = 'I'; /* support inplace_partial behavior */ - eFlags[x++] = 'v'; /* use varint for flist & compat flags; negotiate checksum */ - /* NOTE: Avoid using 'V' -- it was the high bit of a write_byte() that became write_varint(). */ -#undef eFlags - } - - if (x >= (int)sizeof argstr) { /* Not possible... */ - rprintf(FERROR, "argstr overflow in server_options().\n"); - exit_cleanup(RERR_MALLOC); - } - - argstr[x] = '\0'; + /* This '\0'-terminates argstr and makes sure it didn't overflow. */ + x += maybe_add_e_option(argstr + x, (int)sizeof argstr - x); if (x > 1) args[ac++] = argstr; @@ -2634,9 +2695,7 @@ void server_options(char **args, int *argc_p) set++; else set = iconv_opt; - if (asprintf(&arg, "--iconv=%s", set) < 0) - goto oom; - args[ac++] = arg; + args[ac++] = safe_arg("--iconv", set); } #endif @@ -2678,8 +2737,13 @@ void server_options(char **args, int *argc_p) args[ac++] = "--log-format=X"; } + if (msgs2stderr == 1) + args[ac++] = "--msgs2stderr"; + else if (msgs2stderr == 0) + args[ac++] = "--no-msgs2stderr"; + if (block_size) { - if (asprintf(&arg, "-B%lu", block_size) < 0) + if (asprintf(&arg, "-B%u", (int)block_size) < 0) goto oom; args[ac++] = arg; } @@ -2697,33 +2761,24 @@ void server_options(char **args, int *argc_p) } if (backup_dir) { + /* This split idiom allows for ~/path expansion via the shell. */ args[ac++] = "--backup-dir"; - args[ac++] = backup_dir; + args[ac++] = safe_arg("", backup_dir); } /* Only send --suffix if it specifies a non-default value. */ - if (strcmp(backup_suffix, backup_dir ? "" : BACKUP_SUFFIX) != 0) { - /* We use the following syntax to avoid weirdness with '~'. */ - if (asprintf(&arg, "--suffix=%s", backup_suffix) < 0) - goto oom; - args[ac++] = arg; - } + if (strcmp(backup_suffix, backup_dir ? "" : BACKUP_SUFFIX) != 0) + args[ac++] = safe_arg("--suffix", backup_suffix); - if (checksum_choice) { - if (asprintf(&arg, "--checksum-choice=%s", checksum_choice) < 0) - goto oom; - args[ac++] = arg; - } + if (checksum_choice) + args[ac++] = safe_arg("--checksum-choice", checksum_choice); if (do_compression == CPRES_ZLIBX) args[ac++] = "--new-compress"; else if (compress_choice && do_compression == CPRES_ZLIB) args[ac++] = "--old-compress"; - else if (compress_choice) { - if (asprintf(&arg, "--compress-choice=%s", compress_choice) < 0) - goto oom; - args[ac++] = arg; - } + else if (compress_choice) + args[ac++] = safe_arg("--compress-choice", compress_choice); if (am_sender) { if (max_delete > 0) { @@ -2732,14 +2787,10 @@ void server_options(char **args, int *argc_p) args[ac++] = arg; } else if (max_delete == 0) args[ac++] = "--max-delete=-1"; - if (min_size >= 0) { - args[ac++] = "--min-size"; - args[ac++] = min_size_arg; - } - if (max_size >= 0) { - args[ac++] = "--max-size"; - args[ac++] = max_size_arg; - } + if (min_size >= 0) + args[ac++] = safe_arg("--min-size", min_size_arg); + if (max_size >= 0) + args[ac++] = safe_arg("--max-size", max_size_arg); if (delete_before) args[ac++] = "--delete-before"; else if (delete_during == 2) @@ -2763,13 +2814,13 @@ void server_options(char **args, int *argc_p) if (do_stats) args[ac++] = "--stats"; } else { - if (skip_compress) { - if (asprintf(&arg, "--skip-compress=%s", skip_compress) < 0) - goto oom; - args[ac++] = arg; - } + if (skip_compress) + args[ac++] = safe_arg("--skip-compress", skip_compress); } + if (max_alloc_arg && max_alloc != DEFAULT_MAX_ALLOC) + args[ac++] = safe_arg("--max-alloc", max_alloc_arg); + /* --delete-missing-args needs the cooperation of both sides, but * the sender can handle --ignore-missing-args by itself. */ if (missing_args == 2) @@ -2793,7 +2844,7 @@ void server_options(char **args, int *argc_p) if (partial_dir && am_sender) { if (partial_dir != tmp_partialdir) { args[ac++] = "--partial-dir"; - args[ac++] = partial_dir; + args[ac++] = safe_arg("", partial_dir); } if (delay_updates) args[ac++] = "--delay-updates"; @@ -2816,17 +2867,11 @@ void server_options(char **args, int *argc_p) args[ac++] = "--use-qsort"; if (am_sender) { - if (usermap) { - if (asprintf(&arg, "--usermap=%s", usermap) < 0) - goto oom; - args[ac++] = arg; - } + if (usermap) + args[ac++] = safe_arg("--usermap", usermap); - if (groupmap) { - if (asprintf(&arg, "--groupmap=%s", groupmap) < 0) - goto oom; - args[ac++] = arg; - } + if (groupmap) + args[ac++] = safe_arg("--groupmap", groupmap); if (ignore_existing) args[ac++] = "--ignore-existing"; @@ -2837,9 +2882,12 @@ void server_options(char **args, int *argc_p) if (tmpdir) { args[ac++] = "--temp-dir"; - args[ac++] = tmpdir; + args[ac++] = safe_arg("", tmpdir); } + if (do_fsync) + args[ac++] = "--fsync"; + if (basis_dir[0]) { /* the server only needs this option if it is not the sender, * and it may be an older version that doesn't know this @@ -2847,7 +2895,7 @@ void server_options(char **args, int *argc_p) */ for (i = 0; i < basis_dir_cnt; i++) { args[ac++] = alt_dest_opt(0); - args[ac++] = basis_dir[i]; + args[ac++] = safe_arg("", basis_dir[i]); } } } @@ -2862,13 +2910,17 @@ void server_options(char **args, int *argc_p) if (append_mode > 1) args[ac++] = "--append"; args[ac++] = "--append"; - } else if (inplace) + } else if (inplace) { args[ac++] = "--inplace"; + /* Work around a bug in older rsync versions (on the remote side) for --inplace --sparse */ + if (sparse_files && !whole_file && am_sender) + args[ac++] = "--no-W"; + } if (files_from && (!am_sender || filesfrom_host)) { if (filesfrom_host) { args[ac++] = "--files-from"; - args[ac++] = files_from; + args[ac++] = safe_arg("", files_from); if (eol_nulls) args[ac++] = "--from0"; } else { @@ -2890,12 +2942,18 @@ void server_options(char **args, int *argc_p) else if (remove_source_files) args[ac++] = "--remove-sent-files"; + if (copy_devices && !am_sender) + args[ac++] = "--copy-devices"; + if (preallocate_files && am_sender) args[ac++] = "--preallocate"; if (open_noatime && preserve_atimes <= 1) args[ac++] = "--open-noatime"; + if (mkpath_dest_arg && am_sender) + args[ac++] = "--mkpath"; + if (ac > MAX_SERVER_ARGS) { /* Not possible... */ rprintf(FERROR, "argc overflow in server_options().\n"); exit_cleanup(RERR_MALLOC); @@ -2908,7 +2966,7 @@ void server_options(char **args, int *argc_p) exit_cleanup(RERR_SYNTAX); } for (j = 1; j <= remote_option_cnt; j++) - args[ac++] = (char*)remote_options[j]; + args[ac++] = safe_arg(SPLIT_ARG_WHEN_OLD, remote_options[j]); } *argc_p = ac; @@ -2918,6 +2976,52 @@ void server_options(char **args, int *argc_p) out_of_memory("server_options"); } +int maybe_add_e_option(char *buf, int buf_len) +{ + int x = 0; + + /* We don't really know the actual protocol_version at this point, + * but checking the pre-negotiated value allows the user to use a + * --protocol=29 override to avoid the use of this -eFLAGS opt. */ + if (protocol_version >= 30 && buf_len > 0) { + /* We make use of the -e option to let the server know about + * any pre-release protocol version && some behavior flags. */ + buf[x++] = 'e'; + +#if SUBPROTOCOL_VERSION != 0 + if (protocol_version == PROTOCOL_VERSION) + x += snprintf(buf + x, buf_len - x, "%d.%d", PROTOCOL_VERSION, SUBPROTOCOL_VERSION); + else +#endif + buf[x++] = '.'; + if (allow_inc_recurse) + buf[x++] = 'i'; +#ifdef CAN_SET_SYMLINK_TIMES + buf[x++] = 'L'; /* symlink time-setting support */ +#endif +#ifdef ICONV_OPTION + buf[x++] = 's'; /* symlink iconv translation support */ +#endif + buf[x++] = 'f'; /* flist I/O-error safety support */ + buf[x++] = 'x'; /* xattr hardlink optimization not desired */ + buf[x++] = 'C'; /* support checksum seed order fix */ + buf[x++] = 'I'; /* support inplace_partial behavior */ + buf[x++] = 'v'; /* use varint for flist & compat flags; negotiate checksum */ + buf[x++] = 'u'; /* include name of uid 0 & gid 0 in the id map */ + + /* NOTE: Avoid using 'V' -- it was represented with the high bit of a write_byte() that became a write_varint(). */ + } + + if (x >= buf_len) { /* Not possible... */ + rprintf(FERROR, "overflow in add_e_flags().\n"); + exit_cleanup(RERR_MALLOC); + } + + buf[x] = '\0'; + + return x; +} + /* 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