1228753Smm/*- 2228753Smm * Copyright (c) 2003-2007 Tim Kientzle 3228753Smm * All rights reserved. 4228753Smm * 5228753Smm * Redistribution and use in source and binary forms, with or without 6228753Smm * modification, are permitted provided that the following conditions 7228753Smm * are met: 8228753Smm * 1. Redistributions of source code must retain the above copyright 9228753Smm * notice, this list of conditions and the following disclaimer 10228753Smm * in this position and unchanged. 11228753Smm * 2. Redistributions in binary form must reproduce the above copyright 12228753Smm * notice, this list of conditions and the following disclaimer in the 13228753Smm * documentation and/or other materials provided with the distribution. 14228753Smm * 15228753Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 16228753Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17228753Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18228753Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 19228753Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20228753Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21228753Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22228753Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23228753Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24228753Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25228753Smm */ 26228753Smm 27228753Smm 28228753Smm#include "cpio_platform.h" 29228763Smm__FBSDID("$FreeBSD: releng/10.3/contrib/libarchive/cpio/cmdline.c 248616 2013-03-22 13:36:03Z mm $"); 30228753Smm 31228753Smm#ifdef HAVE_ERRNO_H 32228753Smm#include <errno.h> 33228753Smm#endif 34228753Smm#ifdef HAVE_GRP_H 35228753Smm#include <grp.h> 36228753Smm#endif 37228753Smm#ifdef HAVE_PWD_H 38228753Smm#include <pwd.h> 39228753Smm#endif 40228753Smm#include <stdio.h> 41228753Smm#ifdef HAVE_STDLIB_H 42228753Smm#include <stdlib.h> 43228753Smm#endif 44228753Smm#ifdef HAVE_STRING_H 45228753Smm#include <string.h> 46228753Smm#endif 47228753Smm 48228753Smm#include "cpio.h" 49228753Smm#include "err.h" 50228753Smm 51228753Smm/* 52228753Smm * Short options for cpio. Please keep this sorted. 53228753Smm */ 54232153Smmstatic const char *short_options = "0AaBC:cdE:F:f:H:hI:iJjLlmnO:opR:rtuVvW:yZz"; 55228753Smm 56228753Smm/* 57228753Smm * Long options for cpio. Please keep this sorted. 58228753Smm */ 59228753Smmstatic const struct option { 60228753Smm const char *name; 61228753Smm int required; /* 1 if this option requires an argument */ 62228753Smm int equivalent; /* Equivalent short option. */ 63228753Smm} cpio_longopts[] = { 64248616Smm { "b64encode", 0, OPTION_B64ENCODE }, 65228753Smm { "create", 0, 'o' }, 66232153Smm { "dot", 0, 'V' }, 67228753Smm { "extract", 0, 'i' }, 68228753Smm { "file", 1, 'F' }, 69228753Smm { "format", 1, 'H' }, 70248616Smm { "grzip", 0, OPTION_GRZIP }, 71228753Smm { "help", 0, 'h' }, 72228753Smm { "insecure", 0, OPTION_INSECURE }, 73228753Smm { "link", 0, 'l' }, 74228753Smm { "list", 0, 't' }, 75248616Smm { "lrzip", 0, OPTION_LRZIP }, 76228753Smm { "lzma", 0, OPTION_LZMA }, 77248616Smm { "lzop", 0, OPTION_LZOP }, 78228753Smm { "make-directories", 0, 'd' }, 79228753Smm { "no-preserve-owner", 0, OPTION_NO_PRESERVE_OWNER }, 80228753Smm { "null", 0, '0' }, 81228753Smm { "numeric-uid-gid", 0, 'n' }, 82228753Smm { "owner", 1, 'R' }, 83228753Smm { "pass-through", 0, 'p' }, 84228753Smm { "preserve-modification-time", 0, 'm' }, 85228753Smm { "preserve-owner", 0, OPTION_PRESERVE_OWNER }, 86228753Smm { "quiet", 0, OPTION_QUIET }, 87228753Smm { "unconditional", 0, 'u' }, 88248616Smm { "uuencode", 0, OPTION_UUENCODE }, 89228753Smm { "verbose", 0, 'v' }, 90228753Smm { "version", 0, OPTION_VERSION }, 91228753Smm { "xz", 0, 'J' }, 92228753Smm { NULL, 0, 0 } 93228753Smm}; 94228753Smm 95228753Smm/* 96228753Smm * I used to try to select platform-provided getopt() or 97228753Smm * getopt_long(), but that caused a lot of headaches. In particular, 98228753Smm * I couldn't consistently use long options in the test harness 99228753Smm * because not all platforms have getopt_long(). That in turn led to 100228753Smm * overuse of the -W hack in the test harness, which made it rough to 101228753Smm * run the test harness against GNU cpio. (I periodically run the 102228753Smm * test harness here against GNU cpio as a sanity-check. Yes, 103228753Smm * I've found a couple of bugs in GNU cpio that way.) 104228753Smm */ 105228753Smmint 106228753Smmcpio_getopt(struct cpio *cpio) 107228753Smm{ 108228753Smm enum { state_start = 0, state_next_word, state_short, state_long }; 109228753Smm static int state = state_start; 110228753Smm static char *opt_word; 111228753Smm 112228753Smm const struct option *popt, *match = NULL, *match2 = NULL; 113228753Smm const char *p, *long_prefix = "--"; 114228753Smm size_t optlength; 115228753Smm int opt = '?'; 116228753Smm int required = 0; 117228753Smm 118232153Smm cpio->argument = NULL; 119228753Smm 120228753Smm /* First time through, initialize everything. */ 121228753Smm if (state == state_start) { 122228753Smm /* Skip program name. */ 123228753Smm ++cpio->argv; 124228753Smm --cpio->argc; 125228753Smm state = state_next_word; 126228753Smm } 127228753Smm 128228753Smm /* 129228753Smm * We're ready to look at the next word in argv. 130228753Smm */ 131228753Smm if (state == state_next_word) { 132228753Smm /* No more arguments, so no more options. */ 133228753Smm if (cpio->argv[0] == NULL) 134228753Smm return (-1); 135228753Smm /* Doesn't start with '-', so no more options. */ 136228753Smm if (cpio->argv[0][0] != '-') 137228753Smm return (-1); 138228753Smm /* "--" marks end of options; consume it and return. */ 139228753Smm if (strcmp(cpio->argv[0], "--") == 0) { 140228753Smm ++cpio->argv; 141228753Smm --cpio->argc; 142228753Smm return (-1); 143228753Smm } 144228753Smm /* Get next word for parsing. */ 145228753Smm opt_word = *cpio->argv++; 146228753Smm --cpio->argc; 147228753Smm if (opt_word[1] == '-') { 148228753Smm /* Set up long option parser. */ 149228753Smm state = state_long; 150228753Smm opt_word += 2; /* Skip leading '--' */ 151228753Smm } else { 152228753Smm /* Set up short option parser. */ 153228753Smm state = state_short; 154228753Smm ++opt_word; /* Skip leading '-' */ 155228753Smm } 156228753Smm } 157228753Smm 158228753Smm /* 159228753Smm * We're parsing a group of POSIX-style single-character options. 160228753Smm */ 161228753Smm if (state == state_short) { 162228753Smm /* Peel next option off of a group of short options. */ 163228753Smm opt = *opt_word++; 164228753Smm if (opt == '\0') { 165228753Smm /* End of this group; recurse to get next option. */ 166228753Smm state = state_next_word; 167228753Smm return cpio_getopt(cpio); 168228753Smm } 169228753Smm 170228753Smm /* Does this option take an argument? */ 171228753Smm p = strchr(short_options, opt); 172228753Smm if (p == NULL) 173228753Smm return ('?'); 174228753Smm if (p[1] == ':') 175228753Smm required = 1; 176228753Smm 177228753Smm /* If it takes an argument, parse that. */ 178228753Smm if (required) { 179228753Smm /* If arg is run-in, opt_word already points to it. */ 180228753Smm if (opt_word[0] == '\0') { 181228753Smm /* Otherwise, pick up the next word. */ 182228753Smm opt_word = *cpio->argv; 183228753Smm if (opt_word == NULL) { 184228753Smm lafe_warnc(0, 185228753Smm "Option -%c requires an argument", 186228753Smm opt); 187228753Smm return ('?'); 188228753Smm } 189228753Smm ++cpio->argv; 190228753Smm --cpio->argc; 191228753Smm } 192228753Smm if (opt == 'W') { 193228753Smm state = state_long; 194228753Smm long_prefix = "-W "; /* For clearer errors. */ 195228753Smm } else { 196228753Smm state = state_next_word; 197232153Smm cpio->argument = opt_word; 198228753Smm } 199228753Smm } 200228753Smm } 201228753Smm 202228753Smm /* We're reading a long option, including -W long=arg convention. */ 203228753Smm if (state == state_long) { 204228753Smm /* After this long option, we'll be starting a new word. */ 205228753Smm state = state_next_word; 206228753Smm 207228753Smm /* Option name ends at '=' if there is one. */ 208228753Smm p = strchr(opt_word, '='); 209228753Smm if (p != NULL) { 210228753Smm optlength = (size_t)(p - opt_word); 211232153Smm cpio->argument = (char *)(uintptr_t)(p + 1); 212228753Smm } else { 213228753Smm optlength = strlen(opt_word); 214228753Smm } 215228753Smm 216228753Smm /* Search the table for an unambiguous match. */ 217228753Smm for (popt = cpio_longopts; popt->name != NULL; popt++) { 218228753Smm /* Short-circuit if first chars don't match. */ 219228753Smm if (popt->name[0] != opt_word[0]) 220228753Smm continue; 221228753Smm /* If option is a prefix of name in table, record it.*/ 222228753Smm if (strncmp(opt_word, popt->name, optlength) == 0) { 223228753Smm match2 = match; /* Record up to two matches. */ 224228753Smm match = popt; 225228753Smm /* If it's an exact match, we're done. */ 226228753Smm if (strlen(popt->name) == optlength) { 227228753Smm match2 = NULL; /* Forget the others. */ 228228753Smm break; 229228753Smm } 230228753Smm } 231228753Smm } 232228753Smm 233228753Smm /* Fail if there wasn't a unique match. */ 234228753Smm if (match == NULL) { 235228753Smm lafe_warnc(0, 236228753Smm "Option %s%s is not supported", 237228753Smm long_prefix, opt_word); 238228753Smm return ('?'); 239228753Smm } 240228753Smm if (match2 != NULL) { 241228753Smm lafe_warnc(0, 242228753Smm "Ambiguous option %s%s (matches --%s and --%s)", 243228753Smm long_prefix, opt_word, match->name, match2->name); 244228753Smm return ('?'); 245228753Smm } 246228753Smm 247228753Smm /* We've found a unique match; does it need an argument? */ 248228753Smm if (match->required) { 249228753Smm /* Argument required: get next word if necessary. */ 250232153Smm if (cpio->argument == NULL) { 251232153Smm cpio->argument = *cpio->argv; 252232153Smm if (cpio->argument == NULL) { 253228753Smm lafe_warnc(0, 254228753Smm "Option %s%s requires an argument", 255228753Smm long_prefix, match->name); 256228753Smm return ('?'); 257228753Smm } 258228753Smm ++cpio->argv; 259228753Smm --cpio->argc; 260228753Smm } 261228753Smm } else { 262228753Smm /* Argument forbidden: fail if there is one. */ 263232153Smm if (cpio->argument != NULL) { 264228753Smm lafe_warnc(0, 265228753Smm "Option %s%s does not allow an argument", 266228753Smm long_prefix, match->name); 267228753Smm return ('?'); 268228753Smm } 269228753Smm } 270228753Smm return (match->equivalent); 271228753Smm } 272228753Smm 273228753Smm return (opt); 274228753Smm} 275228753Smm 276228753Smm 277228753Smm/* 278228753Smm * Parse the argument to the -R or --owner flag. 279228753Smm * 280228753Smm * The format is one of the following: 281228753Smm * <username|uid> - Override user but not group 282228753Smm * <username>: - Override both, group is user's default group 283228753Smm * <uid>: - Override user but not group 284228753Smm * <username|uid>:<groupname|gid> - Override both 285228753Smm * :<groupname|gid> - Override group but not user 286228753Smm * 287228753Smm * Where uid/gid are decimal representations and groupname/username 288228753Smm * are names to be looked up in system database. Note that we try 289228753Smm * to look up an argument as a name first, then try numeric parsing. 290228753Smm * 291228753Smm * A period can be used instead of the colon. 292228753Smm * 293228753Smm * Sets uid/gid return as appropriate, -1 indicates uid/gid not specified. 294228777Smm * TODO: If the spec uses uname/gname, then return those to the caller 295228777Smm * as well. If the spec provides uid/gid, just return names as NULL. 296228753Smm * 297228753Smm * Returns NULL if no error, otherwise returns error string for display. 298228753Smm * 299228753Smm */ 300228753Smmconst char * 301228753Smmowner_parse(const char *spec, int *uid, int *gid) 302228753Smm{ 303228753Smm static char errbuff[128]; 304228753Smm const char *u, *ue, *g; 305228753Smm 306228753Smm *uid = -1; 307228753Smm *gid = -1; 308228753Smm 309228753Smm if (spec[0] == '\0') 310228753Smm return ("Invalid empty user/group spec"); 311228753Smm 312228753Smm /* 313228753Smm * Split spec into [user][:.][group] 314228753Smm * u -> first char of username, NULL if no username 315228753Smm * ue -> first char after username (colon, period, or \0) 316228753Smm * g -> first char of group name 317228753Smm */ 318228753Smm if (*spec == ':' || *spec == '.') { 319228753Smm /* If spec starts with ':' or '.', then just group. */ 320228753Smm ue = u = NULL; 321228753Smm g = spec + 1; 322228753Smm } else { 323228753Smm /* Otherwise, [user] or [user][:] or [user][:][group] */ 324228753Smm ue = u = spec; 325228753Smm while (*ue != ':' && *ue != '.' && *ue != '\0') 326228753Smm ++ue; 327228753Smm g = ue; 328228753Smm if (*g != '\0') /* Skip : or . to find first char of group. */ 329228753Smm ++g; 330228753Smm } 331228753Smm 332228753Smm if (u != NULL) { 333228753Smm /* Look up user: ue is first char after end of user. */ 334228753Smm char *user; 335228753Smm struct passwd *pwent; 336228753Smm 337228753Smm user = (char *)malloc(ue - u + 1); 338228753Smm if (user == NULL) 339228753Smm return ("Couldn't allocate memory"); 340228753Smm memcpy(user, u, ue - u); 341228753Smm user[ue - u] = '\0'; 342228753Smm if ((pwent = getpwnam(user)) != NULL) { 343228753Smm *uid = pwent->pw_uid; 344228753Smm if (*ue != '\0') 345228753Smm *gid = pwent->pw_gid; 346228753Smm } else { 347228753Smm char *end; 348228753Smm errno = 0; 349232153Smm *uid = (int)strtoul(user, &end, 10); 350228753Smm if (errno || *end != '\0') { 351228753Smm snprintf(errbuff, sizeof(errbuff), 352228753Smm "Couldn't lookup user ``%s''", user); 353228753Smm errbuff[sizeof(errbuff) - 1] = '\0'; 354238856Smm free(user); 355228753Smm return (errbuff); 356228753Smm } 357228753Smm } 358228753Smm free(user); 359228753Smm } 360228753Smm 361228753Smm if (*g != '\0') { 362228753Smm struct group *grp; 363228753Smm if ((grp = getgrnam(g)) != NULL) { 364228753Smm *gid = grp->gr_gid; 365228753Smm } else { 366228753Smm char *end; 367228753Smm errno = 0; 368232153Smm *gid = (int)strtoul(g, &end, 10); 369228753Smm if (errno || *end != '\0') { 370228753Smm snprintf(errbuff, sizeof(errbuff), 371228753Smm "Couldn't lookup group ``%s''", g); 372228753Smm errbuff[sizeof(errbuff) - 1] = '\0'; 373228753Smm return (errbuff); 374228753Smm } 375228753Smm } 376228753Smm } 377228753Smm return (NULL); 378228753Smm} 379