file_cmds-321.100.10.0.1.tar.gz
[apple/file_cmds.git] / mtree / spec.c
1 /*-
2 * Copyright (c) 1989, 1993
3 * The Regents of the University of California. 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 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 #if 0
31 #ifndef lint
32 static char sccsid[] = "@(#)spec.c 8.1 (Berkeley) 6/6/93";
33 #endif /* not lint */
34 #endif
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD: src/usr.sbin/mtree/spec.c,v 1.22 2005/03/29 11:44:17 tobez Exp $");
37
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <ctype.h>
41 #include <err.h>
42 #include <errno.h>
43 #include <fts.h>
44 #include <grp.h>
45 #include <pwd.h>
46 #include <stdio.h>
47 #include <stdint.h>
48 #include <unistd.h>
49 #include <vis.h>
50 #include "metrics.h"
51 #include "mtree.h"
52 #include "extern.h"
53
54 int lineno; /* Current spec line number. */
55
56 static void set(char *, NODE *);
57 static void unset(char *, NODE *);
58
59 NODE *
60 mtree_readspec(FILE *fi)
61 {
62 NODE *centry, *last;
63 char *p;
64 NODE ginfo, *root;
65 int c_cur, c_next;
66 char buf[2048];
67
68 centry = last = root = NULL;
69 bzero(&ginfo, sizeof(ginfo));
70 c_cur = c_next = 0;
71 for (lineno = 1; fgets(buf, sizeof(buf), fi);
72 ++lineno, c_cur = c_next, c_next = 0) {
73 /* Skip empty lines. */
74 if (buf[0] == '\n')
75 continue;
76
77 /* Find end of line. */
78 if ((p = index(buf, '\n')) == NULL) {
79 RECORD_FAILURE(21, ERANGE);
80 errx(1, "line %d too long", lineno);
81 }
82
83 /* See if next line is continuation line. */
84 if (p[-1] == '\\') {
85 --p;
86 c_next = 1;
87 }
88
89 /* Null-terminate the line. */
90 *p = '\0';
91
92 /* Skip leading whitespace. */
93 for (p = buf; *p && isspace(*p); ++p);
94
95 /* If nothing but whitespace or comment char, continue. */
96 if (!*p || *p == '#')
97 continue;
98
99 #ifdef DEBUG
100 (void)fprintf(stderr, "line %d: {%s}\n", lineno, p);
101 #endif
102 if (c_cur) {
103 set(p, centry);
104 continue;
105 }
106
107 /* Grab file name, "$", "set", or "unset". */
108 if ((p = strtok(p, "\n\t ")) == NULL) {
109 RECORD_FAILURE(22, EINVAL);
110 errx(1, "line %d: missing field", lineno);
111 }
112
113 if (p[0] == '/')
114 switch(p[1]) {
115 case 's':
116 if (strcmp(p + 1, "set"))
117 break;
118 set(NULL, &ginfo);
119 continue;
120 case 'u':
121 if (strcmp(p + 1, "unset"))
122 break;
123 unset(NULL, &ginfo);
124 continue;
125 }
126
127 if (index(p, '/')) {
128 RECORD_FAILURE(23, EINVAL);
129 errx(1, "line %d: slash character in file name",
130 lineno);
131 }
132
133 if (!strcmp(p, "..")) {
134 /* Don't go up, if haven't gone down. */
135 if (!root)
136 goto noparent;
137 if (last->type != F_DIR || last->flags & F_DONE) {
138 if (last == root)
139 goto noparent;
140 last = last->parent;
141 }
142 last->flags |= F_DONE;
143 continue;
144
145 noparent: RECORD_FAILURE(24, EINVAL);
146 errx(1, "line %d: no parent node", lineno);
147 }
148
149 if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL) {
150 RECORD_FAILURE(25, ENOMEM);
151 errx(1, "calloc");
152 }
153 *centry = ginfo;
154 #define MAGIC "?*["
155 if (strpbrk(p, MAGIC))
156 centry->flags |= F_MAGIC;
157 if (strunvis(centry->name, p) == -1) {
158 RECORD_FAILURE(26, EILSEQ);
159 errx(1, "filename %s is ill-encoded", p);
160 }
161 set(NULL, centry);
162
163 if (!root) {
164 last = root = centry;
165 root->parent = root;
166 } else if (last->type == F_DIR && !(last->flags & F_DONE)) {
167 centry->parent = last;
168 last = last->child = centry;
169 } else {
170 centry->parent = last->parent;
171 centry->prev = last;
172 last = last->next = centry;
173 }
174 }
175 return (root);
176 }
177
178 static void
179 set(char *t, NODE *ip)
180 {
181 int error = 0;
182 int type;
183 char *kw, *val = NULL;
184 struct group *gr;
185 struct passwd *pw;
186 mode_t *m;
187 int value;
188 char *ep;
189 char *l;
190
191 for (; (kw = strtok(t, "= \t\n")); t = NULL) {
192 ip->flags |= type = parsekey(kw, &value);
193 if ((value == 0) || (val = strtok(NULL, " \t\n")) == NULL) {
194 RECORD_FAILURE(27, EINVAL);
195 errx(1, "line %d: missing value", lineno);
196 }
197 switch(type) {
198 case F_CKSUM:
199 ip->cksum = strtoul(val, &ep, 10);
200 if (*ep) {
201 RECORD_FAILURE(28, EINVAL);
202 errx(1, "line %d: invalid checksum %s",
203 lineno, val);
204 }
205 break;
206 case F_MD5:
207 ip->md5digest = strdup(val);
208 if (!ip->md5digest) {
209 RECORD_FAILURE(29, ENOMEM);
210 errx(1, "strdup");
211 }
212 break;
213 case F_SHA1:
214 ip->sha1digest = strdup(val);
215 if (!ip->sha1digest) {
216 RECORD_FAILURE(30, ENOMEM);
217 errx(1, "strdup");
218 }
219 break;
220 case F_SHA256:
221 ip->sha256digest = strdup(val);
222 if (!ip->sha256digest) {
223 RECORD_FAILURE(31, ENOMEM);
224 errx(1, "strdup");
225 }
226 break;
227 case F_RMD160:
228 ip->rmd160digest = strdup(val);
229 if (!ip->rmd160digest) {
230 RECORD_FAILURE(32, ENOMEM);
231 errx(1, "strdup");
232 }
233 break;
234 case F_FLAGS:
235 if (strcmp("none", val) == 0) {
236 ip->st_flags = 0;
237 } else if (strtofflags(&val, &ip->st_flags, NULL) != 0) {
238 RECORD_FAILURE(33, EINVAL);
239 errx(1, "line %d: invalid flag %s",lineno, val);
240 }
241 break;
242 case F_GID:
243 ip->st_gid = (gid_t)strtoul(val, &ep, 10);
244 if (*ep) {
245 RECORD_FAILURE(34, EINVAL);
246 errx(1, "line %d: invalid gid %s", lineno, val);
247 }
248 break;
249 case F_GNAME:
250 if ((gr = getgrnam(val)) == NULL) {
251 RECORD_FAILURE(35, EINVAL);
252 errx(1, "line %d: unknown group %s", lineno, val);
253 }
254 ip->st_gid = gr->gr_gid;
255 break;
256 case F_IGN:
257 /* just set flag bit */
258 break;
259 case F_MODE:
260 if ((m = setmode(val)) == NULL) {
261 RECORD_FAILURE(36, EINVAL);
262 errx(1, "line %d: invalid file mode %s",
263 lineno, val);
264 }
265 ip->st_mode = getmode(m, 0);
266 free(m);
267 break;
268 case F_NLINK:
269 ip->st_nlink = strtoul(val, &ep, 10);
270 if (*ep) {
271 RECORD_FAILURE(37, EINVAL);
272 errx(1, "line %d: invalid link count %s",
273 lineno, val);
274 }
275 break;
276 case F_SIZE:
277 ip->st_size = strtoq(val, &ep, 10);
278 if (*ep) {
279 RECORD_FAILURE(38, EINVAL);
280 errx(1, "line %d: invalid size %s",
281 lineno, val);
282 }
283 break;
284 case F_SLINK:
285 ip->slink = malloc(strlen(val) + 1);
286 if (ip->slink == NULL) {
287 RECORD_FAILURE(39, ENOMEM);
288 errx(1, "malloc");
289 }
290 if (strunvis(ip->slink, val) == -1) {
291 RECORD_FAILURE(40, EILSEQ);
292 errx(1, "symlink %s is ill-encoded", val);
293 }
294 break;
295 case F_TIME:
296 ip->st_mtimespec.tv_sec = strtoul(val, &ep, 10);
297 if (*ep != '.') {
298 RECORD_FAILURE(41, EINVAL);
299 errx(1, "line %d: invalid time %s",
300 lineno, val);
301 }
302 val = ep + 1;
303 ip->st_mtimespec.tv_nsec = strtoul(val, &ep, 10);
304 if (*ep) {
305 RECORD_FAILURE(42, EINVAL);
306 errx(1, "line %d: invalid time %s",
307 lineno, val);
308 }
309 break;
310 case F_TYPE:
311 switch(*val) {
312 case 'b':
313 if (!strcmp(val, "block"))
314 ip->type = F_BLOCK;
315 break;
316 case 'c':
317 if (!strcmp(val, "char"))
318 ip->type = F_CHAR;
319 break;
320 case 'd':
321 if (!strcmp(val, "dir"))
322 ip->type = F_DIR;
323 break;
324 case 'f':
325 if (!strcmp(val, "file"))
326 ip->type = F_FILE;
327 if (!strcmp(val, "fifo"))
328 ip->type = F_FIFO;
329 break;
330 case 'l':
331 if (!strcmp(val, "link"))
332 ip->type = F_LINK;
333 break;
334 case 's':
335 if (!strcmp(val, "socket"))
336 ip->type = F_SOCK;
337 break;
338 default:
339 RECORD_FAILURE(43, EINVAL);
340 errx(1, "line %d: unknown file type %s",
341 lineno, val);
342 }
343 break;
344 case F_UID:
345 ip->st_uid = (uid_t)strtoul(val, &ep, 10);
346 if (*ep) {
347 RECORD_FAILURE(44, EINVAL);
348 errx(1, "line %d: invalid uid %s", lineno, val);
349 }
350 break;
351 case F_UNAME:
352 if ((pw = getpwnam(val)) == NULL) {
353 RECORD_FAILURE(45, EINVAL);
354 errx(1, "line %d: unknown user %s", lineno, val);
355 }
356 ip->st_uid = pw->pw_uid;
357 break;
358 case F_BTIME:
359 ip->st_birthtimespec.tv_sec = strtoul(val, &ep, 10);
360 if (*ep != '.') {
361 RECORD_FAILURE(46, EINVAL);
362 errx(1, "line %d: invalid time %s",
363 lineno, val);
364 }
365 val = ep + 1;
366 ip->st_birthtimespec.tv_nsec = strtoul(val, &ep, 10);
367 if (*ep) {
368 RECORD_FAILURE(47, EINVAL);
369 errx(1, "line %d: invalid time %s",
370 lineno, val);
371 }
372 break;
373 case F_ATIME:
374 ip->st_atimespec.tv_sec = strtoul(val, &ep, 10);
375 if (*ep != '.') {
376 RECORD_FAILURE(48, EINVAL);
377 errx(1, "line %d: invalid time %s",
378 lineno, val);
379 }
380 val = ep + 1;
381 ip->st_atimespec.tv_nsec = strtoul(val, &ep, 10);
382 if (*ep) {
383 RECORD_FAILURE(49, EINVAL);
384 errx(1, "line %d: invalid time %s",
385 lineno, val);
386 }
387 break;
388 case F_CTIME:
389 ip->st_ctimespec.tv_sec = strtoul(val, &ep, 10);
390 if (*ep != '.') {
391 RECORD_FAILURE(50, EINVAL);
392 errx(1, "line %d: invalid time %s",
393 lineno, val);
394 }
395 val = ep + 1;
396 ip->st_ctimespec.tv_nsec = strtoul(val, &ep, 10);
397 if (*ep) {
398 RECORD_FAILURE(51, EINVAL);
399 errx(1, "line %d: invalid time %s",
400 lineno, val);
401 }
402 break;
403 case F_PTIME:
404 ip->st_ptimespec.tv_sec = strtoul(val, &ep, 10);
405 if (*ep != '.') {
406 RECORD_FAILURE(52, EINVAL);
407 errx(1, "line %d: invalid time %s",
408 lineno, val);
409 }
410 val = ep + 1;
411 ip->st_ptimespec.tv_nsec = strtoul(val, &ep, 10);
412 if (*ep) {
413 RECORD_FAILURE(53, EINVAL);
414 errx(1, "line %d: invalid time %s",
415 lineno, val);
416 }
417 break;
418 case F_XATTRS:
419 /*
420 * Note this is nested inside an strtok loop,
421 * strtok_r must be used to preserve the strtok context
422 * of the loop.
423 */
424 ep = strtok_r(val,".", &l);
425 ip->xattrsdigest = strdup(ep);
426 if (!ip->xattrsdigest) {
427 error = errno;
428 RECORD_FAILURE(54, error);
429 errc(1, error, "strdup");
430 }
431 val = strtok_r(NULL,".", &l);
432 if (val) {
433 ip->xdstream_priv_id = strtoull(val, &ep, 10);
434 if (*ep) {
435 RECORD_FAILURE(55, EINVAL);
436 errx(1, "line %d: invalid private id %s",
437 lineno, val);
438 }
439 } else {
440 ip->xdstream_priv_id = 0;
441 }
442 break;
443 case F_INODE:
444 ip->st_ino = (ino_t)strtoull(val, &ep, 10);
445 if (*ep) {
446 RECORD_FAILURE(56, EINVAL);
447 errx(1, "line %d: invalid inode %s",
448 lineno, val);
449 }
450 break;
451 case F_ACL:
452 ip->acldigest = strdup(val);
453 if (!ip->acldigest) {
454 error = errno;
455 RECORD_FAILURE(57, error);
456 errc(1, error, "strdup");
457 }
458 break;
459 case F_SIBLINGID:
460 ip->sibling_id = (quad_t)strtoull(val, &ep, 10);
461 if (*ep) {
462 RECORD_FAILURE(58, EINVAL);
463 errx(1, "line %d: invalid sibling id %s", lineno, val);
464 }
465 }
466 }
467 }
468
469 static void
470 unset(char *t, NODE *ip)
471 {
472 char *p;
473
474 while ((p = strtok(t, "\n\t ")))
475 ip->flags &= ~parsekey(p, NULL);
476 }