*
* 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
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",
{"dirs", 'd', POPT_ARG_VAL, &xfer_dirs, 2, 0, 0 },
{"no-dirs", 0, POPT_ARG_VAL, &xfer_dirs, 0, 0, 0 },
{"no-d", 0, POPT_ARG_VAL, &xfer_dirs, 0, 0, 0 },
+ {"old-dirs", 0, POPT_ARG_VAL, &xfer_dirs, 4, 0, 0 },
+ {"old-d", 0, POPT_ARG_VAL, &xfer_dirs, 4, 0, 0 },
{"perms", 'p', POPT_ARG_VAL, &preserve_perms, 1, 0, 0 },
{"no-perms", 0, POPT_ARG_VAL, &preserve_perms, 0, 0, 0 },
{"no-p", 0, POPT_ARG_VAL, &preserve_perms, 0, 0, 0 },
case OPT_INCLUDE_FROM:
arg = poptGetOptArg(pc);
if (sanitize_paths)
- arg = sanitize_path(NULL, arg, NULL, 0);
+ arg = sanitize_path(NULL, arg, NULL, 0, SP_DEFAULT);
if (daemon_filter_list.head) {
int rej;
char *dir, *cp = strdup(arg);
goto options_rejected;
dir = cp + (*cp == '/' ? module_dirlen : 0);
clean_fname(dir, CFN_COLLAPSE_DOT_DOT_DIRS);
- rej = check_filter(&daemon_filter_list, dir, 0) < 0;
+ rej = check_filter(&daemon_filter_list, FLOG, dir, 0) < 0;
free(cp);
if (rej)
goto options_rejected;
}
}
- if (human_readable && argc == 2) {
+ if (human_readable && argc == 2 && !am_server) {
/* Allow the old meaning of 'h' (--help) on its own. */
usage(FINFO);
exit_cleanup(0);
if (protect_args == 1 && am_server)
return 1;
+ *argv_p = argv = poptGetArgs(pc);
+ *argc_p = argc = count_args(argv);
+
#ifndef SUPPORT_LINKS
if (preserve_links && !am_sender) {
snprintf(err_buf, sizeof err_buf,
xfer_dirs = 1;
}
- if (xfer_dirs < 1)
- xfer_dirs = recurse || list_only;
+ if (argc < 2 && !read_batch && !am_server)
+ list_only |= 1;
+
+ if (xfer_dirs >= 4) {
+ parse_rule(&filter_list, "- /*/*", 0, 0);
+ recurse = xfer_dirs = 1;
+ } else if (recurse)
+ xfer_dirs = 1;
+ else if (xfer_dirs < 0)
+ xfer_dirs = list_only ? 1 : 0;
if (relative_paths < 0)
relative_paths = files_from? 1 : 0;
need_messages_from_generator = 1;
}
- *argv_p = argv = poptGetArgs(pc);
- *argc_p = argc = count_args(argv);
-
if (sanitize_paths) {
int i;
for (i = argc; i-- > 0; )
- argv[i] = sanitize_path(NULL, argv[i], "", 0);
+ argv[i] = sanitize_path(NULL, argv[i], "", 0, SP_KEEP_DOT_DIRS);
if (tmpdir)
- tmpdir = sanitize_path(NULL, tmpdir, NULL, 0);
+ tmpdir = sanitize_path(NULL, tmpdir, NULL, 0, SP_DEFAULT);
if (backup_dir)
- backup_dir = sanitize_path(NULL, backup_dir, NULL, 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;
goto options_rejected;
dir = tmpdir + (*tmpdir == '/' ? module_dirlen : 0);
clean_fname(dir, CFN_COLLAPSE_DOT_DOT_DIRS);
- if (check_filter(elp, dir, 1) < 0)
+ if (check_filter(elp, FLOG, dir, 1) < 0)
goto options_rejected;
}
if (backup_dir) {
goto options_rejected;
dir = backup_dir + (*backup_dir == '/' ? module_dirlen : 0);
clean_fname(dir, CFN_COLLAPSE_DOT_DOT_DIRS);
- if (check_filter(elp, dir, 1) < 0)
+ if (check_filter(elp, FLOG, dir, 1) < 0)
goto options_rejected;
}
}
}
} else {
if (sanitize_paths)
- files_from = sanitize_path(NULL, files_from, NULL, 0);
+ files_from = sanitize_path(NULL, files_from, NULL, 0, SP_DEFAULT);
if (daemon_filter_list.head) {
char *dir;
if (!*files_from)
goto options_rejected;
dir = files_from + (*files_from == '/' ? module_dirlen : 0);
clean_fname(dir, CFN_COLLAPSE_DOT_DOT_DIRS);
- if (check_filter(&daemon_filter_list, dir, 0) < 0)
+ if (check_filter(&daemon_filter_list, FLOG, dir, 0) < 0)
goto options_rejected;
}
filesfrom_fd = open(files_from, O_RDONLY|O_BINARY);
argstr[x++] = 'n';
if (preserve_links)
argstr[x++] = 'l';
- if ((list_only && !recurse) || xfer_dirs > 1
- || (xfer_dirs && !recurse && delete_mode && am_sender))
+ if ((xfer_dirs >= 2 && xfer_dirs < 4)
+ || (xfer_dirs && !recurse && (list_only || (delete_mode && am_sender))))
argstr[x++] = 'd';
if (am_sender) {
if (keep_dirlinks)
if (do_compression)
argstr[x++] = 'z';
- /* We make use of the -e option to let the server know about any
- * pre-release protocol version && some behavior flags. */
- argstr[x++] = 'e';
+ set_allow_inc_recurse();
+
+ /* Checking the pre-negotiated value allows --protocol=29 override. */
+ if (protocol_version >= 30) {
+ /* We make use of the -e option to let the server know about
+ * any pre-release protocol version && some behavior flags. */
+ argstr[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
+ if (protocol_version == PROTOCOL_VERSION) {
+ x += snprintf(argstr+x, sizeof argstr - x,
+ "%d.%d",
+ PROTOCOL_VERSION, SUBPROTOCOL_VERSION);
+ } else
#endif
- argstr[x++] = '.';
- set_allow_inc_recurse();
- if (allow_inc_recurse)
- argstr[x++] = 'i';
+ argstr[x++] = '.';
+ if (allow_inc_recurse)
+ argstr[x++] = 'i';
#if defined HAVE_LUTIMES && defined HAVE_UTIMES
- argstr[x++] = 'L';
+ argstr[x++] = 'L';
#endif
+#ifdef ICONV_OPTION
+ argstr[x++] = 's';
+#endif
+ }
+
+ if (x >= (int)sizeof argstr) { /* Not possible... */
+ rprintf(FERROR, "argstr overflow in server_options().\n");
+ exit_cleanup(RERR_MALLOC);
+ }
+
argstr[x] = '\0';
- args[ac++] = argstr;
+ if (x > 1)
+ args[ac++] = argstr;
#ifdef ICONV_OPTION
if (iconv_opt) {
* and it may be an older version that doesn't know this
* option, so don't send it if client is the sender.
*/
- int i;
for (i = 0; i < basis_dir_cnt; i++) {
args[ac++] = dest_option;
args[ac++] = basis_dir[i];
else if (remove_source_files)
args[ac++] = "--remove-sent-files";
+ if (ac > MAX_SERVER_ARGS) { /* Not possible... */
+ rprintf(FERROR, "argc overflow in server_options().\n");
+ exit_cleanup(RERR_MALLOC);
+ }
+
*argc_p = ac;
return;
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;
}