1 |
/*- |
2 |
* Copyright (c) 2003-2007 Tim Kientzle |
3 |
* All rights reserved. |
4 |
* |
5 |
* Redistribution and use in source and binary forms, with or without |
6 |
* modification, are permitted provided that the following conditions |
7 |
* are met: |
8 |
* 1. Redistributions of source code must retain the above copyright |
9 |
* notice, this list of conditions and the following disclaimer |
10 |
* in this position and unchanged. |
11 |
* 2. Redistributions in binary form must reproduce the above copyright |
12 |
* notice, this list of conditions and the following disclaimer in the |
13 |
* documentation and/or other materials provided with the distribution. |
14 |
* |
15 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR |
16 |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
17 |
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
18 |
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, |
19 |
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
20 |
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
21 |
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
22 |
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
23 |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
24 |
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
25 |
*/ |
26 |
|
27 |
|
28 |
#include "cpio_platform.h" |
29 |
__FBSDID("$FreeBSD: src/usr.bin/cpio/cmdline.c,v 1.5 2008/12/06 07:30:40 kientzle Exp $"); |
30 |
|
31 |
#ifdef HAVE_ERRNO_H |
32 |
#include <errno.h> |
33 |
#endif |
34 |
#ifdef HAVE_GRP_H |
35 |
#include <grp.h> |
36 |
#endif |
37 |
#ifdef HAVE_PWD_H |
38 |
#include <pwd.h> |
39 |
#endif |
40 |
#include <stdio.h> |
41 |
#ifdef HAVE_STDLIB_H |
42 |
#include <stdlib.h> |
43 |
#endif |
44 |
#ifdef HAVE_STRING_H |
45 |
#include <string.h> |
46 |
#endif |
47 |
|
48 |
#include "cpio.h" |
49 |
#include "err.h" |
50 |
|
51 |
/* |
52 |
* Short options for cpio. Please keep this sorted. |
53 |
*/ |
54 |
static const char *short_options = "0AaBC:cdE:F:f:H:hI:iJjLlmnO:opR:rtuVvW:yZz"; |
55 |
|
56 |
/* |
57 |
* Long options for cpio. Please keep this sorted. |
58 |
*/ |
59 |
static const struct option { |
60 |
const char *name; |
61 |
int required; /* 1 if this option requires an argument */ |
62 |
int equivalent; /* Equivalent short option. */ |
63 |
} cpio_longopts[] = { |
64 |
{ "b64encode", 0, OPTION_B64ENCODE }, |
65 |
{ "create", 0, 'o' }, |
66 |
{ "dot", 0, 'V' }, |
67 |
{ "extract", 0, 'i' }, |
68 |
{ "file", 1, 'F' }, |
69 |
{ "format", 1, 'H' }, |
70 |
{ "grzip", 0, OPTION_GRZIP }, |
71 |
{ "help", 0, 'h' }, |
72 |
{ "insecure", 0, OPTION_INSECURE }, |
73 |
{ "link", 0, 'l' }, |
74 |
{ "list", 0, 't' }, |
75 |
{ "lrzip", 0, OPTION_LRZIP }, |
76 |
{ "lzma", 0, OPTION_LZMA }, |
77 |
{ "lzop", 0, OPTION_LZOP }, |
78 |
{ "make-directories", 0, 'd' }, |
79 |
{ "no-preserve-owner", 0, OPTION_NO_PRESERVE_OWNER }, |
80 |
{ "null", 0, '0' }, |
81 |
{ "numeric-uid-gid", 0, 'n' }, |
82 |
{ "owner", 1, 'R' }, |
83 |
{ "pass-through", 0, 'p' }, |
84 |
{ "preserve-modification-time", 0, 'm' }, |
85 |
{ "preserve-owner", 0, OPTION_PRESERVE_OWNER }, |
86 |
{ "quiet", 0, OPTION_QUIET }, |
87 |
{ "unconditional", 0, 'u' }, |
88 |
{ "uuencode", 0, OPTION_UUENCODE }, |
89 |
{ "verbose", 0, 'v' }, |
90 |
{ "version", 0, OPTION_VERSION }, |
91 |
{ "xz", 0, 'J' }, |
92 |
{ NULL, 0, 0 } |
93 |
}; |
94 |
|
95 |
/* |
96 |
* I used to try to select platform-provided getopt() or |
97 |
* getopt_long(), but that caused a lot of headaches. In particular, |
98 |
* I couldn't consistently use long options in the test harness |
99 |
* because not all platforms have getopt_long(). That in turn led to |
100 |
* overuse of the -W hack in the test harness, which made it rough to |
101 |
* run the test harness against GNU cpio. (I periodically run the |
102 |
* test harness here against GNU cpio as a sanity-check. Yes, |
103 |
* I've found a couple of bugs in GNU cpio that way.) |
104 |
*/ |
105 |
int |
106 |
cpio_getopt(struct cpio *cpio) |
107 |
{ |
108 |
enum { state_start = 0, state_next_word, state_short, state_long }; |
109 |
static int state = state_start; |
110 |
static char *opt_word; |
111 |
|
112 |
const struct option *popt, *match = NULL, *match2 = NULL; |
113 |
const char *p, *long_prefix = "--"; |
114 |
size_t optlength; |
115 |
int opt = '?'; |
116 |
int required = 0; |
117 |
|
118 |
cpio->argument = NULL; |
119 |
|
120 |
/* First time through, initialize everything. */ |
121 |
if (state == state_start) { |
122 |
/* Skip program name. */ |
123 |
++cpio->argv; |
124 |
--cpio->argc; |
125 |
state = state_next_word; |
126 |
} |
127 |
|
128 |
/* |
129 |
* We're ready to look at the next word in argv. |
130 |
*/ |
131 |
if (state == state_next_word) { |
132 |
/* No more arguments, so no more options. */ |
133 |
if (cpio->argv[0] == NULL) |
134 |
return (-1); |
135 |
/* Doesn't start with '-', so no more options. */ |
136 |
if (cpio->argv[0][0] != '-') |
137 |
return (-1); |
138 |
/* "--" marks end of options; consume it and return. */ |
139 |
if (strcmp(cpio->argv[0], "--") == 0) { |
140 |
++cpio->argv; |
141 |
--cpio->argc; |
142 |
return (-1); |
143 |
} |
144 |
/* Get next word for parsing. */ |
145 |
opt_word = *cpio->argv++; |
146 |
--cpio->argc; |
147 |
if (opt_word[1] == '-') { |
148 |
/* Set up long option parser. */ |
149 |
state = state_long; |
150 |
opt_word += 2; /* Skip leading '--' */ |
151 |
} else { |
152 |
/* Set up short option parser. */ |
153 |
state = state_short; |
154 |
++opt_word; /* Skip leading '-' */ |
155 |
} |
156 |
} |
157 |
|
158 |
/* |
159 |
* We're parsing a group of POSIX-style single-character options. |
160 |
*/ |
161 |
if (state == state_short) { |
162 |
/* Peel next option off of a group of short options. */ |
163 |
opt = *opt_word++; |
164 |
if (opt == '\0') { |
165 |
/* End of this group; recurse to get next option. */ |
166 |
state = state_next_word; |
167 |
return cpio_getopt(cpio); |
168 |
} |
169 |
|
170 |
/* Does this option take an argument? */ |
171 |
p = strchr(short_options, opt); |
172 |
if (p == NULL) |
173 |
return ('?'); |
174 |
if (p[1] == ':') |
175 |
required = 1; |
176 |
|
177 |
/* If it takes an argument, parse that. */ |
178 |
if (required) { |
179 |
/* If arg is run-in, opt_word already points to it. */ |
180 |
if (opt_word[0] == '\0') { |
181 |
/* Otherwise, pick up the next word. */ |
182 |
opt_word = *cpio->argv; |
183 |
if (opt_word == NULL) { |
184 |
lafe_warnc(0, |
185 |
"Option -%c requires an argument", |
186 |
opt); |
187 |
return ('?'); |
188 |
} |
189 |
++cpio->argv; |
190 |
--cpio->argc; |
191 |
} |
192 |
if (opt == 'W') { |
193 |
state = state_long; |
194 |
long_prefix = "-W "; /* For clearer errors. */ |
195 |
} else { |
196 |
state = state_next_word; |
197 |
cpio->argument = opt_word; |
198 |
} |
199 |
} |
200 |
} |
201 |
|
202 |
/* We're reading a long option, including -W long=arg convention. */ |
203 |
if (state == state_long) { |
204 |
/* After this long option, we'll be starting a new word. */ |
205 |
state = state_next_word; |
206 |
|
207 |
/* Option name ends at '=' if there is one. */ |
208 |
p = strchr(opt_word, '='); |
209 |
if (p != NULL) { |
210 |
optlength = (size_t)(p - opt_word); |
211 |
cpio->argument = (char *)(uintptr_t)(p + 1); |
212 |
} else { |
213 |
optlength = strlen(opt_word); |
214 |
} |
215 |
|
216 |
/* Search the table for an unambiguous match. */ |
217 |
for (popt = cpio_longopts; popt->name != NULL; popt++) { |
218 |
/* Short-circuit if first chars don't match. */ |
219 |
if (popt->name[0] != opt_word[0]) |
220 |
continue; |
221 |
/* If option is a prefix of name in table, record it.*/ |
222 |
if (strncmp(opt_word, popt->name, optlength) == 0) { |
223 |
match2 = match; /* Record up to two matches. */ |
224 |
match = popt; |
225 |
/* If it's an exact match, we're done. */ |
226 |
if (strlen(popt->name) == optlength) { |
227 |
match2 = NULL; /* Forget the others. */ |
228 |
break; |
229 |
} |
230 |
} |
231 |
} |
232 |
|
233 |
/* Fail if there wasn't a unique match. */ |
234 |
if (match == NULL) { |
235 |
lafe_warnc(0, |
236 |
"Option %s%s is not supported", |
237 |
long_prefix, opt_word); |
238 |
return ('?'); |
239 |
} |
240 |
if (match2 != NULL) { |
241 |
lafe_warnc(0, |
242 |
"Ambiguous option %s%s (matches --%s and --%s)", |
243 |
long_prefix, opt_word, match->name, match2->name); |
244 |
return ('?'); |
245 |
} |
246 |
|
247 |
/* We've found a unique match; does it need an argument? */ |
248 |
if (match->required) { |
249 |
/* Argument required: get next word if necessary. */ |
250 |
if (cpio->argument == NULL) { |
251 |
cpio->argument = *cpio->argv; |
252 |
if (cpio->argument == NULL) { |
253 |
lafe_warnc(0, |
254 |
"Option %s%s requires an argument", |
255 |
long_prefix, match->name); |
256 |
return ('?'); |
257 |
} |
258 |
++cpio->argv; |
259 |
--cpio->argc; |
260 |
} |
261 |
} else { |
262 |
/* Argument forbidden: fail if there is one. */ |
263 |
if (cpio->argument != NULL) { |
264 |
lafe_warnc(0, |
265 |
"Option %s%s does not allow an argument", |
266 |
long_prefix, match->name); |
267 |
return ('?'); |
268 |
} |
269 |
} |
270 |
return (match->equivalent); |
271 |
} |
272 |
|
273 |
return (opt); |
274 |
} |
275 |
|
276 |
|
277 |
/* |
278 |
* Parse the argument to the -R or --owner flag. |
279 |
* |
280 |
* The format is one of the following: |
281 |
* <username|uid> - Override user but not group |
282 |
* <username>: - Override both, group is user's default group |
283 |
* <uid>: - Override user but not group |
284 |
* <username|uid>:<groupname|gid> - Override both |
285 |
* :<groupname|gid> - Override group but not user |
286 |
* |
287 |
* Where uid/gid are decimal representations and groupname/username |
288 |
* are names to be looked up in system database. Note that we try |
289 |
* to look up an argument as a name first, then try numeric parsing. |
290 |
* |
291 |
* A period can be used instead of the colon. |
292 |
* |
293 |
* Sets uid/gid return as appropriate, -1 indicates uid/gid not specified. |
294 |
* TODO: If the spec uses uname/gname, then return those to the caller |
295 |
* as well. If the spec provides uid/gid, just return names as NULL. |
296 |
* |
297 |
* Returns NULL if no error, otherwise returns error string for display. |
298 |
* |
299 |
*/ |
300 |
const char * |
301 |
owner_parse(const char *spec, int *uid, int *gid) |
302 |
{ |
303 |
static char errbuff[128]; |
304 |
const char *u, *ue, *g; |
305 |
|
306 |
*uid = -1; |
307 |
*gid = -1; |
308 |
|
309 |
if (spec[0] == '\0') |
310 |
return ("Invalid empty user/group spec"); |
311 |
|
312 |
/* |
313 |
* Split spec into [user][:.][group] |
314 |
* u -> first char of username, NULL if no username |
315 |
* ue -> first char after username (colon, period, or \0) |
316 |
* g -> first char of group name |
317 |
*/ |
318 |
if (*spec == ':' || *spec == '.') { |
319 |
/* If spec starts with ':' or '.', then just group. */ |
320 |
ue = u = NULL; |
321 |
g = spec + 1; |
322 |
} else { |
323 |
/* Otherwise, [user] or [user][:] or [user][:][group] */ |
324 |
ue = u = spec; |
325 |
while (*ue != ':' && *ue != '.' && *ue != '\0') |
326 |
++ue; |
327 |
g = ue; |
328 |
if (*g != '\0') /* Skip : or . to find first char of group. */ |
329 |
++g; |
330 |
} |
331 |
|
332 |
if (u != NULL) { |
333 |
/* Look up user: ue is first char after end of user. */ |
334 |
char *user; |
335 |
struct passwd *pwent; |
336 |
|
337 |
user = (char *)malloc(ue - u + 1); |
338 |
if (user == NULL) |
339 |
return ("Couldn't allocate memory"); |
340 |
memcpy(user, u, ue - u); |
341 |
user[ue - u] = '\0'; |
342 |
if ((pwent = getpwnam(user)) != NULL) { |
343 |
*uid = pwent->pw_uid; |
344 |
if (*ue != '\0') |
345 |
*gid = pwent->pw_gid; |
346 |
} else { |
347 |
char *end; |
348 |
errno = 0; |
349 |
*uid = (int)strtoul(user, &end, 10); |
350 |
if (errno || *end != '\0') { |
351 |
snprintf(errbuff, sizeof(errbuff), |
352 |
"Couldn't lookup user ``%s''", user); |
353 |
errbuff[sizeof(errbuff) - 1] = '\0'; |
354 |
free(user); |
355 |
return (errbuff); |
356 |
} |
357 |
} |
358 |
free(user); |
359 |
} |
360 |
|
361 |
if (*g != '\0') { |
362 |
struct group *grp; |
363 |
if ((grp = getgrnam(g)) != NULL) { |
364 |
*gid = grp->gr_gid; |
365 |
} else { |
366 |
char *end; |
367 |
errno = 0; |
368 |
*gid = (int)strtoul(g, &end, 10); |
369 |
if (errno || *end != '\0') { |
370 |
snprintf(errbuff, sizeof(errbuff), |
371 |
"Couldn't lookup group ``%s''", g); |
372 |
errbuff[sizeof(errbuff) - 1] = '\0'; |
373 |
return (errbuff); |
374 |
} |
375 |
} |
376 |
} |
377 |
return (NULL); |
378 |
} |