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: stable/10/contrib/libarchive/cpio/cmdline.c 324418 2017-10-08 20:55:45Z 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' }, 66305192Smm { "dereference", 0, 'L' }, 67232153Smm { "dot", 0, 'V' }, 68228753Smm { "extract", 0, 'i' }, 69228753Smm { "file", 1, 'F' }, 70228753Smm { "format", 1, 'H' }, 71248616Smm { "grzip", 0, OPTION_GRZIP }, 72228753Smm { "help", 0, 'h' }, 73228753Smm { "insecure", 0, OPTION_INSECURE }, 74228753Smm { "link", 0, 'l' }, 75228753Smm { "list", 0, 't' }, 76248616Smm { "lrzip", 0, OPTION_LRZIP }, 77302001Smm { "lz4", 0, OPTION_LZ4 }, 78228753Smm { "lzma", 0, OPTION_LZMA }, 79248616Smm { "lzop", 0, OPTION_LZOP }, 80228753Smm { "make-directories", 0, 'd' }, 81228753Smm { "no-preserve-owner", 0, OPTION_NO_PRESERVE_OWNER }, 82228753Smm { "null", 0, '0' }, 83228753Smm { "numeric-uid-gid", 0, 'n' }, 84228753Smm { "owner", 1, 'R' }, 85302001Smm { "passphrase", 1, OPTION_PASSPHRASE }, 86228753Smm { "pass-through", 0, 'p' }, 87228753Smm { "preserve-modification-time", 0, 'm' }, 88228753Smm { "preserve-owner", 0, OPTION_PRESERVE_OWNER }, 89228753Smm { "quiet", 0, OPTION_QUIET }, 90228753Smm { "unconditional", 0, 'u' }, 91248616Smm { "uuencode", 0, OPTION_UUENCODE }, 92228753Smm { "verbose", 0, 'v' }, 93228753Smm { "version", 0, OPTION_VERSION }, 94228753Smm { "xz", 0, 'J' }, 95324418Smm { "zstd", 0, OPTION_ZSTD }, 96228753Smm { NULL, 0, 0 } 97228753Smm}; 98228753Smm 99228753Smm/* 100228753Smm * I used to try to select platform-provided getopt() or 101228753Smm * getopt_long(), but that caused a lot of headaches. In particular, 102228753Smm * I couldn't consistently use long options in the test harness 103228753Smm * because not all platforms have getopt_long(). That in turn led to 104228753Smm * overuse of the -W hack in the test harness, which made it rough to 105228753Smm * run the test harness against GNU cpio. (I periodically run the 106228753Smm * test harness here against GNU cpio as a sanity-check. Yes, 107228753Smm * I've found a couple of bugs in GNU cpio that way.) 108228753Smm */ 109228753Smmint 110228753Smmcpio_getopt(struct cpio *cpio) 111228753Smm{ 112228753Smm enum { state_start = 0, state_next_word, state_short, state_long }; 113228753Smm static int state = state_start; 114228753Smm static char *opt_word; 115228753Smm 116228753Smm const struct option *popt, *match = NULL, *match2 = NULL; 117228753Smm const char *p, *long_prefix = "--"; 118228753Smm size_t optlength; 119228753Smm int opt = '?'; 120228753Smm int required = 0; 121228753Smm 122232153Smm cpio->argument = NULL; 123228753Smm 124228753Smm /* First time through, initialize everything. */ 125228753Smm if (state == state_start) { 126228753Smm /* Skip program name. */ 127228753Smm ++cpio->argv; 128228753Smm --cpio->argc; 129228753Smm state = state_next_word; 130228753Smm } 131228753Smm 132228753Smm /* 133228753Smm * We're ready to look at the next word in argv. 134228753Smm */ 135228753Smm if (state == state_next_word) { 136228753Smm /* No more arguments, so no more options. */ 137228753Smm if (cpio->argv[0] == NULL) 138228753Smm return (-1); 139228753Smm /* Doesn't start with '-', so no more options. */ 140228753Smm if (cpio->argv[0][0] != '-') 141228753Smm return (-1); 142228753Smm /* "--" marks end of options; consume it and return. */ 143228753Smm if (strcmp(cpio->argv[0], "--") == 0) { 144228753Smm ++cpio->argv; 145228753Smm --cpio->argc; 146228753Smm return (-1); 147228753Smm } 148228753Smm /* Get next word for parsing. */ 149228753Smm opt_word = *cpio->argv++; 150228753Smm --cpio->argc; 151228753Smm if (opt_word[1] == '-') { 152228753Smm /* Set up long option parser. */ 153228753Smm state = state_long; 154228753Smm opt_word += 2; /* Skip leading '--' */ 155228753Smm } else { 156228753Smm /* Set up short option parser. */ 157228753Smm state = state_short; 158228753Smm ++opt_word; /* Skip leading '-' */ 159228753Smm } 160228753Smm } 161228753Smm 162228753Smm /* 163228753Smm * We're parsing a group of POSIX-style single-character options. 164228753Smm */ 165228753Smm if (state == state_short) { 166228753Smm /* Peel next option off of a group of short options. */ 167228753Smm opt = *opt_word++; 168228753Smm if (opt == '\0') { 169228753Smm /* End of this group; recurse to get next option. */ 170228753Smm state = state_next_word; 171228753Smm return cpio_getopt(cpio); 172228753Smm } 173228753Smm 174228753Smm /* Does this option take an argument? */ 175228753Smm p = strchr(short_options, opt); 176228753Smm if (p == NULL) 177228753Smm return ('?'); 178228753Smm if (p[1] == ':') 179228753Smm required = 1; 180228753Smm 181228753Smm /* If it takes an argument, parse that. */ 182228753Smm if (required) { 183228753Smm /* If arg is run-in, opt_word already points to it. */ 184228753Smm if (opt_word[0] == '\0') { 185228753Smm /* Otherwise, pick up the next word. */ 186228753Smm opt_word = *cpio->argv; 187228753Smm if (opt_word == NULL) { 188228753Smm lafe_warnc(0, 189228753Smm "Option -%c requires an argument", 190228753Smm opt); 191228753Smm return ('?'); 192228753Smm } 193228753Smm ++cpio->argv; 194228753Smm --cpio->argc; 195228753Smm } 196228753Smm if (opt == 'W') { 197228753Smm state = state_long; 198228753Smm long_prefix = "-W "; /* For clearer errors. */ 199228753Smm } else { 200228753Smm state = state_next_word; 201232153Smm cpio->argument = opt_word; 202228753Smm } 203228753Smm } 204228753Smm } 205228753Smm 206228753Smm /* We're reading a long option, including -W long=arg convention. */ 207228753Smm if (state == state_long) { 208228753Smm /* After this long option, we'll be starting a new word. */ 209228753Smm state = state_next_word; 210228753Smm 211228753Smm /* Option name ends at '=' if there is one. */ 212228753Smm p = strchr(opt_word, '='); 213228753Smm if (p != NULL) { 214228753Smm optlength = (size_t)(p - opt_word); 215232153Smm cpio->argument = (char *)(uintptr_t)(p + 1); 216228753Smm } else { 217228753Smm optlength = strlen(opt_word); 218228753Smm } 219228753Smm 220228753Smm /* Search the table for an unambiguous match. */ 221228753Smm for (popt = cpio_longopts; popt->name != NULL; popt++) { 222228753Smm /* Short-circuit if first chars don't match. */ 223228753Smm if (popt->name[0] != opt_word[0]) 224228753Smm continue; 225228753Smm /* If option is a prefix of name in table, record it.*/ 226228753Smm if (strncmp(opt_word, popt->name, optlength) == 0) { 227228753Smm match2 = match; /* Record up to two matches. */ 228228753Smm match = popt; 229228753Smm /* If it's an exact match, we're done. */ 230228753Smm if (strlen(popt->name) == optlength) { 231228753Smm match2 = NULL; /* Forget the others. */ 232228753Smm break; 233228753Smm } 234228753Smm } 235228753Smm } 236228753Smm 237228753Smm /* Fail if there wasn't a unique match. */ 238228753Smm if (match == NULL) { 239228753Smm lafe_warnc(0, 240228753Smm "Option %s%s is not supported", 241228753Smm long_prefix, opt_word); 242228753Smm return ('?'); 243228753Smm } 244228753Smm if (match2 != NULL) { 245228753Smm lafe_warnc(0, 246228753Smm "Ambiguous option %s%s (matches --%s and --%s)", 247228753Smm long_prefix, opt_word, match->name, match2->name); 248228753Smm return ('?'); 249228753Smm } 250228753Smm 251228753Smm /* We've found a unique match; does it need an argument? */ 252228753Smm if (match->required) { 253228753Smm /* Argument required: get next word if necessary. */ 254232153Smm if (cpio->argument == NULL) { 255232153Smm cpio->argument = *cpio->argv; 256232153Smm if (cpio->argument == NULL) { 257228753Smm lafe_warnc(0, 258228753Smm "Option %s%s requires an argument", 259228753Smm long_prefix, match->name); 260228753Smm return ('?'); 261228753Smm } 262228753Smm ++cpio->argv; 263228753Smm --cpio->argc; 264228753Smm } 265228753Smm } else { 266228753Smm /* Argument forbidden: fail if there is one. */ 267232153Smm if (cpio->argument != NULL) { 268228753Smm lafe_warnc(0, 269228753Smm "Option %s%s does not allow an argument", 270228753Smm long_prefix, match->name); 271228753Smm return ('?'); 272228753Smm } 273228753Smm } 274228753Smm return (match->equivalent); 275228753Smm } 276228753Smm 277228753Smm return (opt); 278228753Smm} 279228753Smm 280228753Smm 281228753Smm/* 282228753Smm * Parse the argument to the -R or --owner flag. 283228753Smm * 284228753Smm * The format is one of the following: 285228753Smm * <username|uid> - Override user but not group 286228753Smm * <username>: - Override both, group is user's default group 287228753Smm * <uid>: - Override user but not group 288228753Smm * <username|uid>:<groupname|gid> - Override both 289228753Smm * :<groupname|gid> - Override group but not user 290228753Smm * 291228753Smm * Where uid/gid are decimal representations and groupname/username 292228753Smm * are names to be looked up in system database. Note that we try 293228753Smm * to look up an argument as a name first, then try numeric parsing. 294228753Smm * 295228753Smm * A period can be used instead of the colon. 296228753Smm * 297228753Smm * Sets uid/gid return as appropriate, -1 indicates uid/gid not specified. 298228777Smm * TODO: If the spec uses uname/gname, then return those to the caller 299228777Smm * as well. If the spec provides uid/gid, just return names as NULL. 300228753Smm * 301228753Smm * Returns NULL if no error, otherwise returns error string for display. 302228753Smm * 303228753Smm */ 304228753Smmconst char * 305228753Smmowner_parse(const char *spec, int *uid, int *gid) 306228753Smm{ 307228753Smm static char errbuff[128]; 308228753Smm const char *u, *ue, *g; 309228753Smm 310228753Smm *uid = -1; 311228753Smm *gid = -1; 312228753Smm 313228753Smm if (spec[0] == '\0') 314228753Smm return ("Invalid empty user/group spec"); 315228753Smm 316228753Smm /* 317228753Smm * Split spec into [user][:.][group] 318228753Smm * u -> first char of username, NULL if no username 319228753Smm * ue -> first char after username (colon, period, or \0) 320228753Smm * g -> first char of group name 321228753Smm */ 322228753Smm if (*spec == ':' || *spec == '.') { 323228753Smm /* If spec starts with ':' or '.', then just group. */ 324228753Smm ue = u = NULL; 325228753Smm g = spec + 1; 326228753Smm } else { 327228753Smm /* Otherwise, [user] or [user][:] or [user][:][group] */ 328228753Smm ue = u = spec; 329228753Smm while (*ue != ':' && *ue != '.' && *ue != '\0') 330228753Smm ++ue; 331228753Smm g = ue; 332228753Smm if (*g != '\0') /* Skip : or . to find first char of group. */ 333228753Smm ++g; 334228753Smm } 335228753Smm 336228753Smm if (u != NULL) { 337228753Smm /* Look up user: ue is first char after end of user. */ 338228753Smm char *user; 339228753Smm struct passwd *pwent; 340228753Smm 341228753Smm user = (char *)malloc(ue - u + 1); 342228753Smm if (user == NULL) 343228753Smm return ("Couldn't allocate memory"); 344228753Smm memcpy(user, u, ue - u); 345228753Smm user[ue - u] = '\0'; 346228753Smm if ((pwent = getpwnam(user)) != NULL) { 347228753Smm *uid = pwent->pw_uid; 348228753Smm if (*ue != '\0') 349228753Smm *gid = pwent->pw_gid; 350228753Smm } else { 351228753Smm char *end; 352228753Smm errno = 0; 353232153Smm *uid = (int)strtoul(user, &end, 10); 354228753Smm if (errno || *end != '\0') { 355228753Smm snprintf(errbuff, sizeof(errbuff), 356228753Smm "Couldn't lookup user ``%s''", user); 357228753Smm errbuff[sizeof(errbuff) - 1] = '\0'; 358238856Smm free(user); 359228753Smm return (errbuff); 360228753Smm } 361228753Smm } 362228753Smm free(user); 363228753Smm } 364228753Smm 365228753Smm if (*g != '\0') { 366228753Smm struct group *grp; 367228753Smm if ((grp = getgrnam(g)) != NULL) { 368228753Smm *gid = grp->gr_gid; 369228753Smm } else { 370228753Smm char *end; 371228753Smm errno = 0; 372232153Smm *gid = (int)strtoul(g, &end, 10); 373228753Smm if (errno || *end != '\0') { 374228753Smm snprintf(errbuff, sizeof(errbuff), 375228753Smm "Couldn't lookup group ``%s''", g); 376228753Smm errbuff[sizeof(errbuff) - 1] = '\0'; 377228753Smm return (errbuff); 378228753Smm } 379228753Smm } 380228753Smm } 381228753Smm return (NULL); 382228753Smm} 383