*
* 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"
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;
/*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,hlink",
+ /*5*/ "chdir,deltasum4,flist4,fuzzy2,hash,hlink",
};
#define MAX_VERBOSITY ((int)(sizeof debug_verbosity / sizeof debug_verbosity[0]) - 1)
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 conversions (levels 1-2)"),
DEBUG_WORD(OWN, W_REC, "Debug ownership changes in users & groups (levels 1-2)"),
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," --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-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 },
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);
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);
}
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)
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";
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;
}