* Copyright (C) 2002-2007 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ * with this program; if not, visit the http://fsf.org website.
*/
#include "rsync.h"
int sparse_files = 0;
int do_compression = 0;
int def_compress_level = Z_DEFAULT_COMPRESSION;
-int am_root = 0;
+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 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 ignore_existing = 0;
int ignore_non_existing = 0;
int need_messages_from_generator = 0;
-int max_delete = -1;
+int max_delete = INT_MIN;
OFF_T max_size = 0;
OFF_T min_size = 0;
int ignore_errors = 0;
int inplace = 0;
int delay_updates = 0;
long block_size = 0; /* "long" because popt can't set an int32. */
+char *skip_compress = NULL;
/** Network address family. **/
#ifdef INET6
#define MAX_BATCH_NAME_LEN 256 /* Must be less than MAXPATHLEN-13 */
char *batch_name = NULL;
+#ifdef ICONV_OPTION
+int need_unsorted_flist = 0;
+char *iconv_opt = ICONV_OPTION;
+#endif
+
struct chmod_mode_struct *chmod_modes = NULL;
static int daemon_opt; /* sets am_daemon after option error-reporting */
static void print_rsync_version(enum logcode f)
{
+ char *subprotocol = "";
char const *got_socketpair = "no ";
char const *have_inplace = "no ";
char const *hardlinks = "no ";
char const *acls = "no ";
char const *xattrs = "no ";
char const *links = "no ";
+ char const *iconv = "no ";
char const *ipv6 = "no ";
STRUCT_STAT *dumstat;
+#if SUBPROTOCOL_VERSION != 0
+ asprintf(&subprotocol, ".PR%d", SUBPROTOCOL_VERSION);
+#endif
#ifdef HAVE_SOCKETPAIR
got_socketpair = "";
#endif
-
#ifdef HAVE_FTRUNCATE
have_inplace = "";
#endif
-
#ifdef SUPPORT_HARD_LINKS
hardlinks = "";
#endif
-
#ifdef SUPPORT_ACLS
acls = "";
#endif
#ifdef SUPPORT_LINKS
links = "";
#endif
-
#ifdef INET6
ipv6 = "";
#endif
+#ifdef ICONV_OPTION
+ iconv = "";
+#endif
- rprintf(f, "%s version %s protocol version %d\n",
- RSYNC_NAME, RSYNC_VERSION, PROTOCOL_VERSION);
+ rprintf(f, "%s version %s protocol version %d%s\n",
+ RSYNC_NAME, RSYNC_VERSION, PROTOCOL_VERSION, subprotocol);
rprintf(f, "Copyright (C) 1996-2007 by Andrew Tridgell, Wayne Davison, and others.\n");
- rprintf(f, "<http://rsync.samba.org/>\n");
- rprintf(f, "Capabilities: %d-bit files, %d-bit system inums, %d-bit internal inums,\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 (OFF_T) * 8),
(int)(sizeof dumstat->st_ino * 8), /* Don't check ino_t! */
+ (int)(sizeof (time_t) * 8),
(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\n",
- have_inplace, acls, xattrs);
+ rprintf(f, " %sappend, %sACLs, %sxattrs, %siconv\n",
+ have_inplace, acls, xattrs, iconv);
#ifdef MAINTAINER_MODE
rprintf(f, "Panic Action: \"%s\"\n", get_panic_action());
rprintf(F," --devices preserve device files (super-user only)\n");
rprintf(F," --specials preserve special files\n");
rprintf(F," -D same as --devices --specials\n");
- rprintf(F," -t, --times preserve times\n");
- rprintf(F," -O, --omit-dir-times omit directories when preserving times\n");
+ rprintf(F," -t, --times preserve modification times\n");
+ rprintf(F," -O, --omit-dir-times omit directories 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");
rprintf(F," -n, --dry-run show what would have been transferred\n");
rprintf(F," -W, --whole-file copy files whole (without rsync algorithm)\n");
rprintf(F," --link-dest=DIR hardlink to files in DIR when unchanged\n");
rprintf(F," -z, --compress compress file data during the transfer\n");
rprintf(F," --compress-level=NUM explicitly set compression level\n");
+ rprintf(F," --skip-compress=LIST skip compressing files with a suffix in LIST\n");
rprintf(F," -C, --cvs-exclude auto-ignore files the same way CVS does\n");
rprintf(F," -f, --filter=RULE add a file-filtering RULE\n");
rprintf(F," -F same as --filter='dir-merge /.rsync-filter'\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");
rprintf(F," --protocol=NUM force an older protocol version to be used\n");
+#ifdef ICONV_OPTION
+ rprintf(F," --iconv=CONVERT_SPEC request charset conversion of filesnames\n");
+#endif
#ifdef INET6
rprintf(F," -4, --ipv4 prefer IPv4\n");
rprintf(F," -6, --ipv6 prefer IPv6\n");
{"no-motd", 0, POPT_ARG_VAL, &output_motd, 0, 0, 0 },
{"stats", 0, POPT_ARG_NONE, &do_stats, 0, 0, 0 },
{"human-readable", 'h', POPT_ARG_NONE, 0, 'h', 0, 0},
+ {"no-human-readable",0, POPT_ARG_VAL, &human_readable, 0, 0, 0},
+ {"no-h", 0, POPT_ARG_VAL, &human_readable, 0, 0, 0},
{"dry-run", 'n', POPT_ARG_NONE, &dry_run, 0, 0, 0 },
{"archive", 'a', POPT_ARG_NONE, 0, 'a', 0, 0 },
{"recursive", 'r', POPT_ARG_VAL, &recurse, 2, 0, 0 },
{"no-recursive", 0, POPT_ARG_VAL, &recurse, 0, 0, 0 },
- {"ir", 0, POPT_ARG_VAL, &allow_inc_recurse, 1, 0, 0 },
+ {"no-r", 0, POPT_ARG_VAL, &recurse, 0, 0, 0 },
{"inc-recursive", 0, POPT_ARG_VAL, &allow_inc_recurse, 1, 0, 0 },
- {"no-ir", 0, POPT_ARG_VAL, &allow_inc_recurse, 0, 0, 0 },
{"no-inc-recursive", 0, POPT_ARG_VAL, &allow_inc_recurse, 0, 0, 0 },
- {"no-r", 0, POPT_ARG_VAL, &recurse, 0, 0, 0 },
+ {"ir", 0, POPT_ARG_VAL, &allow_inc_recurse, 1, 0, 0 },
+ {"no-ir", 0, POPT_ARG_VAL, &allow_inc_recurse, 0, 0, 0 },
{"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 },
{"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 },
+ {"fake-super", 0, POPT_ARG_VAL, &am_root, -1, 0, 0 },
{"owner", 'o', POPT_ARG_VAL, &preserve_uid, 1, 0, 0 },
{"no-owner", 0, POPT_ARG_VAL, &preserve_uid, 0, 0, 0 },
{"no-o", 0, POPT_ARG_VAL, &preserve_uid, 0, 0, 0 },
{"whole-file", 'W', POPT_ARG_VAL, &whole_file, 1, 0, 0 },
{"no-whole-file", 0, POPT_ARG_VAL, &whole_file, 0, 0, 0 },
{"no-W", 0, POPT_ARG_VAL, &whole_file, 0, 0, 0 },
- {"checksum", 'c', POPT_ARG_NONE, &always_checksum, 0, 0, 0 },
+ {"checksum", 'c', POPT_ARG_VAL, &always_checksum, 1, 0, 0 },
+ {"no-checksum", 0, POPT_ARG_VAL, &always_checksum, 0, 0, 0 },
+ {"no-c", 0, POPT_ARG_VAL, &always_checksum, 0, 0, 0 },
{"block-size", 'B', POPT_ARG_LONG, &block_size, 0, 0, 0 },
{"compare-dest", 0, POPT_ARG_STRING, 0, OPT_COMPARE_DEST, 0, 0 },
{"copy-dest", 0, POPT_ARG_STRING, 0, OPT_COPY_DEST, 0, 0 },
{"link-dest", 0, POPT_ARG_STRING, 0, OPT_LINK_DEST, 0, 0 },
{"fuzzy", 'y', POPT_ARG_NONE, &fuzzy_basis, 0, 0, 0 },
{"compress", 'z', POPT_ARG_NONE, 0, 'z', 0, 0 },
+ {"no-compress", 0, POPT_ARG_VAL, &do_compression, 0, 0, 0 },
+ {"skip-compress", 0, POPT_ARG_STRING, &skip_compress, 0, 0, 0 },
+ {"no-z", 0, POPT_ARG_VAL, &do_compression, 0, 0, 0 },
{"compress-level", 0, POPT_ARG_INT, &def_compress_level, 'z', 0, 0 },
{0, 'P', POPT_ARG_NONE, 0, 'P', 0, 0 },
{"progress", 0, POPT_ARG_VAL, &do_progress, 1, 0, 0 },
{"partial", 0, POPT_ARG_VAL, &keep_partial, 1, 0, 0 },
{"no-partial", 0, POPT_ARG_VAL, &keep_partial, 0, 0, 0 },
{"partial-dir", 0, POPT_ARG_STRING, &partial_dir, 0, 0, 0 },
- {"delay-updates", 0, POPT_ARG_NONE, &delay_updates, 0, 0, 0 },
+ {"delay-updates", 0, POPT_ARG_VAL, &delay_updates, 1, 0, 0 },
+ {"no-delay-updates", 0, POPT_ARG_VAL, &delay_updates, 0, 0, 0 },
{"prune-empty-dirs",'m', POPT_ARG_NONE, &prune_empty_dirs, 0, 0, 0 },
{"log-file", 0, POPT_ARG_STRING, &logfile_name, 0, 0, 0 },
{"log-file-format", 0, POPT_ARG_STRING, &logfile_format, 0, 0, 0 },
{"out-format", 0, POPT_ARG_STRING, &stdout_format, 0, 0, 0 },
{"log-format", 0, POPT_ARG_STRING, &stdout_format, 0, 0, 0 }, /* DEPRECATED */
{"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 },
- {"backup", 'b', POPT_ARG_NONE, &make_backups, 0, 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 },
{"backup-dir", 0, POPT_ARG_STRING, &backup_dir, 0, 0, 0 },
{"suffix", 0, POPT_ARG_STRING, &backup_suffix, 0, 0, 0 },
{"list-only", 0, POPT_ARG_VAL, &list_only, 2, 0, 0 },
{"write-batch", 0, POPT_ARG_STRING, &batch_name, OPT_WRITE_BATCH, 0, 0 },
{"only-write-batch", 0, POPT_ARG_STRING, &batch_name, OPT_ONLY_WRITE_BATCH, 0, 0 },
{"files-from", 0, POPT_ARG_STRING, &files_from, 0, 0, 0 },
- {"from0", '0', POPT_ARG_NONE, &eol_nulls, 0, 0, 0},
- {"numeric-ids", 0, POPT_ARG_NONE, &numeric_ids, 0, 0, 0 },
+ {"from0", '0', POPT_ARG_VAL, &eol_nulls, 1, 0, 0},
+ {"no-from0", 0, POPT_ARG_VAL, &eol_nulls, 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 },
{"timeout", 0, POPT_ARG_INT, &io_timeout, 0, 0, 0 },
+ {"no-timeout", 0, POPT_ARG_VAL, &io_timeout, 0, 0, 0 },
{"rsh", 'e', POPT_ARG_STRING, &shell_cmd, 0, 0, 0 },
{"rsync-path", 0, POPT_ARG_STRING, &rsync_path, 0, 0, 0 },
{"temp-dir", 'T', POPT_ARG_STRING, &tmpdir, 0, 0, 0 },
+#ifdef ICONV_OPTION
+ {"iconv", 0, POPT_ARG_STRING, &iconv_opt, 0, 0, 0 },
+#endif
#ifdef INET6
{"ipv4", '4', POPT_ARG_VAL, &default_af_hint, AF_INET, 0, 0 },
{"ipv6", '6', POPT_ARG_VAL, &default_af_hint, AF_INET6, 0, 0 },
#endif
{"8-bit-output", '8', POPT_ARG_NONE, &allow_8bit_chars, 0, 0, 0 },
+ {"qsort", 0, POPT_ARG_NONE, &use_qsort, 0, 0, 0 },
{"address", 0, POPT_ARG_STRING, &bind_address, 0, 0, 0 },
{"port", 0, POPT_ARG_INT, &rsync_port, 0, 0, 0 },
{"sockopts", 0, POPT_ARG_STRING, &sockopts, 0, 0, 0 },
{"ipv6", '6', POPT_ARG_VAL, &default_af_hint, AF_INET6, 0, 0 },
#endif
{"detach", 0, POPT_ARG_VAL, &no_detach, 0, 0, 0 },
+ {"no-detach", 0, POPT_ARG_VAL, &no_detach, 1, 0, 0 },
{"log-file", 0, POPT_ARG_STRING, &logfile_name, 0, 0, 0 },
{"log-file-format", 0, POPT_ARG_STRING, &logfile_format, 0, 0, 0 },
- {"no-detach", 0, POPT_ARG_VAL, &no_detach, 1, 0, 0 },
{"port", 0, POPT_ARG_INT, &rsync_port, 0, 0, 0 },
{"sockopts", 0, POPT_ARG_STRING, &sockopts, 0, 0, 0 },
{"protocol", 0, POPT_ARG_INT, &protocol_version, 0, 0, 0 },
if (am_daemon)
set_refuse_options("log-file*");
+#ifdef ICONV_OPTION
+ if (!am_daemon && (arg = getenv("RSYNC_ICONV")) != NULL && *arg)
+ iconv_opt = strdup(arg);
+#endif
+
/* TODO: Call poptReadDefaultConfig; handle errors. */
/* The context leaks in case of an error, but if there's a
long_options, 0);
am_server = 1;
}
+#ifdef ICONV_OPTION
+ iconv_opt = NULL;
+#endif
break;
case OPT_SENDER:
sizeof err_buf);
return 0;
}
+#ifdef ICONV_OPTION
+ iconv_opt = NULL;
+#endif
poptFreeContext(pc);
pc = poptGetContext(RSYNC_NAME, *argc, *argv,
long_daemon_options, 0);
case 'X':
#ifdef SUPPORT_XATTRS
- preserve_xattrs = 1;
+ preserve_xattrs++;
preserve_perms = 1;
break;
#else
exit_cleanup(0);
}
+#ifdef ICONV_OPTION
+ if (iconv_opt) {
+ if (!am_server && strcmp(iconv_opt, "-") == 0)
+ iconv_opt = NULL;
+ else
+ need_unsorted_flist = 1;
+ }
+#endif
+
#ifndef SUPPORT_LINKS
if (preserve_links && !am_sender) {
snprintf(err_buf, sizeof err_buf,
}
#endif
+#ifndef SUPPORT_XATTRS
+ if (am_root < 0) {
+ snprintf(err_buf, sizeof err_buf,
+ "--fake-super requires an rsync with extended attributes enabled\n");
+ return 0;
+ }
+#endif
+
if (write_batch && read_batch) {
snprintf(err_buf, sizeof err_buf,
"--write-batch and --read-batch can not be used together\n");
return 0;
}
+ if (max_delete < 0 && max_delete != INT_MIN) {
+ /* Negative numbers are treated as "no deletions". */
+ max_delete = 0;
+ }
+
if (compare_dest + copy_dest + link_dest > 1) {
snprintf(err_buf, sizeof err_buf,
"You may not mix --compare-dest, --copy-dest, and --link-dest.\n");
argstr[x++] = 'A';
#endif
#ifdef SUPPORT_XATTRS
- if (preserve_xattrs)
+ if (preserve_xattrs) {
argstr[x++] = 'X';
+ if (preserve_xattrs > 1)
+ argstr[x++] = 'X';
+ }
#endif
if (recurse)
argstr[x++] = 'r';
if (list_only == 1 && !recurse)
argstr[x++] = 'r';
+#if SUBPROTOCOL_VERSION != 0
+ /* If we're speaking a pre-release version of a protocol, we tell
+ * the server about this by (ab)using the -e option. */
+ if (protocol_version == PROTOCOL_VERSION) {
+ x += snprintf(argstr+x, sizeof argstr - x,
+ "e%d.%d", PROTOCOL_VERSION, SUBPROTOCOL_VERSION);
+ }
+#endif
+
argstr[x] = '\0';
if (x != 1)
args[ac++] = "--log-format=X";
}
- if (block_size) {
- if (asprintf(&arg, "-B%lu", block_size) < 0)
+#ifdef ICONV_OPTION
+ if (iconv_opt) {
+ char *set = strchr(iconv_opt, ',');
+ if (set)
+ set++;
+ else
+ set = iconv_opt;
+ if (asprintf(&arg, "--iconv=%s", set) < 0)
goto oom;
args[ac++] = arg;
}
+#endif
- if (max_delete >= 0 && am_sender) {
- if (asprintf(&arg, "--max-delete=%d", max_delete) < 0)
+ if (block_size) {
+ if (asprintf(&arg, "-B%lu", block_size) < 0)
goto oom;
args[ac++] = arg;
}
- if (min_size && am_sender) {
- args[ac++] = "--min-size";
- args[ac++] = min_size_arg;
- }
-
- if (max_size && am_sender) {
- args[ac++] = "--max-size";
- args[ac++] = max_size_arg;
- }
-
if (io_timeout) {
if (asprintf(&arg, "--timeout=%d", io_timeout) < 0)
goto oom;
}
if (am_sender) {
+ if (max_delete > 0) {
+ if (asprintf(&arg, "--max-delete=%d", max_delete) < 0)
+ goto oom;
+ args[ac++] = arg;
+ } else if (max_delete == 0)
+ args[ac++] = "--max_delete=-1";
+ if (min_size) {
+ args[ac++] = "--min-size";
+ args[ac++] = min_size_arg;
+ }
+ if (max_size) {
+ args[ac++] = "--max-size";
+ args[ac++] = max_size_arg;
+ }
if (delete_before)
args[ac++] = "--delete-before";
else if (delete_during == 2)
args[ac++] = "--super";
if (size_only)
args[ac++] = "--size-only";
+ } else {
+ if (skip_compress) {
+ if (asprintf(&arg, "--skip-compress=%s", skip_compress) < 0)
+ goto oom;
+ args[ac++] = arg;
+ }
}
if (modify_window_set) {
if (numeric_ids)
args[ac++] = "--numeric-ids";
- if (ignore_existing && am_sender)
- args[ac++] = "--ignore-existing";
+ if (!allow_inc_recurse)
+ args[ac++] = "--no-ir";
- /* Backward compatibility: send --existing, not --ignore-non-existing. */
- if (ignore_non_existing && am_sender)
- args[ac++] = "--existing";
+ if (am_sender) {
+ if (ignore_existing)
+ args[ac++] = "--ignore-existing";
- if (append_mode)
- args[ac++] = "--append";
- else if (inplace)
- args[ac++] = "--inplace";
+ /* Backward compatibility: send --existing, not --ignore-non-existing. */
+ if (ignore_non_existing)
+ args[ac++] = "--existing";
- if (tmpdir && am_sender) {
- args[ac++] = "--temp-dir";
- args[ac++] = tmpdir;
- }
+ if (tmpdir) {
+ args[ac++] = "--temp-dir";
+ args[ac++] = tmpdir;
+ }
- if (basis_dir[0] && am_sender) {
- /* the server only needs this option if it is not the sender,
- * 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];
+ if (basis_dir[0]) {
+ /* the server only needs this option if it is not the sender,
+ * 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];
+ }
}
}
+ if (append_mode)
+ args[ac++] = "--append";
+ else if (inplace)
+ args[ac++] = "--inplace";
+
if (files_from && (!am_sender || filesfrom_host)) {
if (filesfrom_host) {
args[ac++] = "--files-from";
if (p[1] == ':')
*port_ptr = atoi(p+2);
} else {
- if ((p = strchr(s, ':')) != NULL) {
+ if ((p = strchr(s, ':')) != NULL && p < s + hostlen) {
hostlen = p - s;
*port_ptr = atoi(p+1);
}