*
* Copyright (C) 1998-2001 Andrew Tridgell <tridge@samba.org>
* Copyright (C) 2000, 2001, 2002 Martin Pool <mbp@samba.org>
- * Copyright (C) 2002-2008 Wayne Davison
+ * Copyright (C) 2002-2011 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
*/
#include "rsync.h"
-#include "ifuncs.h"
+#include "itypes.h"
#include <popt.h>
#include "zlib/zlib.h"
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 daemon_filter_list;
+extern filter_rule_list filter_list;
+extern filter_rule_list daemon_filter_list;
int make_backups = 0;
int one_file_system = 0;
int protocol_version = PROTOCOL_VERSION;
int sparse_files = 0;
+int preallocate_files = 0;
int do_compression = 0;
int def_compress_level = Z_DEFAULT_COMPRESSION;
int am_root = 0; /* 0 = normal, 1 = root, 2 = --super, -1 = --fake-super */
int am_server = 0;
int am_sender = 0;
-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 msgs2stderr = 0;
int allow_8bit_chars = 0;
int force_delete = 0;
int io_timeout = 0;
-int allowed_lull = 0;
int prune_empty_dirs = 0;
int use_qsort = 0;
char *files_from = NULL;
int filesfrom_fd = -1;
char *filesfrom_host = NULL;
int eol_nulls = 0;
-int protect_args = 0;
-int human_readable = 0;
+int protect_args = -1;
+int human_readable = 1;
int recurse = 0;
int allow_inc_recurse = 1;
int xfer_dirs = -1;
int keep_partial = 0;
int safe_symlinks = 0;
int copy_unsafe_links = 0;
+int munge_symlinks = 0;
int size_only = 0;
int daemon_bwlimit = 0;
int bwlimit = 0;
int inplace = 0;
int delay_updates = 0;
long block_size = 0; /* "long" because popt can't set an int32. */
+char number_separator;
char *skip_compress = NULL;
+item_list dparam_list = EMPTY_ITEM_LIST;
/** Network address family. **/
int default_af_hint
char *backup_dir = NULL;
char backup_dir_buf[MAXPATHLEN];
char *sockopts = NULL;
+char *usermap = NULL;
+char *groupmap = NULL;
int rsync_port = 0;
int compare_dest = 0;
int copy_dest = 0;
static const char *debug_verbosity[] = {
/*0*/ NULL,
/*1*/ NULL,
- /*2*/ "bind,cmd,deltasum,connect,del,dup,filter,flist",
- /*3*/ "acl,backup,deltasum2,del2,exit,filter2,flist2,fuzzy,genr,own,recv,send,time",
- /*4*/ "cmd2,deltasum3,del3,exit2,flist3,iconv,own2,proto,time2",
- /*5*/ "chdir,deltasum4,flist4,fuzzy2,hlink",
+ /*2*/ "BIND,CMD,CONNECT,DEL,DELTASUM,DUP,FILTER,FLIST,ICONV",
+ /*3*/ "ACL,BACKUP,DELTASUM2,DEL2,EXIT,FILTER2,FLIST2,FUZZY,GENR,OWN,RECV,SEND,TIME",
+ /*4*/ "CMD2,DELTASUM3,DEL3,EXIT2,FLIST3,ICONV2,OWN2,PROTO,TIME2",
+ /*5*/ "CHDIR,DELTASUM4,FLIST4,FUZZY2,HASH,HLINK",
};
#define MAX_VERBOSITY ((int)(sizeof debug_verbosity / sizeof debug_verbosity[0]) - 1)
static const char *info_verbosity[1+MAX_VERBOSITY] = {
/*0*/ NULL,
- /*1*/ "copy,del,flist,misc,name,stats,symsafe",
- /*2*/ "backup,misc2,mount,name2,remove,skip",
+ /*1*/ "COPY,DEL,FLIST,MISC,NAME,STATS,SYMSAFE",
+ /*2*/ "BACKUP,MISC2,MOUNT,NAME2,REMOVE,SKIP",
};
#define MAX_OUT_LEVEL 4 /* The largest N allowed for any flagN word. */
DEBUG_WORD(DEL, W_REC, "Debug delete actions (levels 1-3)"),
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-2)"),
+ 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(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"),
- DEBUG_WORD(HLINK, W_SND|W_REC, "Debug hard-link actions"),
- DEBUG_WORD(ICONV, W_CLI|W_SRV, "Debug iconv (character conversion)"),
+ DEBUG_WORD(HASH, W_SND|W_REC, "Debug hashtable code"),
+ DEBUG_WORD(HLINK, W_SND|W_REC, "Debug hard-link actions (levels 1-3)"),
+ DEBUG_WORD(ICONV, W_CLI|W_SRV, "Debug iconv character conversions (levels 1-2)"),
+ DEBUG_WORD(IO, W_CLI|W_SRV, "Debug I/O routines (levels 1-4)"),
DEBUG_WORD(OWN, W_REC, "Debug ownership changes in users & groups (levels 1-2)"),
DEBUG_WORD(PROTO, W_CLI|W_SRV, "Debug protocol information"),
DEBUG_WORD(RECV, W_REC, "Debug receiver functions"),
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;
static int refused_partial, refused_progress, refused_delete_before;
static int refused_delete_during;
static int refused_inplace, refused_no_iconv;
-static char *max_size_arg, *min_size_arg;
+static BOOL usermap_via_chown, groupmap_via_chown;
+static char *bwlimit_arg, *max_size_arg, *min_size_arg;
static char tmp_partialdir[] = ".~tmp~";
/** Local address to bind. As a character string because it's
char const *got_socketpair = "no ";
char const *have_inplace = "no ";
char const *hardlinks = "no ";
+ char const *prealloc = "no ";
char const *symtimes = "no ";
char const *acls = "no ";
char const *xattrs = "no ";
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 = "";
#ifdef SUPPORT_HARD_LINKS
hardlinks = "";
#endif
+#ifdef SUPPORT_PREALLOCATION
+ prealloc = "";
+#endif
#ifdef SUPPORT_ACLS
acls = "";
#endif
#ifdef ICONV_OPTION
iconv = "";
#endif
-#if defined HAVE_LUTIMES && defined HAVE_UTIMES
+#ifdef CAN_SET_SYMLINK_TIMES
symtimes = "";
#endif
rprintf(f, "%s version %s protocol version %d%s\n",
RSYNC_NAME, RSYNC_VERSION, PROTOCOL_VERSION, subprotocol);
- rprintf(f, "Copyright (C) 1996-2008 by Andrew Tridgell, Wayne Davison, and others.\n");
+ rprintf(f, "Copyright (C) 1996-2011 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",
(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, %ssymtimes\n",
- have_inplace, acls, xattrs, iconv, symtimes);
+ rprintf(f, " %sappend, %sACLs, %sxattrs, %siconv, %ssymtimes, %sprealloc\n",
+ have_inplace, acls, xattrs, iconv, symtimes, prealloc);
#ifdef MAINTAINER_MODE
rprintf(f, "Panic Action: \"%s\"\n", get_panic_action());
rprintf(F," -L, --copy-links transform symlink into referent file/dir\n");
rprintf(F," --copy-unsafe-links only \"unsafe\" symlinks are transformed\n");
rprintf(F," --safe-links ignore symlinks that point outside the source tree\n");
+ rprintf(F," --munge-links munge symlinks to make them safer (but unusable)\n");
rprintf(F," -k, --copy-dirlinks transform symlink to a dir into referent dir\n");
rprintf(F," -K, --keep-dirlinks treat symlinked dir on receiver as dir\n");
rprintf(F," -H, --hard-links preserve hard links\n");
rprintf(F," -D same as --devices --specials\n");
rprintf(F," -t, --times preserve modification times\n");
rprintf(F," -O, --omit-dir-times omit directories from --times\n");
+ rprintf(F," -J, --omit-link-times omit symlinks from --times\n");
rprintf(F," --super receiver attempts super-user activities\n");
#ifdef SUPPORT_XATTRS
rprintf(F," --fake-super store/recover privileged attrs using xattrs\n");
#endif
rprintf(F," -S, --sparse handle sparse files efficiently\n");
+#ifdef SUPPORT_PREALLOCATION
+ rprintf(F," --preallocate allocate dest files before writing them\n");
+#else
+ rprintf(F," --preallocate pre-allocate dest files on remote receiver\n");
+#endif
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," --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");
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," --usermap=STRING custom username mapping\n");
+ rprintf(F," --groupmap=STRING custom groupname mapping\n");
+ rprintf(F," --chown=USER:GROUP simple username/groupname mapping\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," --log-file-format=FMT log updates using the specified FMT\n");
rprintf(F," --password-file=FILE read daemon-access password from FILE\n");
rprintf(F," --list-only list the files instead of copying them\n");
- rprintf(F," --bwlimit=KBPS limit I/O bandwidth; KBytes per second\n");
+ rprintf(F," --bwlimit=RATE limit socket I/O bandwidth\n");
rprintf(F," --write-batch=FILE write a batched update to FILE\n");
rprintf(F," --only-write-batch=FILE like --write-batch but w/o updating destination\n");
rprintf(F," --read-batch=FILE read a batched update from FILE\n");
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_SERVER, OPT_REFUSED_BASE = 9000};
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, 2, 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 },
{"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 },
+ {"omit-link-times", 'J', POPT_ARG_VAL, &omit_link_times, 1, 0, 0 },
+ {"no-omit-link-times",0, POPT_ARG_VAL, &omit_link_times, 0, 0, 0 },
+ {"no-J", 0, POPT_ARG_VAL, &omit_link_times, 0, 0, 0 },
{"modify-window", 0, POPT_ARG_INT, &modify_window, OPT_MODIFY_WINDOW, 0, 0 },
{"super", 0, POPT_ARG_VAL, &am_root, 2, 0, 0 },
{"no-super", 0, POPT_ARG_VAL, &am_root, 0, 0, 0 },
{"copy-links", 'L', POPT_ARG_NONE, ©_links, 0, 0, 0 },
{"copy-unsafe-links",0, POPT_ARG_NONE, ©_unsafe_links, 0, 0, 0 },
{"safe-links", 0, POPT_ARG_NONE, &safe_symlinks, 0, 0, 0 },
+ {"munge-links", 0, POPT_ARG_VAL, &munge_symlinks, 1, 0, 0 },
+ {"no-munge-links", 0, POPT_ARG_VAL, &munge_symlinks, 0, 0, 0 },
{"copy-dirlinks", 'k', POPT_ARG_NONE, ©_dirlinks, 0, 0, 0 },
{"keep-dirlinks", 'K', POPT_ARG_NONE, &keep_dirlinks, 0, 0, 0 },
{"hard-links", 'H', POPT_ARG_NONE, 0, 'H', 0, 0 },
{"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 },
+ {"no-one-file-system",0, POPT_ARG_VAL, &one_file_system, 0, 0, 0 },
+ {"no-x", 0, 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 },
{"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 },
+ {"preallocate", 0, POPT_ARG_NONE, &preallocate_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 },
{"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_VAL, &force_delete, 1, 0, 0 },
{"itemize-changes", 'i', POPT_ARG_NONE, 0, 'i', 0, 0 },
{"no-itemize-changes",0, POPT_ARG_VAL, &itemize_changes, 0, 0, 0 },
{"no-i", 0, POPT_ARG_VAL, &itemize_changes, 0, 0, 0 },
- {"bwlimit", 0, POPT_ARG_INT, &bwlimit, 0, 0, 0 },
+ {"bwlimit", 0, POPT_ARG_STRING, &bwlimit_arg, OPT_BWLIMIT, 0, 0 },
{"no-bwlimit", 0, POPT_ARG_VAL, &bwlimit, 0, 0, 0 },
{"backup", 'b', POPT_ARG_VAL, &make_backups, 1, 0, 0 },
{"no-backup", 0, POPT_ARG_VAL, &make_backups, 0, 0, 0 },
{"no-s", 0, POPT_ARG_VAL, &protect_args, 0, 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 },
+ {"groupmap", 0, POPT_ARG_STRING, 0, OPT_GROUPMAP, 0, 0 },
+ {"chown", 0, POPT_ARG_STRING, 0, OPT_CHOWN, 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 },
/* All the following options switch us into daemon-mode option-parsing. */
{"config", 0, POPT_ARG_STRING, 0, OPT_DAEMON, 0, 0 },
{"daemon", 0, POPT_ARG_NONE, 0, OPT_DAEMON, 0, 0 },
+ {"dparam", 0, POPT_ARG_STRING, 0, OPT_DAEMON, 0, 0 },
{"detach", 0, POPT_ARG_NONE, 0, OPT_DAEMON, 0, 0 },
{"no-detach", 0, POPT_ARG_NONE, 0, OPT_DAEMON, 0, 0 },
{0,0,0,0, 0, 0, 0}
rprintf(F,"\n");
rprintf(F,"Usage: rsync --daemon [OPTION]...\n");
rprintf(F," --address=ADDRESS bind to the specified address\n");
- rprintf(F," --bwlimit=KBPS limit I/O bandwidth; KBytes per second\n");
+ rprintf(F," --bwlimit=RATE limit socket I/O bandwidth\n");
rprintf(F," --config=FILE specify alternate rsyncd.conf file\n");
+ rprintf(F," -M, --dparam=OVERRIDE override global daemon config parameter\n");
rprintf(F," --no-detach do not detach from the parent\n");
rprintf(F," --port=PORT listen on alternate port number\n");
rprintf(F," --log-file=FILE override the \"log file\" setting\n");
{"bwlimit", 0, POPT_ARG_INT, &daemon_bwlimit, 0, 0, 0 },
{"config", 0, POPT_ARG_STRING, &config_file, 0, 0, 0 },
{"daemon", 0, POPT_ARG_NONE, &daemon_opt, 0, 0, 0 },
+ {"dparam", 'M', POPT_ARG_STRING, 0, 'M', 0, 0 },
{"ipv4", '4', POPT_ARG_VAL, &default_af_hint, AF_INET, 0, 0 },
{"ipv6", '6', POPT_ARG_VAL, &default_af_hint, AF_INET6, 0, 0 },
{"detach", 0, POPT_ARG_VAL, &no_detach, 0, 0, 0 },
size += atoi(arg), make_compatible = 1, arg += 2;
if (*arg)
return -1;
- if (size > 0 && make_compatible) {
+ 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;
}
#ifdef ICONV_OPTION
- if (!am_daemon && !protect_args && (arg = getenv("RSYNC_ICONV")) != NULL && *arg)
+ if (!am_daemon && protect_args <= 0 && (arg = getenv("RSYNC_ICONV")) != NULL && *arg)
iconv_opt = strdup(arg);
#endif
#ifdef ICONV_OPTION
iconv_opt = NULL;
#endif
+ protect_args = 0;
poptFreeContext(pc);
pc = poptGetContext(RSYNC_NAME, argc, argv,
long_daemon_options, 0);
while ((opt = poptGetNextOpt(pc)) != -1) {
+ char **cpp;
switch (opt) {
case 'h':
daemon_usage(FINFO);
exit_cleanup(0);
+ case 'M':
+ arg = poptGetOptArg(pc);
+ if (!strchr(arg, '=')) {
+ rprintf(FERROR,
+ "--dparam value is missing an '=': %s\n",
+ arg);
+ goto daemon_error;
+ }
+ cpp = EXPAND_ITEM_LIST(&dparam_list, char *, 4);
+ *cpp = strdup(arg);
+ break;
+
case 'v':
verbose++;
break;
}
}
+ if (dparam_list.count && !set_dparams(1))
+ exit_cleanup(RERR_SYNTAX);
+
if (tmpdir && strlen(tmpdir) >= MAXPATHLEN - 10) {
snprintf(err_buf, sizeof err_buf,
"the --temp-dir path is WAY too long.\n");
break;
case OPT_FILTER:
- parse_rule(&filter_list, poptGetOptArg(pc), 0, 0);
+ parse_filter_str(&filter_list, poptGetOptArg(pc),
+ rule_template(0), 0);
break;
case OPT_EXCLUDE:
- parse_rule(&filter_list, poptGetOptArg(pc),
- 0, XFLG_OLD_PREFIXES);
+ parse_filter_str(&filter_list, poptGetOptArg(pc),
+ rule_template(0), XFLG_OLD_PREFIXES);
break;
case OPT_INCLUDE:
- parse_rule(&filter_list, poptGetOptArg(pc),
- MATCHFLG_INCLUDE, XFLG_OLD_PREFIXES);
+ parse_filter_str(&filter_list, poptGetOptArg(pc),
+ rule_template(FILTRULE_INCLUDE), XFLG_OLD_PREFIXES);
break;
case OPT_EXCLUDE_FROM:
arg = sanitize_path(NULL, arg, NULL, 0, SP_DEFAULT);
if (daemon_filter_list.head) {
int rej;
- char *dir, *cp = strdup(arg);
+ char *cp = strdup(arg);
if (!cp)
out_of_memory("parse_arguments");
if (!*cp)
- goto options_rejected;
- dir = cp + (*cp == '/' ? module_dirlen : 0);
- clean_fname(dir, CFN_COLLAPSE_DOT_DOT_DIRS);
- rej = check_filter(&daemon_filter_list, FLOG, dir, 0) < 0;
+ rej = 1;
+ else {
+ char *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;
}
parse_filter_file(&filter_list, arg,
- opt == OPT_INCLUDE_FROM ? MATCHFLG_INCLUDE : 0,
+ rule_template(opt == OPT_INCLUDE_FROM ? FILTRULE_INCLUDE : 0),
XFLG_FATAL_ERRORS | XFLG_OLD_PREFIXES);
break;
preserve_links = 1;
#endif
preserve_perms = 1;
- preserve_times = 2;
+ preserve_times = 1;
preserve_gid = 1;
preserve_uid = 1;
preserve_devices = 1;
case 'F':
switch (++F_option_cnt) {
case 1:
- parse_rule(&filter_list,": /.rsync-filter",0,0);
+ parse_filter_str(&filter_list,": /.rsync-filter",rule_template(0),0);
break;
case 2:
- parse_rule(&filter_list,"- .rsync-filter",0,0);
+ parse_filter_str(&filter_list,"- .rsync-filter",rule_template(0),0);
break;
}
break;
}
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;
+ }
+ }
+ break;
+
case OPT_APPEND:
if (am_server)
append_mode++;
parse_output_words(debug_words, debug_levels, arg, USER_PRIORITY);
break;
+ case OPT_USERMAP:
+ if (usermap) {
+ if (usermap_via_chown) {
+ snprintf(err_buf, sizeof err_buf,
+ "--usermap conflicts with prior --chown.\n");
+ return 0;
+ }
+ snprintf(err_buf, sizeof err_buf,
+ "You can only specify --usermap once.\n");
+ return 0;
+ }
+ usermap = (char *)poptGetOptArg(pc);
+ usermap_via_chown = False;
+ break;
+
+ case OPT_GROUPMAP:
+ if (groupmap) {
+ if (groupmap_via_chown) {
+ snprintf(err_buf, sizeof err_buf,
+ "--groupmap conflicts with prior --chown.\n");
+ return 0;
+ }
+ snprintf(err_buf, sizeof err_buf,
+ "You can only specify --groupmap once.\n");
+ return 0;
+ }
+ groupmap = (char *)poptGetOptArg(pc);
+ groupmap_via_chown = False;
+ break;
+
+ case OPT_CHOWN: {
+ const char *chown = poptGetOptArg(pc);
+ int len;
+ if ((arg = strchr(chown, ':')) != NULL)
+ len = arg++ - chown;
+ else
+ len = strlen(chown);
+ if (len) {
+ if (usermap) {
+ if (!usermap_via_chown) {
+ snprintf(err_buf, sizeof err_buf,
+ "--chown conflicts with prior --usermap.\n");
+ return 0;
+ }
+ snprintf(err_buf, sizeof err_buf,
+ "You can only specify a user-affecting --chown once.\n");
+ return 0;
+ }
+ if (asprintf(&usermap, "*:%.*s", len, chown) < 0)
+ out_of_memory("parse_arguments");
+ usermap_via_chown = True;
+ }
+ if (arg && *arg) {
+ if (groupmap) {
+ if (!groupmap_via_chown) {
+ snprintf(err_buf, sizeof err_buf,
+ "--chown conflicts with prior --groupmap.\n");
+ return 0;
+ }
+ snprintf(err_buf, sizeof err_buf,
+ "You can only specify a group-affecting --chown once.\n");
+ return 0;
+ }
+ if (asprintf(&groupmap, "*:%s", arg) < 0)
+ out_of_memory("parse_arguments");
+ groupmap_via_chown = True;
+ }
+ break;
+ }
+
case OPT_HELP:
usage(FINFO);
exit_cleanup(0);
}
}
- if (human_readable && argc == 2 && !am_server) {
+ if (protect_args < 0) {
+ if (am_server)
+ protect_args = 0;
+ else if ((arg = getenv("RSYNC_PROTECT_ARGS")) != NULL && *arg)
+ protect_args = atoi(arg) ? 1 : 0;
+ else {
+#ifdef RSYNC_USE_PROTECTED_ARGS
+ protect_args = 1;
+#else
+ protect_args = 0;
+#endif
+ }
+ }
+
+ if (human_readable > 1 && argc == 2 && !am_server) {
/* Allow the old meaning of 'h' (--help) on its own. */
usage(FINFO);
exit_cleanup(0);
set_output_verbosity(verbose, DEFAULT_PRIORITY);
- if (do_stats && !am_server) {
+ if (do_stats) {
parse_output_words(info_words, info_levels,
verbose > 1 ? "stats3" : "stats2", DEFAULT_PRIORITY);
}
+ if (human_readable) {
+ char buf[32];
+ snprintf(buf, sizeof buf, "%f", 3.14);
+ if (strchr(buf, '.') != NULL)
+ number_separator = ',';
+ else
+ number_separator = '.';
+ }
+
#ifdef ICONV_OPTION
if (iconv_opt && protect_args != 2) {
if (!am_server && strcmp(iconv_opt, "-") == 0)
"--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",
list_only |= 1;
if (xfer_dirs >= 4) {
- parse_rule(&filter_list, "- /*/*", 0, 0);
+ parse_filter_str(&filter_list, "- /*/*", rule_template(0), 0);
recurse = xfer_dirs = 1;
} else if (recurse)
xfer_dirs = 1;
}
if (!xfer_dirs && delete_mode) {
snprintf(err_buf, sizeof err_buf,
- "--delete does not work without -r or -d.\n");
+ "--delete does not work without --recursive (-r) or --dirs (-d).\n");
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;
}
need_messages_from_generator = 1;
}
+ if (munge_symlinks && !am_daemon) {
+ STRUCT_STAT st;
+ char prefix[SYMLINK_PREFIX_LEN]; /* NOT +1 ! */
+ strlcpy(prefix, SYMLINK_PREFIX, sizeof prefix); /* trim the trailing slash */
+ if (do_stat(prefix, &st) == 0 && S_ISDIR(st.st_mode)) {
+ rprintf(FERROR, "Symlink munging is unsafe when a %s directory exists.\n",
+ prefix);
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+ }
+
if (sanitize_paths) {
int i;
for (i = argc; i-- > 0; )
backup_dir = sanitize_path(NULL, backup_dir, NULL, 0, SP_DEFAULT);
}
if (daemon_filter_list.head && !am_sender) {
- struct filter_list_struct *elp = &daemon_filter_list;
+ filter_rule_list *elp = &daemon_filter_list;
if (tmpdir) {
char *dir;
if (!*tmpdir)
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) {
+ size_t len;
+ while (*backup_dir == '.' && backup_dir[1] == '/')
+ backup_dir += 2;
+ if (*backup_dir == '.' && backup_dir[1] == '\0')
+ backup_dir++;
+ len = strlcpy(backup_dir_buf, backup_dir, sizeof backup_dir_buf);
+ if (len > sizeof backup_dir_buf - 128) {
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_len = (int)len;
+ if (!backup_dir_len) {
+ backup_dir_len = -1;
+ backup_dir = NULL;
+ } else if (backup_dir_buf[backup_dir_len - 1] != '/') {
backup_dir_buf[backup_dir_len++] = '/';
backup_dir_buf[backup_dir_len] = '\0';
}
- if (INFO_GTE(BACKUP, 1) && !am_sender)
- rprintf(FINFO, "backup_dir is %s\n", backup_dir_buf);
+ backup_dir_remainder = sizeof backup_dir_buf - backup_dir_len;
+ }
+ if (backup_dir) {
+ /* No need for a suffix or a protect rule. */
} else if (!backup_suffix_len && (!am_server || !am_sender)) {
snprintf(err_buf, sizeof err_buf,
- "--suffix cannot be a null string without --backup-dir\n");
+ "--suffix cannot be empty %s\n", backup_dir_len < 0
+ ? "when --backup-dir is the same as the dest dir"
+ : "without a --backup-dir");
return 0;
} else if (make_backups && delete_mode && !delete_excluded && !am_server) {
snprintf(backup_dir_buf, sizeof backup_dir_buf,
"P *%s", backup_suffix);
- parse_rule(&filter_list, backup_dir_buf, 0, 0);
+ 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. */
- if (preserve_times > 1)
- preserve_times = 1;
- } else if (omit_dir_times) {
- if (preserve_times > 1)
- preserve_times = 1;
+ preserve_times &= ~PRESERVE_DIR_TIMES;
}
if (stdout_format) {
if (files_from) {
char *h, *p;
int q;
- if (argc > 2 || (!am_daemon && argc == 1)) {
+ if (argc > 2 || (!am_daemon && !am_server && argc == 1)) {
usage(FERROR);
exit_cleanup(RERR_SYNTAX);
}
argstr[x++] = 'm';
if (omit_dir_times)
argstr[x++] = 'O';
+ if (omit_link_times)
+ argstr[x++] = 'J';
} else {
if (copy_links)
argstr[x++] = 'L';
argstr[x++] = '.';
if (allow_inc_recurse)
argstr[x++] = 'i';
-#if defined HAVE_LUTIMES && defined HAVE_UTIMES
+#ifdef CAN_SET_SYMLINK_TIMES
argstr[x++] = 'L';
+#endif
+#ifdef ICONV_OPTION
+ argstr[x++] = 's';
#endif
}
argstr[x] = '\0';
- args[ac++] = argstr;
+ if (x > 1)
+ args[ac++] = argstr;
#ifdef ICONV_OPTION
if (iconv_opt) {
args[ac++] = "--super";
if (size_only)
args[ac++] = "--size-only";
+ if (do_stats)
+ args[ac++] = "--stats";
} else {
if (skip_compress) {
if (asprintf(&arg, "--skip-compress=%s", skip_compress) < 0)
}
}
+ /* --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;
args[ac++] = "--use-qsort";
if (am_sender) {
+ if (usermap) {
+ if (asprintf(&arg, "--usermap=%s", usermap) < 0)
+ goto oom;
+ args[ac++] = arg;
+ }
+
+ if (groupmap) {
+ if (asprintf(&arg, "--groupmap=%s", groupmap) < 0)
+ goto oom;
+ args[ac++] = arg;
+ }
+
if (ignore_existing)
args[ac++] = "--ignore-existing";
else if (remove_source_files)
args[ac++] = "--remove-sent-files";
+ if (preallocate_files && am_sender)
+ args[ac++] = "--preallocate";
+
if (ac > MAX_SERVER_ARGS) { /* Not possible... */
rprintf(FERROR, "argc overflow in server_options().\n");
exit_cleanup(RERR_MALLOC);
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
* "[::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;
}