X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/142a5e7b8b128797db27c1a31af7d05c9cc4928e..f8605c5b89720a09b8aa0d5e7aa3ea27b1ebbf4f:/uidlist.c diff --git a/uidlist.c b/uidlist.c index 790e9c16..7e8cbd7b 100644 --- a/uidlist.c +++ b/uidlist.c @@ -3,11 +3,12 @@ * * Copyright (C) 1996 Andrew Tridgell * Copyright (C) 1996 Paul Mackerras - * Copyright (C) 2004-2007 Wayne Davison + * Copyright (C) 2004-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 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 @@ -15,8 +16,7 @@ * 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. */ /* If the source username/group does not exist on the target then use @@ -24,14 +24,17 @@ * are special. */ #include "rsync.h" +#include "ifuncs.h" +#include "itypes.h" #include "io.h" -extern int verbose; extern int am_root; 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,17 +44,20 @@ 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; - char *name; + const char *name; id_t id, id2; 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, char *name, +static struct idlist *add_to_list(struct idlist **root, id_t id, const char *name, id_t id2, uint16 flags) { struct idlist *node = new(struct idlist); @@ -67,7 +73,7 @@ static struct idlist *add_to_list(struct idlist **root, id_t id, char *name, } /* turn a uid into a user name */ -static char *uid_to_name(uid_t uid) +static const char *uid_to_name(uid_t uid) { struct passwd *pass = getpwuid(uid); if (pass) @@ -76,7 +82,7 @@ static char *uid_to_name(uid_t uid) } /* turn a gid into a group name */ -static char *gid_to_name(gid_t gid) +static const char *gid_to_name(gid_t gid) { struct group *grp = getgrgid(gid); if (grp) @@ -84,22 +90,6 @@ static char *gid_to_name(gid_t gid) return NULL; } -static uid_t map_uid(uid_t id, 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, 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 @@ -126,7 +116,7 @@ static int is_in_group(gid_t gid) } if (n == ngroups) gidset[ngroups++] = mygid; - if (verbose > 3) { + if (DEBUG_GTE(OWN, 2)) { int pos; char *gidbuf = new_array(char, ngroups*21+32); if (!gidbuf) @@ -152,41 +142,60 @@ static int is_in_group(gid_t gid) static gid_t mygid = GID_NONE; if (mygid == GID_NONE) { mygid = MY_GID(); - if (verbose > 3) + if (DEBUG_GTE(OWN, 2)) rprintf(FINFO, "process has gid %u\n", (unsigned)mygid); } return gid == mygid; #endif } -/* Add a uid to the list of uids. Only called on receiving side. */ -static struct idlist *recv_add_uid(uid_t id, 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, 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); + if (DEBUG_GTE(OWN, 2)) { + 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, 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,37 +214,39 @@ 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) { - static gid_t last_in = GID_NONE, last_out = GID_NONE; + static struct idlist *last = NULL; struct idlist *list; - if (gid == last_in) - return last_out; - - last_in = gid; - - for (list = gidlist; list; list = list->next) { - if (list->id == gid) - break; + if (last && gid == last->id) + list = last; + else { + for (list = gidlist; list; list = list->next) { + if (list->id == gid) + break; + } + if (!list) + list = recv_add_id(&gidlist, gidmap, gid, NULL); + last = list; } - if (!list) - list = recv_add_gid(gid, NULL); - if (flags_ptr && list->flags & FLAG_SKIP_GROUP) *flags_ptr |= FLAG_SKIP_GROUP; - return last_out = list->id2; + return list->id2; } /* Add a uid to the list of uids. Only called on sending side. */ -char *add_uid(uid_t uid) +const char *add_uid(uid_t uid) { struct idlist *list; struct idlist *node; @@ -256,7 +264,7 @@ char *add_uid(uid_t uid) } /* Add a gid to the list of gids. Only called on sending side. */ -char *add_gid(gid_t gid) +const char *add_gid(gid_t gid) { struct idlist *list; struct idlist *node; @@ -317,7 +325,11 @@ uid_t recv_user_name(int f, uid_t uid) if (!name) out_of_memory("recv_user_name"); read_sbuf(f, name, len); - node = recv_add_uid(uid, name); /* node keeps name's memory */ + if (numeric_ids < 0) { + free(name); + name = NULL; + } + node = recv_add_id(&uidlist, uidmap, uid, name); /* node keeps name's memory */ return node->id2; } @@ -329,7 +341,11 @@ gid_t recv_group_name(int f, gid_t gid, uint16 *flags_ptr) if (!name) out_of_memory("recv_group_name"); read_sbuf(f, name, len); - node = recv_add_gid(gid, name); /* node keeps name's memory */ + if (numeric_ids < 0) { + free(name); + name = NULL; + } + 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; @@ -342,13 +358,13 @@ void recv_id_list(int f, struct file_list *flist) id_t id; int i; - if ((preserve_uid || preserve_acls) && !numeric_ids) { + if ((preserve_uid || preserve_acls) && numeric_ids <= 0) { /* read the uid list */ while ((id = read_varint30(f)) != 0) recv_user_name(f, id); } - if ((preserve_gid || preserve_acls) && !numeric_ids) { + if ((preserve_gid || preserve_acls) && numeric_ids <= 0) { /* read the gid list */ while ((id = read_varint30(f)) != 0) recv_group_name(f, id, NULL); @@ -356,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) { - for (i = 0; i < flist->count; i++) + 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)) { - for (i = 0; i < flist->count; i++) { + 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, BOOL 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)); +}