spec.c revision 1.56 1 /* $NetBSD: spec.c,v 1.56 2004/06/20 22:20:18 jmc Exp $ */
2
3 /*-
4 * Copyright (c) 1989, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 /*-
33 * Copyright (c) 2001-2002 The NetBSD Foundation, Inc.
34 * All rights reserved.
35 *
36 * This code is derived from software contributed to The NetBSD Foundation
37 * by Luke Mewburn of Wasabi Systems.
38 *
39 * Redistribution and use in source and binary forms, with or without
40 * modification, are permitted provided that the following conditions
41 * are met:
42 * 1. Redistributions of source code must retain the above copyright
43 * notice, this list of conditions and the following disclaimer.
44 * 2. Redistributions in binary form must reproduce the above copyright
45 * notice, this list of conditions and the following disclaimer in the
46 * documentation and/or other materials provided with the distribution.
47 * 3. All advertising materials mentioning features or use of this software
48 * must display the following acknowledgement:
49 * This product includes software developed by the NetBSD
50 * Foundation, Inc. and its contributors.
51 * 4. Neither the name of The NetBSD Foundation nor the names of its
52 * contributors may be used to endorse or promote products derived
53 * from this software without specific prior written permission.
54 *
55 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
56 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
57 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
58 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
59 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
60 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
61 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
62 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
63 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
64 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
65 * POSSIBILITY OF SUCH DAMAGE.
66 */
67
68 #if HAVE_NBTOOL_CONFIG_H
69 #include "nbtool_config.h"
70 #endif
71
72 #include <sys/cdefs.h>
73 #if defined(__RCSID) && !defined(lint)
74 #if 0
75 static char sccsid[] = "@(#)spec.c 8.2 (Berkeley) 4/28/95";
76 #else
77 __RCSID("$NetBSD: spec.c,v 1.56 2004/06/20 22:20:18 jmc Exp $");
78 #endif
79 #endif /* not lint */
80
81 #include <sys/param.h>
82 #include <sys/stat.h>
83
84 #include <ctype.h>
85 #include <errno.h>
86 #include <grp.h>
87 #include <pwd.h>
88 #include <stdio.h>
89 #include <stdlib.h>
90 #include <string.h>
91 #include <unistd.h>
92 #include <vis.h>
93 #include <util.h>
94
95 #include "extern.h"
96 #include "pack_dev.h"
97
98 size_t mtree_lineno; /* Current spec line number */
99 int Wflag; /* Don't "whack" permissions */
100
101 static dev_t parsedev(char *);
102 static void replacenode(NODE *, NODE *);
103 static void set(char *, NODE *);
104 static void unset(char *, NODE *);
105
106 NODE *
107 spec(FILE *fp)
108 {
109 NODE *centry, *last, *pathparent, *cur;
110 char *p, *e, *next;
111 NODE ginfo, *root;
112 char *buf, *tname, *ntname;
113 size_t tnamelen, plen;
114
115 root = NULL;
116 centry = last = NULL;
117 tname = NULL;
118 tnamelen = 0;
119 memset(&ginfo, 0, sizeof(ginfo));
120 for (mtree_lineno = 0;
121 (buf = fparseln(fp, NULL, &mtree_lineno, NULL,
122 FPARSELN_UNESCCOMM | FPARSELN_UNESCCONT | FPARSELN_UNESCESC));
123 free(buf)) {
124 /* Skip leading whitespace. */
125 for (p = buf; *p && isspace((unsigned char)*p); ++p)
126 continue;
127
128 /* If nothing but whitespace, continue. */
129 if (!*p)
130 continue;
131
132 #ifdef DEBUG
133 fprintf(stderr, "line %lu: {%s}\n",
134 (u_long)mtree_lineno, p);
135 #endif
136 /* Grab file name, "$", "set", or "unset". */
137 next = buf;
138 while ((p = strsep(&next, " \t")) != NULL && *p == '\0')
139 continue;
140 if (p == NULL)
141 mtree_err("missing field");
142
143 if (p[0] == '/') {
144 if (strcmp(p + 1, "set") == 0)
145 set(next, &ginfo);
146 else if (strcmp(p + 1, "unset") == 0)
147 unset(next, &ginfo);
148 else
149 mtree_err("invalid specification `%s'", p);
150 continue;
151 }
152
153 if (strcmp(p, "..") == 0) {
154 /* Don't go up, if haven't gone down. */
155 if (root == NULL)
156 goto noparent;
157 if (last->type != F_DIR || last->flags & F_DONE) {
158 if (last == root)
159 goto noparent;
160 last = last->parent;
161 }
162 last->flags |= F_DONE;
163 continue;
164
165 noparent: mtree_err("no parent node");
166 }
167
168 plen = strlen(p) + 1;
169 if (plen > tnamelen) {
170 if ((ntname = realloc(tname, plen)) == NULL)
171 mtree_err("realloc: %s", strerror(errno));
172 tname = ntname;
173 tnamelen = plen;
174 }
175 if (strunvis(tname, p) == -1)
176 mtree_err("strunvis failed on `%s'", p);
177 p = tname;
178
179 pathparent = NULL;
180 if (strchr(p, '/') != NULL) {
181 cur = root;
182 for (; (e = strchr(p, '/')) != NULL; p = e+1) {
183 if (p == e)
184 continue; /* handle // */
185 *e = '\0';
186 if (strcmp(p, ".") != 0) {
187 while (cur &&
188 strcmp(cur->name, p) != 0) {
189 cur = cur->next;
190 }
191 }
192 if (cur == NULL || cur->type != F_DIR) {
193 mtree_err("%s: %s", tname,
194 strerror(ENOENT));
195 }
196 *e = '/';
197 pathparent = cur;
198 cur = cur->child;
199 }
200 if (*p == '\0')
201 mtree_err("%s: empty leaf element", tname);
202 }
203
204 if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL)
205 mtree_err("%s", strerror(errno));
206 *centry = ginfo;
207 centry->lineno = mtree_lineno;
208 strcpy(centry->name, p);
209 #define MAGIC "?*["
210 if (strpbrk(p, MAGIC))
211 centry->flags |= F_MAGIC;
212 set(next, centry);
213
214 if (root == NULL) {
215 /*
216 * empty tree
217 */
218 if (strcmp(centry->name, ".") != 0 ||
219 centry->type != F_DIR)
220 mtree_err(
221 "root node must be the directory `.'");
222 last = root = centry;
223 root->parent = root;
224 } else if (pathparent != NULL) {
225 /*
226 * full path entry
227 */
228 centry->parent = pathparent;
229 cur = pathparent->child;
230 if (cur == NULL) {
231 pathparent->child = centry;
232 last = centry;
233 } else {
234 for (; cur != NULL; cur = cur->next) {
235 if (strcmp(cur->name, centry->name)
236 == 0) {
237 /* existing entry; replace */
238 replacenode(cur, centry);
239 break;
240 }
241 if (cur->next == NULL) {
242 /* last entry; add new */
243 cur->next = centry;
244 centry->prev = cur;
245 break;
246 }
247 }
248 last = cur;
249 while (last->next != NULL)
250 last = last->next;
251 }
252 } else if (strcmp(centry->name, ".") == 0) {
253 /*
254 * duplicate "." entry; always replace
255 */
256 replacenode(root, centry);
257 } else if (last->type == F_DIR && !(last->flags & F_DONE)) {
258 /*
259 * new relative child
260 * (no duplicate check)
261 */
262 centry->parent = last;
263 last = last->child = centry;
264 } else {
265 /*
266 * relative entry, up one directory
267 * (no duplicate check)
268 */
269 centry->parent = last->parent;
270 centry->prev = last;
271 last = last->next = centry;
272 }
273 }
274 return (root);
275 }
276
277 /*
278 * dump_nodes --
279 * dump the NODEs from `cur', based in the directory `dir'.
280 * if pathlast is none zero, print the path last, otherwise print
281 * it first.
282 */
283 void
284 dump_nodes(const char *dir, NODE *root, int pathlast)
285 {
286 NODE *cur;
287 char path[MAXPATHLEN];
288 const char *name;
289
290 for (cur = root; cur != NULL; cur = cur->next) {
291 if (cur->type != F_DIR && !matchtags(cur))
292 continue;
293
294 if (snprintf(path, sizeof(path), "%s%s%s",
295 dir, *dir ? "/" : "", cur->name)
296 >= sizeof(path))
297 mtree_err("Pathname too long.");
298
299 if (!pathlast)
300 printf("%s ", vispath(path));
301
302 #define MATCHFLAG(f) ((keys & (f)) && (cur->flags & (f)))
303 if (MATCHFLAG(F_TYPE))
304 printf("type=%s ", nodetype(cur->type));
305 if (MATCHFLAG(F_UID | F_UNAME)) {
306 if (keys & F_UNAME &&
307 (name = user_from_uid(cur->st_uid, 1)) != NULL)
308 printf("uname=%s ", name);
309 else
310 printf("uid=%u ", cur->st_uid);
311 }
312 if (MATCHFLAG(F_GID | F_GNAME)) {
313 if (keys & F_GNAME &&
314 (name = group_from_gid(cur->st_gid, 1)) != NULL)
315 printf("gname=%s ", name);
316 else
317 printf("gid=%u ", cur->st_gid);
318 }
319 if (MATCHFLAG(F_MODE))
320 printf("mode=%#o ", cur->st_mode);
321 if (MATCHFLAG(F_DEV) &&
322 (cur->type == F_BLOCK || cur->type == F_CHAR))
323 printf("device=%#x ", cur->st_rdev);
324 if (MATCHFLAG(F_NLINK))
325 printf("nlink=%d ", cur->st_nlink);
326 if (MATCHFLAG(F_SLINK))
327 printf("link=%s ", cur->slink);
328 if (MATCHFLAG(F_SIZE))
329 printf("size=%lld ", (long long)cur->st_size);
330 if (MATCHFLAG(F_TIME))
331 printf("time=%ld.%ld ", (long)cur->st_mtimespec.tv_sec,
332 cur->st_mtimespec.tv_nsec);
333 if (MATCHFLAG(F_CKSUM))
334 printf("cksum=%lu ", cur->cksum);
335 if (MATCHFLAG(F_MD5))
336 printf("md5=%s ", cur->md5digest);
337 if (MATCHFLAG(F_RMD160))
338 printf("rmd160=%s ", cur->rmd160digest);
339 if (MATCHFLAG(F_SHA1))
340 printf("sha1=%s ", cur->sha1digest);
341 if (MATCHFLAG(F_FLAGS))
342 printf("flags=%s ",
343 flags_to_string(cur->st_flags, "none"));
344 if (MATCHFLAG(F_IGN))
345 printf("ignore ");
346 if (MATCHFLAG(F_OPT))
347 printf("optional ");
348 if (MATCHFLAG(F_TAGS))
349 printf("tags=%s ", cur->tags);
350 puts(pathlast ? vispath(path) : "");
351
352 if (cur->child)
353 dump_nodes(path, cur->child, pathlast);
354 }
355 }
356
357 /*
358 * vispath --
359 * strsvis(3) encodes path, which must not be longer than MAXPATHLEN
360 * characters long, and returns a pointer to a static buffer containing
361 * the result.
362 */
363 char *
364 vispath(const char *path)
365 {
366 const char extra[] = { ' ', '\t', '\n', '\\', '#', '\0' };
367 static char pathbuf[4*MAXPATHLEN + 1];
368
369 strsvis(pathbuf, path, VIS_CSTYLE, extra);
370 return(pathbuf);
371 }
372
373
374 static dev_t
375 parsedev(char *arg)
376 {
377 #define MAX_PACK_ARGS 3
378 u_long numbers[MAX_PACK_ARGS];
379 char *p, *ep, *dev;
380 int argc;
381 pack_t *pack;
382 dev_t result;
383 const char *error = NULL;
384
385 if ((dev = strchr(arg, ',')) != NULL) {
386 *dev++='\0';
387 if ((pack = pack_find(arg)) == NULL)
388 mtree_err("unknown format `%s'", arg);
389 argc = 0;
390 while ((p = strsep(&dev, ",")) != NULL) {
391 if (*p == '\0')
392 mtree_err("missing number");
393 numbers[argc++] = strtoul(p, &ep, 0);
394 if (*ep != '\0')
395 mtree_err("invalid number `%s'",
396 p);
397 if (argc > MAX_PACK_ARGS)
398 mtree_err("too many arguments");
399 }
400 if (argc < 2)
401 mtree_err("not enough arguments");
402 result = (*pack)(argc, numbers, &error);
403 if (error != NULL)
404 mtree_err(error);
405 } else {
406 result = (dev_t)strtoul(arg, &ep, 0);
407 if (*ep != '\0')
408 mtree_err("invalid device `%s'", arg);
409 }
410 return (result);
411 }
412
413 static void
414 replacenode(NODE *cur, NODE *new)
415 {
416
417 if (cur->type != new->type)
418 mtree_err("existing entry for `%s', type `%s' does not match type `%s'",
419 cur->name, nodetype(cur->type), nodetype(new->type));
420 #define REPLACE(x) cur->x = new->x
421 #define REPLACESTR(x) if (cur->x) free(cur->x); cur->x = new->x
422
423 REPLACE(st_size);
424 REPLACE(st_mtimespec);
425 REPLACESTR(slink);
426 REPLACE(st_uid);
427 REPLACE(st_gid);
428 REPLACE(st_mode);
429 REPLACE(st_rdev);
430 REPLACE(st_flags);
431 REPLACE(st_nlink);
432 REPLACE(cksum);
433 REPLACESTR(md5digest);
434 REPLACESTR(rmd160digest);
435 REPLACESTR(sha1digest);
436 REPLACESTR(tags);
437 REPLACE(lineno);
438 REPLACE(flags);
439 free(new);
440 }
441
442 static void
443 set(char *t, NODE *ip)
444 {
445 int type, value, len;
446 gid_t gid;
447 uid_t uid;
448 char *kw, *val, *md, *ep;
449 void *m;
450
451 val = NULL;
452 while ((kw = strsep(&t, "= \t")) != NULL) {
453 if (*kw == '\0')
454 continue;
455 if (strcmp(kw, "all") == 0)
456 mtree_err("invalid keyword `all'");
457 ip->flags |= type = parsekey(kw, &value);
458 if (value) {
459 while ((val = strsep(&t, " \t")) != NULL &&
460 *val == '\0')
461 continue;
462 if (val == NULL)
463 mtree_err("missing value");
464 }
465 switch(type) {
466 case F_CKSUM:
467 ip->cksum = strtoul(val, &ep, 10);
468 if (*ep)
469 mtree_err("invalid checksum `%s'", val);
470 break;
471 case F_DEV:
472 ip->st_rdev = parsedev(val);
473 break;
474 case F_FLAGS:
475 if (strcmp("none", val) == 0)
476 ip->st_flags = 0;
477 else if (string_to_flags(&val, &ip->st_flags, NULL)
478 != 0)
479 mtree_err("invalid flag `%s'", val);
480 break;
481 case F_GID:
482 ip->st_gid = (gid_t)strtoul(val, &ep, 10);
483 if (*ep)
484 mtree_err("invalid gid `%s'", val);
485 break;
486 case F_GNAME:
487 if (Wflag) /* don't parse if whacking */
488 break;
489 if (gid_from_group(val, &gid) == -1)
490 mtree_err("unknown group `%s'", val);
491 ip->st_gid = gid;
492 break;
493 case F_IGN:
494 /* just set flag bit */
495 break;
496 case F_MD5:
497 if (val[0]=='0' && val[1]=='x')
498 md=&val[2];
499 else
500 md=val;
501 if ((ip->md5digest = strdup(md)) == NULL)
502 mtree_err("memory allocation error");
503 break;
504 case F_MODE:
505 if ((m = setmode(val)) == NULL)
506 mtree_err("invalid file mode `%s'", val);
507 ip->st_mode = getmode(m, 0);
508 free(m);
509 break;
510 case F_NLINK:
511 ip->st_nlink = (nlink_t)strtoul(val, &ep, 10);
512 if (*ep)
513 mtree_err("invalid link count `%s'", val);
514 break;
515 case F_OPT:
516 /* just set flag bit */
517 break;
518 case F_RMD160:
519 if (val[0]=='0' && val[1]=='x')
520 md=&val[2];
521 else
522 md=val;
523 if ((ip->rmd160digest = strdup(md)) == NULL)
524 mtree_err("memory allocation error");
525 break;
526 case F_SHA1:
527 if (val[0]=='0' && val[1]=='x')
528 md=&val[2];
529 else
530 md=val;
531 if ((ip->sha1digest = strdup(md)) == NULL)
532 mtree_err("memory allocation error");
533 break;
534 case F_SIZE:
535 ip->st_size = (off_t)strtoll(val, &ep, 10);
536 if (*ep)
537 mtree_err("invalid size `%s'", val);
538 break;
539 case F_SLINK:
540 if ((ip->slink = strdup(val)) == NULL)
541 mtree_err("memory allocation error");
542 break;
543 case F_TAGS:
544 len = strlen(val) + 3; /* "," + str + ",\0" */
545 if ((ip->tags = malloc(len)) == NULL)
546 mtree_err("memory allocation error");
547 snprintf(ip->tags, len, ",%s,", val);
548 break;
549 case F_TIME:
550 ip->st_mtimespec.tv_sec =
551 (time_t)strtoul(val, &ep, 10);
552 if (*ep != '.')
553 mtree_err("invalid time `%s'", val);
554 val = ep + 1;
555 ip->st_mtimespec.tv_nsec = strtoul(val, &ep, 10);
556 if (*ep)
557 mtree_err("invalid time `%s'", val);
558 break;
559 case F_TYPE:
560 ip->type = parsetype(val);
561 break;
562 case F_UID:
563 ip->st_uid = (uid_t)strtoul(val, &ep, 10);
564 if (*ep)
565 mtree_err("invalid uid `%s'", val);
566 break;
567 case F_UNAME:
568 if (Wflag) /* don't parse if whacking */
569 break;
570 if (uid_from_user(val, &uid) == -1)
571 mtree_err("unknown user `%s'", val);
572 ip->st_uid = uid;
573 break;
574 default:
575 mtree_err(
576 "set(): unsupported key type 0x%x (INTERNAL ERROR)",
577 type);
578 /* NOTREACHED */
579 }
580 }
581 }
582
583 static void
584 unset(char *t, NODE *ip)
585 {
586 char *p;
587
588 while ((p = strsep(&t, " \t")) != NULL) {
589 if (*p == '\0')
590 continue;
591 ip->flags &= ~parsekey(p, NULL);
592 }
593 }
594