*
* 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-2009 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
*/
#include "rsync.h"
-#include "ifuncs.h"
+#include "itypes.h"
#include <popt.h>
#include "zlib/zlib.h"
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 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;
char *filesfrom_host = NULL;
int eol_nulls = 0;
int protect_args = 0;
-int human_readable = 0;
+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,chksum,connect,del,dup,filter,flist",
- /*3*/ "acl,backup,chksum2,del2,exit,filter2,flist2,fuzzy,genr,own,recv,send,time",
- /*4*/ "cmd2,chksum3,del3,exit2,flist3,iconv,own2,proto,time2",
- /*5*/ "chdir,chksum4,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",
+ /*2*/ "backup,mount,name2,remove,skip",
};
#define MAX_OUT_LEVEL 4 /* The largest N allowed for any flagN word. */
INFO_WORD(COPY, W_REC, "Mention files copied locally on the receiving side"),
INFO_WORD(DEL, W_REC, "Mention deletions on the receiving side"),
INFO_WORD(FLIST, W_CLI, "Mention file-list receiving/sending (levels 1-2)"),
- INFO_WORD(MISC, W_SND|W_REC, "Mention miscellaneous information (levels 1-2)"),
+ INFO_WORD(MISC, W_SND|W_REC, "Mention miscellaneous information"),
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(PROGRESS, W_CLI, "Mention 1) per-file progress or 2) total transfer progress"),
DEBUG_WORD(BIND, W_CLI, "Debug socket bind actions"),
DEBUG_WORD(CHDIR, W_CLI|W_SRV, "Debug when the current directory changes"),
DEBUG_WORD(CONNECT, W_CLI, "Debug connection events"),
- DEBUG_WORD(CHKSUM, W_SND|W_REC, "Debug delta-transfer checksumming (levels 1-4)"),
DEBUG_WORD(CMD, W_CLI, "Debug commands+options that are issued (levels 1-2)"),
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(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(HASH, W_SND|W_REC, "Debug hashtable code"),
DEBUG_WORD(HLINK, W_SND|W_REC, "Debug hard-link actions"),
- DEBUG_WORD(ICONV, W_CLI|W_SRV, "Debug iconv (character conversion)"),
+ DEBUG_WORD(ICONV, W_CLI|W_SRV, "Debug iconv character conversions (levels 1-2)"),
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 refused_partial, refused_progress, refused_delete_before;
static int refused_delete_during;
static int refused_inplace, refused_no_iconv;
+static BOOL usermap_via_chown, groupmap_via_chown;
static char *max_size_arg, *min_size_arg;
static char tmp_partialdir[] = ".~tmp~";
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 = "";
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-2009 by Andrew Tridgell, Wayne Davison, and others.\n");
rprintf(f, "Web site: http://rsync.samba.org/\n");
rprintf(f, "Capabilities:\n");
rprintf(f, " %d-bit files, %d-bit inums, %d-bit timestamps, %d-bit long ints,\n",
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," --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");
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_SERVER, OPT_REFUSED_BASE = 9000};
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 },
{"quiet", 'q', POPT_ARG_NONE, 0, 'q', 0, 0 },
{"motd", 0, POPT_ARG_VAL, &output_motd, 1, 0, 0 },
{"no-motd", 0, POPT_ARG_VAL, &output_motd, 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 },
{"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," --address=ADDRESS bind to the specified address\n");
rprintf(F," --bwlimit=KBPS limit I/O bandwidth; KBytes per second\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 },
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");
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 (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)
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; )
argstr[x++] = 'i';
#if defined HAVE_LUTIMES && defined HAVE_UTIMES
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) {
if (protect_args && !local_server) /* unprotected args stop here */
args[ac++] = NULL;
+ if (do_stats)
+ args[ac++] = "--stats";
+
if (list_only > 1)
args[ac++] = "--list-only";
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";