From 11ea87aa82d22c855c9aa02021378a022ef04fdd Mon Sep 17 00:00:00 2001 From: Tim Kientzle Date: Thu, 2 Jul 2009 00:17:34 -0400 Subject: [PATCH] Support decimal uid/gid when parsing -R/--owner option. Extend existing test to cover new functionality. Rework this test slightly to reduce conditionals for Cygwin. SVN-Revision: 1208 --- cpio/cmdline.c | 68 +++++++++++++++++++++--------- cpio/test/test_owner_parse.c | 81 ++++++++++++++++++++++-------------- 2 files changed, 99 insertions(+), 50 deletions(-) diff --git a/cpio/cmdline.c b/cpio/cmdline.c index 33307bb34..f090e260b 100644 --- a/cpio/cmdline.c +++ b/cpio/cmdline.c @@ -271,16 +271,36 @@ cpio_getopt(struct cpio *cpio) * Parse the argument to the -R or --owner flag. * * The format is one of the following: - * - Override user but not group - * : - Override both, group is user's default group - * : - Override both - * : - Override group but not user + * - Override user but not group + * : - Override both, group is user's default group + * : - Override user but not group + * : - Override both + * : - Override group but not user + * + * Where uid/gid are decimal representations and groupname/username + * are names to be looked up in system database. Note that + * uid/gid parsing takes priority over username/groupname lookup, + * so this won't do a lookup for usernames or group names that + * consist entirely of digits. * * A period can be used instead of the colon. * - * Sets uid/gid as appropriate, -1 indicates uid/gid not specified. + * Sets uid/gid return as appropriate, -1 indicates uid/gid not specified. * */ +static int +decimal_parse(const char *p) +{ + /* TODO: guard against overflow. */ + int n = 0; + for (; *p != '\0'; ++p) { + if (*p < '0' || *p > '9') + return (-1); + n = n * 10 + *p - '0'; + } + return (n); +} + int owner_parse(const char *spec, int *uid, int *gid) { @@ -321,24 +341,34 @@ owner_parse(const char *spec, int *uid, int *gid) } memcpy(user, u, ue - u); user[ue - u] = '\0'; - pwent = getpwnam(user); - if (pwent == NULL) { - lafe_warnc(errno, "Couldn't lookup user ``%s''", user); - return (1); + *uid = decimal_parse(user); + if (*uid < 0) { + /* Couldn't parse as integer, try username lookup. */ + pwent = getpwnam(user); + if (pwent == NULL) { + lafe_warnc(errno, + "Couldn't lookup user ``%s''", user); + return (1); + } + *uid = pwent->pw_uid; + if (*ue != '\0' && *g == '\0') + *gid = pwent->pw_gid; } free(user); - *uid = pwent->pw_uid; - if (*ue != '\0' && *g == '\0') - *gid = pwent->pw_gid; } if (*g != '\0') { - struct group *grp; - grp = getgrnam(g); - if (grp != NULL) - *gid = grp->gr_gid; - else { - lafe_warnc(errno, "Couldn't look up group ``%s''", g); - return (1); + *gid = decimal_parse(g); + if (*gid < 0) { + /* Couldn't parse int, try group name lookup. */ + struct group *grp; + grp = getgrnam(g); + if (grp != NULL) + *gid = grp->gr_gid; + else { + lafe_warnc(errno, + "Couldn't look up group ``%s''", g); + return (1); + } } } return (0); diff --git a/cpio/test/test_owner_parse.c b/cpio/test/test_owner_parse.c index b756a2f81..050d5a088 100644 --- a/cpio/test/test_owner_parse.c +++ b/cpio/test/test_owner_parse.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2003-2007 Tim Kientzle + * Copyright (c) 2003-2009 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,16 +38,23 @@ __FBSDID("$FreeBSD$"); * Use CreateWellKnownSID() and LookupAccountName()? */ #define ROOT "Administrator" -#define ROOT_UID 500 -#define ROOT_GID1 513 -#define ROOT_GID2 545 -#define ROOT_GID3 544 +static int root_uids[] = { 500 }; +static int root_gids[] = { 513, 545, 544 }; #else #define ROOT "root" -#define ROOT_UID 0 -#define ROOT_GID 0 +static int root_uids[] = { 0 }; +static int root_gids[] = { 0 }; #endif +static int +int_in_list(int i, int *l, size_t n) +{ + while (n-- > 0) + if (*l++ == i) + return (1); + return (0); +} + DEFINE_TEST(test_owner_parse) { #if defined(_WIN32) && !defined(__CYGWIN__) @@ -56,38 +63,49 @@ DEFINE_TEST(test_owner_parse) #else int uid, gid; - lafe_progname = "Ignore this message"; + lafe_progname = "cpio"; assertEqualInt(0, owner_parse(ROOT, &uid, &gid)); - assertEqualInt(ROOT_UID, uid); + assert(int_in_list(uid, root_uids, + sizeof(root_uids)/sizeof(root_uids[0]))); assertEqualInt(-1, gid); assertEqualInt(0, owner_parse(ROOT ":", &uid, &gid)); - assertEqualInt(ROOT_UID, uid); -#if defined(__CYGWIN__) - { - int gidIsOneOf = (ROOT_GID1 == gid) - || (ROOT_GID2 == gid) - || (ROOT_GID3 == gid); - assertEqualInt(1, gidIsOneOf); - } -#else - assertEqualInt(ROOT_GID, gid); -#endif + assert(int_in_list(uid, root_uids, + sizeof(root_uids)/sizeof(root_uids[0]))); + assert(int_in_list(gid, root_gids, + sizeof(root_gids)/sizeof(root_gids[0]))); assertEqualInt(0, owner_parse(ROOT ".", &uid, &gid)); - assertEqualInt(ROOT_UID, uid); -#if defined(__CYGWIN__) - { - int gidIsOneOf = (ROOT_GID1 == gid) - || (ROOT_GID2 == gid) - || (ROOT_GID3 == gid); - assertEqualInt(1, gidIsOneOf); - } -#else - assertEqualInt(ROOT_GID, gid); -#endif + assert(int_in_list(uid, root_uids, + sizeof(root_uids)/sizeof(root_uids[0]))); + assert(int_in_list(gid, root_gids, + sizeof(root_gids)/sizeof(root_gids[0]))); + + assertEqualInt(0, owner_parse("111", &uid, &gid)); + assertEqualInt(111, uid); + assertEqualInt(-1, gid); + + assertEqualInt(0, owner_parse("112:", &uid, &gid)); + assertEqualInt(112, uid); + /* Can't assert gid, since we don't know gid for user #112. */ + + assertEqualInt(0, owner_parse("113.", &uid, &gid)); + assertEqualInt(113, uid); + /* Can't assert gid, since we don't know gid for user #113. */ + + assertEqualInt(0, owner_parse(":114", &uid, &gid)); + assertEqualInt(-1, uid); + assertEqualInt(114, gid); + + assertEqualInt(0, owner_parse(".115", &uid, &gid)); + assertEqualInt(-1, uid); + assertEqualInt(115, gid); + + assertEqualInt(0, owner_parse("116:117", &uid, &gid)); + assertEqualInt(116, uid); + assertEqualInt(117, gid); /* * TODO: Lookup current user/group name, build strings and @@ -104,6 +122,7 @@ DEFINE_TEST(test_owner_parse) * Alternatively, redirect stderr temporarily to suppress the output. */ + lafe_progname = "Ignore this message"; assertEqualInt(1, owner_parse(":nonexistentgroup", &uid, &gid)); assertEqualInt(1, owner_parse(ROOT ":nonexistentgroup", &uid, &gid)); assertEqualInt(1, -- 2.47.3