Updated patches to work with the current trunk.
[rsync/rsync-patches.git] / usermap.diff
diff --git a/usermap.diff b/usermap.diff
deleted file mode 100644 (file)
index a17bac8..0000000
+++ /dev/null
@@ -1,550 +0,0 @@
-This adds a --usermap and a --groupmap option.  See the man page for
-more details.
-
-To use this patch, run these commands for a successful build:
-
-    patch -p1 <patches/usermap.diff
-    ./configure                         (optional if already run)
-    make
-
-diff --git a/flist.c b/flist.c
---- a/flist.c
-+++ b/flist.c
-@@ -71,6 +71,7 @@ extern int sender_symlink_iconv;
- extern int unsort_ndx;
- extern struct stats stats;
- extern char *filesfrom_host;
-+extern char *usermap, *groupmap;
- extern char curr_dir[MAXPATHLEN];
-@@ -767,7 +768,7 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
-                       uid = (uid_t)read_varint(f);
-                       if (xflags & XMIT_USER_NAME_FOLLOWS)
-                               uid = recv_user_name(f, uid);
--                      else if (inc_recurse && am_root && !numeric_ids)
-+                      else if (inc_recurse && am_root && (!numeric_ids || usermap))
-                               uid = match_uid(uid);
-               }
-       }
-@@ -779,7 +780,7 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
-                       gid_flags = 0;
-                       if (xflags & XMIT_GROUP_NAME_FOLLOWS)
-                               gid = recv_group_name(f, gid, &gid_flags);
--                      else if (inc_recurse && (!am_root || !numeric_ids))
-+                      else if (inc_recurse && (!am_root || !numeric_ids || groupmap))
-                               gid = match_gid(gid, &gid_flags);
-               }
-       }
-@@ -2248,8 +2249,13 @@ struct file_list *recv_file_list(int f)
-       int64 start_read;
-       int save_verbose = verbose;
--      if (!first_flist)
-+      if (!first_flist) {
-               rprintf(FLOG, "receiving file list\n");
-+              if (usermap)
-+                      parse_name_map(usermap, 1);
-+              if (groupmap)
-+                      parse_name_map(groupmap, 0);
-+      }
-       if (show_filelist_p())
-               start_filelist_progress("receiving file list");
-       else if (inc_recurse && verbose && !am_server && !first_flist)
-diff --git a/options.c b/options.c
---- a/options.c
-+++ b/options.c
-@@ -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;
-@@ -385,6 +387,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");
-@@ -447,7 +452,7 @@ enum {OPT_VERSION = 1000, OPT_DAEMON, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM,
-       OPT_FILTER, OPT_COMPARE_DEST, OPT_COPY_DEST, OPT_LINK_DEST, OPT_HELP,
-       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_NO_D, OPT_APPEND, OPT_NO_ICONV, OPT_USERMAP, OPT_GROUPMAP, OPT_CHOWN,
-       OPT_SERVER, OPT_REFUSED_BASE = 9000};
- static struct poptOption long_options[] = {
-@@ -623,6 +628,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 },
-@@ -1229,6 +1237,43 @@ int parse_arguments(int *argc_p, const char ***argv_p)
-                       }
-                       break;
-+              case OPT_USERMAP:
-+                      if (usermap) {
-+                              snprintf(err_buf, sizeof err_buf,
-+                                  "You can only specify --usermap once.\n");
-+                              return 0;
-+                      }
-+                      usermap = (char *)poptGetOptArg(pc);
-+                      break;
-+
-+              case OPT_GROUPMAP:
-+                      if (groupmap) {
-+                              snprintf(err_buf, sizeof err_buf,
-+                                  "You can only specify --groupmap once.\n");
-+                              return 0;
-+                      }
-+                      groupmap = (char *)poptGetOptArg(pc);
-+                      break;
-+
-+              case OPT_CHOWN:
-+                      if (usermap || groupmap) {
-+                              snprintf(err_buf, sizeof err_buf,
-+                                  "You can only specify --chown once.\n");
-+                              return 0;
-+                      } else {
-+                              const char *chown = poptGetOptArg(pc);
-+                              int len;
-+                              if ((arg = strchr(chown, ':')) != NULL) {
-+                                      if (arg[1] && asprintf(&groupmap, "*:%s", arg+1) < 0)
-+                                              out_of_memory("parse_arguments");
-+                                      len = arg - chown;
-+                              } else
-+                                      len = strlen(chown);
-+                              if (len && asprintf(&usermap, "*:%.*s", len, chown) < 0)
-+                                      out_of_memory("parse_arguments");
-+                      }
-+                      break;
-+
-               case OPT_HELP:
-                       usage(FINFO);
-                       exit_cleanup(0);
-@@ -2007,6 +2052,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";
-diff --git a/rsync.yo b/rsync.yo
---- a/rsync.yo
-+++ b/rsync.yo
-@@ -382,6 +382,9 @@ to the detailed description below for a complete description.  verb(
-      --delay-updates         put all updated files into place at end
-  -m, --prune-empty-dirs      prune empty directory chains from file-list
-      --numeric-ids           don't map uid/gid values by user/group name
-+     --usermap=STRING        custom username mapping
-+     --groupmap=STRING       custom groupname mapping
-+     --chown=USER:GROUP      simple username/groupname mapping
-      --timeout=SECONDS       set I/O timeout in seconds
-      --contimeout=SECONDS    set daemon connection timeout in seconds
-  -I, --ignore-times          don't skip files that match size and time
-@@ -1624,6 +1627,57 @@ from the source system is used instead.  See also the comments on the
- the chroot setting affects rsync's ability to look up the names of the
- users and groups and what you can do about it.
-+dit(bf(--usermap=STRING, --groupmap=STRING)) These options allow you to
-+specify users and groups that should be mapped to other values by the
-+receiving side.  The bf(STRING) is one or more bf(FROM):bf(TO) pairs of
-+values separated by commas.  Any matching bf(FROM) value from the sender is
-+replaced with a bf(TO) value from the receiver.  You may specify usernames
-+or user IDs for the bf(FROM) and bf(TO) values, and the bf(FROM) value may
-+also be a wild-card string, which will be matched against the sender's
-+names (wild-cards do NOT match against ID numbers, though see below for
-+why a '*' matches everything).  You may instead specify a range of ID
-+numbers via an inclusive range: LOW-HIGH.  For example:
-+
-+verb(  --usermap=0-99:nobody,wayne:admin,*:normal --groupmap=usr:1,1:usr)
-+
-+The first match in the list is the one that is used.  You should specify
-+all your user mappings using a single bf(--usermap) option, and/or all
-+your group mappings using a single bf(--groupmap) option.
-+
-+Note that the sender's name for the 0 user and group are not transmitted
-+to the receiver, so you should either match these values using a 0, or use
-+the names in effect on the receiving side (typically "root").  All other
-+bf(FROM) names match those in use on the sending side.  All bf(TO) names
-+match those in use on the receiving side.
-+
-+Any IDs that do not have a name on the sending side are treated as having an
-+empty name for the purpose of matching.  This allows them to be matched via
-+a "*" or using an empty name.  For instance:
-+
-+verb(  --usermap=:nobody --groupmap=*:nobody)
-+
-+When the bf(--numeric-ids) option is used,the sender does not send any
-+names, so all the IDs are treated as having an empty name.  This means that
-+you will need to specify numeric bf(FROM) values if you want to map these
-+nameless IDs to different values.
-+
-+For the bf(--usermap) option to have any effect, the bf(-o) (bf(--owner))
-+option must be used (or implied), and the receiver will need to be running
-+as a super-user (see also the bf(--fake-super) option).  For the bf(--groupmap)
-+option to have any effect, the bf(-g) (bf(--groups)) option must be used
-+(or implied), and the receiver will need to have permissions to set that
-+group.
-+
-+dit(bf(--chown=USER:GROUP)) This option forces all files to be owned by USER
-+with group GROUP.  This is a simpler interface than using bf(--usermap) and
-+bf(--groupmap) directly, but it is implemented using those options internally,
-+so you cannot mix them.  If either the USER or GROUP is empty, no mapping for
-+the omitted user/group will occur.  If GROUP is empty, the trailing colon may
-+be omitted, but if USER is empty, a leading colon must be supplied.
-+
-+If you specify "--chown=foo:bar, this is exactly the same as specifying
-+"--usermap=*:foo --groupmap=*:bar", only easier.
-+
- dit(bf(--timeout=TIMEOUT)) This option allows you to set a maximum I/O
- timeout in seconds. If no data is transferred for the specified time
- then rsync will exit. The default is 0, which means no timeout.
-diff --git a/support/mapfrom b/support/mapfrom
-new file mode 100755
---- /dev/null
-+++ b/support/mapfrom
-@@ -0,0 +1,5 @@
-+#!/usr/bin/perl
-+while (<>) {
-+    push @_, "$2:$1" if /^(\w+):[^:]+:(\d+)/;
-+}
-+print join(',', @_), "\n";
-diff --git a/support/mapto b/support/mapto
-new file mode 100755
---- /dev/null
-+++ b/support/mapto
-@@ -0,0 +1,5 @@
-+#!/usr/bin/perl
-+while (<>) {
-+    push @_, "$1:$2" if /^(\w+):[^:]+:(\d+)/;
-+}
-+print join(',', @_), "\n";
-diff --git a/uidlist.c b/uidlist.c
---- a/uidlist.c
-+++ b/uidlist.c
-@@ -24,6 +24,7 @@
-  * are special. */
- #include "rsync.h"
-+#include "ifuncs.h"
- #include "io.h"
- extern int verbose;
-@@ -32,6 +33,8 @@ extern int preserve_uid;
- extern int preserve_gid;
- extern int preserve_acls;
- extern int numeric_ids;
-+extern char *usermap;
-+extern char *groupmap;
- #ifdef HAVE_GETGROUPS
- # ifndef GETGROUPS_T
-@@ -41,6 +44,9 @@ extern int numeric_ids;
- #define GID_NONE ((gid_t)-1)
-+#define NFLAGS_WILD_NAME_MATCH (1<<0)
-+#define NFLAGS_NAME_MATCH (1<<1)
-+
- struct idlist {
-       struct idlist *next;
-       const char *name;
-@@ -48,8 +54,8 @@ struct idlist {
-       uint16 flags;
- };
--static struct idlist *uidlist;
--static struct idlist *gidlist;
-+static struct idlist *uidlist, *uidmap;
-+static struct idlist *gidlist, *gidmap;
- static struct idlist *add_to_list(struct idlist **root, id_t id, const char *name,
-                                 id_t id2, uint16 flags)
-@@ -84,22 +90,6 @@ static const char *gid_to_name(gid_t gid)
-       return NULL;
- }
--static uid_t map_uid(uid_t id, const char *name)
--{
--      uid_t uid;
--      if (id != 0 && name_to_uid(name, &uid))
--              return uid;
--      return id;
--}
--
--static gid_t map_gid(gid_t id, const char *name)
--{
--      gid_t gid;
--      if (id != 0 && name_to_gid(name, &gid))
--              return gid;
--      return id;
--}
--
- static int is_in_group(gid_t gid)
- {
- #ifdef HAVE_GETGROUPS
-@@ -159,34 +149,53 @@ static int is_in_group(gid_t gid)
- #endif
- }
--/* Add a uid to the list of uids.  Only called on receiving side. */
--static struct idlist *recv_add_uid(uid_t id, const char *name)
-+/* Add a uid/gid to its list of ids.  Only called on receiving side. */
-+static struct idlist *recv_add_id(struct idlist **idlist_ptr, struct idlist *idmap,
-+                                id_t id, const char *name)
- {
--      uid_t id2 = name ? map_uid(id, name) : id;
-       struct idlist *node;
-+      int flag;
-+      id_t id2;
--      node = add_to_list(&uidlist, id, name, id2, 0);
-+      if (!name)
-+              name = "";
--      if (verbose > 3) {
--              rprintf(FINFO, "uid %u(%s) maps to %u\n",
--                      (unsigned)id, name ? name : "", (unsigned)id2);
-+      for (node = idmap; node; node = node->next) {
-+              if (node->flags & NFLAGS_WILD_NAME_MATCH) {
-+                      if (!wildmatch(node->name, name))
-+                              continue;
-+              } else if (node->flags & NFLAGS_NAME_MATCH) {
-+                      if (strcmp(node->name, name) != 0)
-+                              continue;
-+              } else if (node->name) {
-+                      if (id < node->id || id > (unsigned long)node->name)
-+                              continue;
-+              } else {
-+                      if (node->id != id)
-+                              continue;
-+              }
-+              break;
-       }
-+      if (node)
-+              id2 = node->id2;
-+      else if (*name && id) {
-+              if (idlist_ptr == &uidlist) {
-+                      uid_t uid;
-+                      id2 = name_to_uid(name, &uid) ? uid : id;
-+              } else {
-+                      gid_t gid;
-+                      id2 = name_to_gid(name, &gid) ? gid : id;
-+              }
-+      } else
-+              id2 = id;
--      return node;
--}
--
--/* Add a gid to the list of gids.  Only called on receiving side. */
--static struct idlist *recv_add_gid(gid_t id, const char *name)
--{
--      gid_t id2 = name ? map_gid(id, name) : id;
--      struct idlist *node;
--
--      node = add_to_list(&gidlist, id, name, id2,
--              !am_root && !is_in_group(id2) ? FLAG_SKIP_GROUP : 0);
-+      flag = idlist_ptr == &gidlist && !am_root && !is_in_group(id2) ? FLAG_SKIP_GROUP : 0;
-+      node = add_to_list(idlist_ptr, id, *name ? name : NULL, id2, flag);
-       if (verbose > 3) {
--              rprintf(FINFO, "gid %u(%s) maps to %u\n",
--                      (unsigned)id, name ? name : "", (unsigned)id2);
-+              rprintf(FINFO, "%sid %u(%s) maps to %u\n",
-+                      idlist_ptr == &uidlist ? "u" : "g",
-+                      (unsigned)id, name, (unsigned)id2);
-       }
-       return node;
-@@ -195,12 +204,9 @@ static struct idlist *recv_add_gid(gid_t id, const char *name)
- /* this function is a definate candidate for a faster algorithm */
- uid_t match_uid(uid_t uid)
- {
--      static uid_t last_in, last_out;
-+      static uid_t last_in = -1, last_out = -1;
-       struct idlist *list;
--      if (uid == 0)
--              return 0;
--
-       if (uid == last_in)
-               return last_out;
-@@ -208,10 +214,13 @@ uid_t match_uid(uid_t uid)
-       for (list = uidlist; list; list = list->next) {
-               if (list->id == uid)
--                      return last_out = list->id2;
-+                      break;
-       }
--      return last_out = uid;
-+      if (!list)
-+              list = recv_add_id(&uidlist, uidmap, uid, NULL);
-+
-+      return last_out = list->id2;
- }
- gid_t match_gid(gid_t gid, uint16 *flags_ptr)
-@@ -227,7 +236,7 @@ gid_t match_gid(gid_t gid, uint16 *flags_ptr)
-                               break;
-               }
-               if (!list)
--                      list = recv_add_gid(gid, NULL);
-+                      list = recv_add_id(&gidlist, gidmap, gid, NULL);
-               last = list;
-       }
-@@ -320,7 +329,7 @@ uid_t recv_user_name(int f, uid_t uid)
-               free(name);
-               name = NULL;
-       }
--      node = recv_add_uid(uid, name); /* node keeps name's memory */
-+      node = recv_add_id(&uidlist, uidmap, uid, name); /* node keeps name's memory */
-       return node->id2;
- }
-@@ -336,7 +345,7 @@ gid_t recv_group_name(int f, gid_t gid, uint16 *flags_ptr)
-               free(name);
-               name = NULL;
-       }
--      node = recv_add_gid(gid, name); /* node keeps name's memory */
-+      node = recv_add_id(&gidlist, gidmap, gid, name); /* node keeps name's memory */
-       if (flags_ptr && node->flags & FLAG_SKIP_GROUP)
-               *flags_ptr |= FLAG_SKIP_GROUP;
-       return node->id2;
-@@ -363,17 +372,103 @@ void recv_id_list(int f, struct file_list *flist)
-       /* Now convert all the uids/gids from sender values to our values. */
- #ifdef SUPPORT_ACLS
--      if (preserve_acls && !numeric_ids)
-+      if (preserve_acls && (!numeric_ids || usermap || groupmap))
-               match_acl_ids();
- #endif
--      if (am_root && preserve_uid && !numeric_ids) {
-+      if (am_root && preserve_uid && (!numeric_ids || usermap)) {
-               for (i = 0; i < flist->used; i++)
-                       F_OWNER(flist->files[i]) = match_uid(F_OWNER(flist->files[i]));
-       }
--      if (preserve_gid && (!am_root || !numeric_ids)) {
-+      if (preserve_gid && (!am_root || !numeric_ids || groupmap)) {
-               for (i = 0; i < flist->used; i++) {
-                       F_GROUP(flist->files[i]) = match_gid(F_GROUP(flist->files[i]),
-                                                            &flist->files[i]->flags);
-               }
-       }
- }
-+
-+void parse_name_map(char *map, int usernames)
-+{
-+      struct idlist **idmap_ptr = usernames ? &uidmap : &gidmap;
-+      struct idlist **idlist_ptr = usernames ? &uidlist : &gidlist;
-+      char *colon, *end, *name, *cp = map + strlen(map);
-+      id_t id1;
-+      uint16 flags;
-+
-+      /* Parse the list in reverse, so the order in the struct is right. */
-+      while (1) {
-+              end = cp;
-+              while (cp > map && cp[-1] != ',') cp--;
-+              if (!(colon = strchr(cp, ':'))) {
-+                      rprintf(FERROR, "No colon found in --%smap: %s\n",
-+                              usernames ? "user" : "group", cp);
-+                      exit_cleanup(RERR_SYNTAX);
-+              }
-+              if (!colon[1]) {
-+                      rprintf(FERROR, "No name found after colon --%smap: %s\n",
-+                              usernames ? "user" : "group", cp);
-+                      exit_cleanup(RERR_SYNTAX);
-+              }
-+              *colon = '\0';
-+
-+              if (isDigit(cp)) {
-+                      char *dash = strchr(cp, '-');
-+                      if (strspn(cp, "0123456789-") != (size_t)(colon - cp)
-+                       || (dash && (!dash[1] || strchr(dash+1, '-')))) {
-+                        bad_number:
-+                              rprintf(FERROR, "Invalid number in --%smap: %s\n",
-+                                      usernames ? "user" : "group", cp);
-+                              exit_cleanup(RERR_SYNTAX);
-+                      }
-+                      if (dash)
-+                              name = (char *)atol(dash+1);
-+                      else
-+                              name = (char *)0;
-+                      flags = 0;
-+                      id1 = atol(cp);
-+              } else if (strpbrk(cp, "*[?")) {
-+                      flags = NFLAGS_WILD_NAME_MATCH;
-+                      name = cp;
-+                      id1 = 0;
-+              } else {
-+                      flags = NFLAGS_NAME_MATCH;
-+                      name = cp;
-+                      id1 = 0;
-+              }
-+
-+              if (isDigit(colon+1)) {
-+                      if (strspn(colon+1, "0123456789") != (size_t)(end - colon - 1)) {
-+                              cp = colon+1;
-+                              goto bad_number;
-+                      }
-+                      add_to_list(idmap_ptr, id1, name, atol(colon+1), flags);
-+              } else if (usernames) {
-+                      uid_t uid;
-+                      if (name_to_uid(colon+1, &uid))
-+                              add_to_list(idmap_ptr, id1, name, uid, flags);
-+                      else {
-+                              rprintf(FERROR,
-+                                  "Unknown --usermap name on receiver: %s\n",
-+                                  colon+1);
-+                      }
-+              } else {
-+                      gid_t gid;
-+                      if (name_to_gid(colon+1, &gid))
-+                              add_to_list(idmap_ptr, id1, name, gid, flags);
-+                      else {
-+                              rprintf(FERROR,
-+                                  "Unknown --groupmap name on receiver: %s\n",
-+                                  colon+1);
-+                      }
-+              }
-+
-+              if (cp == map)
-+                      break;
-+
-+              *--cp = '\0'; /* replace comma */
-+      }
-+
-+      /* The 0 user/group doesn't get its name sent, so add it explicitly. */
-+      recv_add_id(idlist_ptr, *idmap_ptr, 0,
-+                  numeric_ids ? NULL : usernames ? uid_to_name(0) : gid_to_name(0));
-+}