X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/9411292489496984c8d5d9a446bf071afac3866d..58cf354711f9e7eb2ec2a54b5f623abb860dafcf:/options.c diff --git a/options.c b/options.c index c561f47e..0c64eaa1 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-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 @@ -167,6 +167,8 @@ char *rsync_path = RSYNC_PATH; 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; @@ -296,6 +298,7 @@ static int refused_delete, refused_archive_part, refused_compress; 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~"; @@ -598,7 +601,7 @@ static void print_rsync_version(enum logcode f) 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", @@ -725,6 +728,9 @@ void usage(enum logcode F) 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"); @@ -789,6 +795,7 @@ enum {OPT_VERSION = 1000, OPT_DAEMON, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM, 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[] = { @@ -969,6 +976,9 @@ 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 }, @@ -1625,6 +1635,76 @@ int parse_arguments(int *argc_p, const char ***argv_p) 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); @@ -1680,7 +1760,7 @@ int parse_arguments(int *argc_p, const char ***argv_p) 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); } @@ -2288,6 +2368,9 @@ void server_options(char **args, int *argc_p) 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"; @@ -2435,6 +2518,18 @@ 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 (groupmap) { + if (asprintf(&arg, "--groupmap=%s", groupmap) < 0) + goto oom; + args[ac++] = arg; + } + if (ignore_existing) args[ac++] = "--ignore-existing"; @@ -2523,6 +2618,61 @@ void server_options(char **args, int *argc_p) 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 = str; + char *host_start = str; + int hostlen = 0, userlen = 0; + char *ret; + + for ( ; ; s++) { + if (!*s) { + /* It is only OK if we run out of string with rsync:// */ + if (port_ptr) + break; + return NULL; + } + 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++ != '/') + 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 @@ -2532,68 +2682,28 @@ void server_options(char **args, int *argc_p) * "[::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 = ""; - } - 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); - } + *host_ptr = parse_hostspec(s + strlen(URL_PREFIX), &path, port_ptr); + if (*host_ptr) { + if (!*port_ptr) + *port_ptr = RSYNC_PORT; + return path; } - 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; }