This patch implements an idiom where two -o options cause rsync
to try to set the owner information even if we're not running as
UID 0.  Similarly, two -g options cause rsync to try to set all
groups, even if they weren't returned by getgroups().  E.g.:

    rsync -avoogg host:/from/ /to/

--- orig/compat.c	2005-03-09 18:53:55
+++ compat.c	2005-03-11 11:25:46
@@ -28,11 +28,14 @@
 int remote_protocol = 0;
 
 extern int verbose;
+extern int am_root;
 extern int am_server;
 extern int am_sender;
 extern int inplace;
 extern int fuzzy_basis;
 extern int read_batch;
+extern int preserve_uid;
+extern int preserve_gid;
 extern int checksum_seed;
 extern int basis_dir_cnt;
 extern int protocol_version;
@@ -106,4 +109,11 @@ void setup_protocol(int f_out,int f_in)
 	} else {
 		checksum_seed = read_int(f_in);
 	}
+
+	if (am_root) {
+		if (preserve_uid)
+			preserve_uid = 2;
+		if (preserve_gid)
+			preserve_gid = 2;
+	}
 }
--- orig/options.c	2005-03-31 00:24:21
+++ options.c	2004-09-09 01:59:08
@@ -405,8 +405,8 @@ static struct poptOption long_options[] 
   {"no-whole-file",    0,  POPT_ARG_VAL,    &whole_file, 0, 0, 0 },
   {"copy-unsafe-links",0,  POPT_ARG_NONE,   &copy_unsafe_links, 0, 0, 0 },
   {"perms",           'p', POPT_ARG_NONE,   &preserve_perms, 0, 0, 0 },
-  {"owner",           'o', POPT_ARG_NONE,   &preserve_uid, 0, 0, 0 },
-  {"group",           'g', POPT_ARG_NONE,   &preserve_gid, 0, 0, 0 },
+  {"owner",           'o', POPT_ARG_NONE,   0,               'o', 0, 0 },
+  {"group",           'g', POPT_ARG_NONE,   0,               'g', 0, 0 },
   {"devices",         'D', POPT_ARG_NONE,   &preserve_devices, 0, 0, 0 },
   {"times",           't', POPT_ARG_NONE,   &preserve_times, 0, 0, 0 },
   {"omit-dir-times",  'O', POPT_ARG_VAL,    &omit_dir_times, 2, 0, 0 },
@@ -748,6 +748,14 @@ int parse_arguments(int *argc, const cha
 			usage(FINFO);
 			exit_cleanup(0);
 
+		case 'o':
+			preserve_uid++;
+			break;
+
+		case 'g':
+			preserve_gid++;
+			break;
+
 		case 'v':
 			verbose++;
 			break;
@@ -950,8 +958,8 @@ int parse_arguments(int *argc, const cha
 #endif
 		preserve_perms = 1;
 		preserve_times = 1;
-		preserve_gid = 1;
-		preserve_uid = 1;
+		preserve_uid |= 1;
+		preserve_gid |= 1;
 		preserve_devices = 1;
 	}
 
@@ -1264,10 +1272,16 @@ void server_options(char **args,int *arg
 
 	if (preserve_hard_links)
 		argstr[x++] = 'H';
-	if (preserve_uid)
+	if (preserve_uid) {
 		argstr[x++] = 'o';
-	if (preserve_gid)
+		if (preserve_uid > 1)
+			argstr[x++] = 'o';
+	}
+	if (preserve_gid) {
 		argstr[x++] = 'g';
+		if (preserve_gid > 1)
+			argstr[x++] = 'g';
+	}
 	if (preserve_devices)
 		argstr[x++] = 'D';
 	if (preserve_times)
--- orig/rsync.c	2005-03-16 02:19:30
+++ rsync.c	2005-02-01 10:46:04
@@ -27,7 +27,6 @@ extern int dry_run;
 extern int daemon_log_format_has_i;
 extern int preserve_times;
 extern int omit_dir_times;
-extern int am_root;
 extern int am_server;
 extern int am_sender;
 extern int am_generator;
@@ -81,7 +80,7 @@ int set_perms(char *fname,struct file_st
 		updated = 1;
 	}
 
-	change_uid = am_root && preserve_uid && st->st_uid != file->uid;
+	change_uid = preserve_uid > 1 && st->st_uid != file->uid;
 	change_gid = preserve_gid && file->gid != GID_NONE
 		&& st->st_gid != file->gid;
 #if !defined HAVE_LCHOWN && !defined CHOWN_MODIFIES_SYMLINK
--- orig/uidlist.c	2005-02-14 00:53:44
+++ uidlist.c	2004-09-09 01:59:08
@@ -35,7 +35,6 @@ extern int verbose;
 extern int preserve_uid;
 extern int preserve_gid;
 extern int numeric_ids;
-extern int am_root;
 
 struct idlist {
 	struct idlist *next;
@@ -177,7 +176,7 @@ static struct idlist *recv_add_gid(int i
 	int id2 = name ? map_gid(id, name) : id;
 	struct idlist *node;
 
-	if (!am_root && !is_in_group(id2))
+	if (preserve_gid < 2 && !is_in_group(id2))
 		id2 = GID_NONE;
 	node = add_to_list(&gidlist, id, name, id2);
 
@@ -339,11 +338,11 @@ void recv_uid_list(int f, struct file_li
 
 	/* now convert the uid/gid of all files in the list to the mapped
 	 * uid/gid */
-	if (am_root && preserve_uid && !numeric_ids) {
+	if (preserve_uid > 1 && !numeric_ids) {
 		for (i = 0; i < flist->count; i++)
 			flist->files[i]->uid = match_uid(flist->files[i]->uid);
 	}
-	if (preserve_gid && (!am_root || !numeric_ids)) {
+	if (preserve_gid && (preserve_gid < 2 || !numeric_ids)) {
 		for (i = 0; i < flist->count; i++)
 			flist->files[i]->gid = match_gid(flist->files[i]->gid);
 	}
