spec.c revision 1.51 1 /* $NetBSD: spec.c,v 1.51 2003/09/19 06:15:55 itojun 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 #include <sys/cdefs.h>
69 #if defined(__RCSID) && !defined(lint)
70 #if 0
71 static char sccsid[] = "@(#)spec.c 8.2 (Berkeley) 4/28/95";
72 #else
73 __RCSID("$NetBSD: spec.c,v 1.51 2003/09/19 06:15:55 itojun Exp $");
74 #endif
75 #endif /* not lint */
76
77 #include <sys/param.h>
78 #include <sys/stat.h>
79
80 #include <ctype.h>
81 #include <errno.h>
82 #include <grp.h>
83 #include <pwd.h>
84 #include <stdio.h>
85 #include <stdlib.h>
86 #include <string.h>
87 #include <unistd.h>
88 #include <vis.h>
89
90 #include "extern.h"
91 #include "pack_dev.h"
92
93 size_t mtree_lineno; /* Current spec line number */
94 int Wflag; /* Don't "whack" permissions */
95
96 static dev_t parsedev(char *);
97 static void replacenode(NODE *, NODE *);
98 static void set(char *, NODE *);
99 static void unset(char *, NODE *);
100
101 NODE *
102 spec(FILE *fp)
103 {
104 NODE *centry, *last, *pathparent, *cur;
105 char *p, *e, *next;
106 NODE ginfo, *root;
107 char *buf, *tname, *ntname;
108 size_t tnamelen, plen;
109
110 root = NULL;
111 centry = last = NULL;
112 tname = NULL;
113 tnamelen = 0;
114 memset(&ginfo, 0, sizeof(ginfo));
115 for (mtree_lineno = 0;
116 (buf = fparseln(fp, NULL, &mtree_lineno, NULL,
117 FPARSELN_UNESCCOMM | FPARSELN_UNESCCONT | FPARSELN_UNESCESC));
118 free(buf)) {
119 /* Skip leading whitespace. */
120 for (p = buf; *p && isspace((unsigned char)*p); ++p)
121 continue;
122
123 /* If nothing but whitespace, continue. */
124 if (!*p)
125 continue;
126
127 #ifdef DEBUG
128 fprintf(stderr, "line %lu: {%s}\n",
129 (u_long)mtree_lineno, p);
130 #endif
131 /* Grab file name, "$", "set", or "unset". */
132 next = buf;
133 while ((p = strsep(&next, " \t")) != NULL && *p == '\0')
134 continue;
135 if (p == NULL)
136 mtree_err("missing field");
137
138 if (p[0] == '/') {
139 if (strcmp(p + 1, "set") == 0)
140 set(next, &ginfo);
141 else if (strcmp(p + 1, "unset") == 0)
142 unset(next, &ginfo);
143 else
144 mtree_err("invalid specification `%s'", p);
145 continue;
146 }
147
148 if (strcmp(p, "..") == 0) {
149 /* Don't go up, if haven't gone down. */
150 if (root == NULL)
151 goto noparent;
152 if (last->type != F_DIR || last->flags & F_DONE) {
153 if (last == root)
154 goto noparent;
155 last = last->parent;
156 }
157 last->flags |= F_DONE;
158 continue;
159
160 noparent: mtree_err("no parent node");
161 }
162
163 plen = strlen(p) + 1;
164 if (plen > tnamelen) {
165 if ((ntname = realloc(tname, plen)) == NULL)
166 mtree_err("realloc: %s", strerror(errno));
167 tname = ntname;
168 tnamelen = plen;
169 }
170 if (strunvis(tname, p) == -1)
171 mtree_err("strunvis failed on `%s'", p);
172 p = tname;
173
174 pathparent = NULL;
175 if (strchr(p, '/') != NULL) {
176 cur = root;
177 for (; (e = strchr(p, '/')) != NULL; p = e+1) {
178 if (p == e)
179 continue; /* handle // */
180 *e = '\0';
181 if (strcmp(p, ".") != 0) {
182 while (cur &&
183 strcmp(cur->name, p) != 0) {
184 cur = cur->next;
185 }
186 }
187 if (cur == NULL || cur->type != F_DIR) {
188 mtree_err("%s: %s", tname,
189 strerror(ENOENT));
190 }
191 *e = '/';
192 pathparent = cur;
193 cur = cur->child;
194 }
195 if (*p == '\0')
196 mtree_err("%s: empty leaf element", tname);
197 }
198
199 if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL)
200 mtree_err("%s", strerror(errno));
201 *centry = ginfo;
202 centry->lineno = mtree_lineno;
203 strcpy(centry->name, p);
204 #define MAGIC "?*["
205 if (strpbrk(p, MAGIC))
206 centry->flags |= F_MAGIC;
207 set(next, centry);
208
209 if (root == NULL) {
210 /*
211 * empty tree
212 */
213 if (strcmp(centry->name, ".") != 0 ||
214 centry->type != F_DIR)
215 mtree_err(
216 "root node must be the directory `.'");
217 last = root = centry;
218 root->parent = root;
219 } else if (pathparent != NULL) {
220 /*
221 * full path entry
222 */
223 centry->parent = pathparent;
224 cur = pathparent->child;
225 if (cur == NULL) {
226 pathparent->child = centry;
227 last = centry;
228 } else {
229 for (; cur != NULL; cur = cur->next) {
230 if (strcmp(cur->name, centry->name)
231 == 0) {
232 /* existing entry; replace */
233 replacenode(cur, centry);
234 break;
235 }
236 if (cur->next == NULL) {
237 /* last entry; add new */
238 cur->next = centry;
239 centry->prev = cur;
240 break;
241 }
242 }
243 last = cur;
244 while (last->next != NULL)
245 last = last->next;
246 }
247 } else if (strcmp(centry->name, ".") == 0) {
248 /*
249 * duplicate "." entry; always replace
250 */
251 replacenode(root, centry);
252 } else if (last->type == F_DIR && !(last->flags & F_DONE)) {
253 /*
254 * new relative child
255 * (no duplicate check)
256 */
257 centry->parent = last;
258 last = last->child = centry;
259 } else {
260 /*
261 * relative entry, up one directory
262 * (no duplicate check)
263 */
264 centry->parent = last->parent;
265 centry->prev = last;
266 last = last->next = centry;
267 }
268 }
269 return (root);
270 }
271
272 /*
273 * dump_nodes --
274 * dump the NODEs from `cur', based in the directory `dir'.
275 * if pathlast is none zero, print the path last, otherwise print
276 * it first.
277 */
278 void
279 dump_nodes(const char *dir, NODE *root, int pathlast)
280 {
281 NODE *cur;
282 char path[MAXPATHLEN];
283 const char *name;
284
285 for (cur = root; cur != NULL; cur = cur->next) {
286 if (cur->type != F_DIR && !matchtags(cur))
287 continue;
288
289 if (snprintf(path, sizeof(path), "%s%s%s",
290 dir, *dir ? "/" : "", cur->name)
291 >= sizeof(path))
292 mtree_err("Pathname too long.");
293
294 if (!pathlast)
295 printf("%s ", vispath(path));
296
297 #define MATCHFLAG(f) ((keys & (f)) && (cur->flags & (f)))
298 if (MATCHFLAG(F_TYPE))
299 printf("type=%s ", nodetype(cur->type));
300 if (MATCHFLAG(F_UID | F_UNAME)) {
301 if (keys & F_UNAME &&
302 (name = user_from_uid(cur->st_uid, 1)) != NULL)
303 printf("uname=%s ", name);
304 else
305 printf("uid=%u ", cur->st_uid);
306 }
307 if (MATCHFLAG(F_GID | F_GNAME)) {
308 if (keys & F_GNAME &&
309 (name = group_from_gid(cur->st_gid, 1)) != NULL)
310 printf("gname=%s ", name);
311 else
312 printf("gid=%u ", cur->st_gid);
313 }
314 if (MATCHFLAG(F_MODE))
315 printf("mode=%#o ", cur->st_mode);
316 if (MATCHFLAG(F_DEV) &&
317 (cur->type == F_BLOCK || cur->type == F_CHAR))
318 printf("device=%#x ", cur->st_rdev);
319 if (MATCHFLAG(F_NLINK))
320 printf("nlink=%d ", cur->st_nlink);
321 if (MATCHFLAG(F_SLINK))
322 printf("link=%s ", cur->slink);
323 if (MATCHFLAG(F_SIZE))
324 printf("size=%lld ", (long long)cur->st_size);
325 if (MATCHFLAG(F_TIME))
326 printf("time=%ld.%ld ", (long)cur->st_mtimespec.tv_sec,
327 cur->st_mtimespec.tv_nsec);
328 if (MATCHFLAG(F_CKSUM))
329 printf("cksum=%lu ", cur->cksum);
330 if (MATCHFLAG(F_MD5))
331 printf("md5=%s ", cur->md5digest);
332 if (MATCHFLAG(F_RMD160))
333 printf("rmd160=%s ", cur->rmd160digest);
334 if (MATCHFLAG(F_SHA1))
335 printf("sha1=%s ", cur->sha1digest);
336 if (MATCHFLAG(F_FLAGS))
337 printf("flags=%s ",
338 flags_to_string(cur->st_flags, "none"));
339 if (MATCHFLAG(F_IGN))
340 printf("ignore ");
341 if (MATCHFLAG(F_OPT))
342 printf("optional ");
343 if (MATCHFLAG(F_TAGS))
344 printf("tags=%s ", cur->tags);
345 puts(pathlast ? vispath(path) : "");
346
347 if (cur->child)
348 dump_nodes(path, cur->child, pathlast);
349 }
350 }
351
352 /*
353 * vispath --
354 * strsvis(3) encodes path, which must not be longer than MAXPATHLEN
355 * characters long, and returns a pointer to a static buffer containing
356 * the result.
357 */
358 char *
359 vispath(const char *path)
360 {
361 const char extra[] = { ' ', '\t', '\n', '\\', '#', '\0' };
362 static char pathbuf[4*MAXPATHLEN + 1];
363
364 strsvis(pathbuf, path, VIS_CSTYLE, extra);
365 return(pathbuf);
366 }
367
368
369 static dev_t
370 parsedev(char *arg)
371 {
372 #define MAX_PACK_ARGS 3
373 u_long numbers[MAX_PACK_ARGS];
374 char *p, *ep, *dev;
375 int argc;
376 pack_t *pack;
377 dev_t result;
378
379 if ((dev = strchr(arg, ',')) != NULL) {
380 *dev++='\0';
381 if ((pack = pack_find(arg)) == NULL)
382 mtree_err("unknown format `%s'", arg);
383 argc = 0;
384 while ((p = strsep(&dev, ",")) != NULL) {
385 if (*p == '\0')
386 mtree_err("missing number");
387 numbers[argc++] = strtoul(p, &ep, 0);
388 if (*ep != '\0')
389 mtree_err("invalid number `%s'",
390 p);
391 if (argc > MAX_PACK_ARGS)
392 mtree_err("too many arguments");
393 }
394 if (argc < 2)
395 mtree_err("not enough arguments");
396 result = (*pack)(argc, numbers);
397 } else {
398 result = (dev_t)strtoul(arg, &ep, 0);
399 if (*ep != '\0')
400 mtree_err("invalid device `%s'", arg);
401 }
402 return (result);
403 }
404
405 static void
406 replacenode(NODE *cur, NODE *new)
407 {
408
409 if (cur->type != new->type)
410 mtree_err("existing entry type `%s' does not match type `%s'",
411 nodetype(cur->type), nodetype(new->type));
412 #define REPLACE(x) cur->x = new->x
413 #define REPLACESTR(x) if (cur->x) free(cur->x); cur->x = new->x
414
415 REPLACE(st_size);
416 REPLACE(st_mtimespec);
417 REPLACESTR(slink);
418 REPLACE(st_uid);
419 REPLACE(st_gid);
420 REPLACE(st_mode);
421 REPLACE(st_rdev);
422 REPLACE(st_flags);
423 REPLACE(st_nlink);
424 REPLACE(cksum);
425 REPLACESTR(md5digest);
426 REPLACESTR(rmd160digest);
427 REPLACESTR(sha1digest);
428 REPLACESTR(tags);
429 REPLACE(lineno);
430 REPLACE(flags);
431 free(new);
432 }
433
434 static void
435 set(char *t, NODE *ip)
436 {
437 int type, value, len;
438 gid_t gid;
439 uid_t uid;
440 char *kw, *val, *md, *ep;
441 void *m;
442
443 val = NULL;
444 while ((kw = strsep(&t, "= \t")) != NULL) {
445 if (*kw == '\0')
446 continue;
447 if (strcmp(kw, "all") == 0)
448 mtree_err("invalid keyword `all'");
449 ip->flags |= type = parsekey(kw, &value);
450 if (value) {
451 while ((val = strsep(&t, " \t")) != NULL &&
452 *val == '\0')
453 continue;
454 if (val == NULL)
455 mtree_err("missing value");
456 }
457 switch(type) {
458 case F_CKSUM:
459 ip->cksum = strtoul(val, &ep, 10);
460 if (*ep)
461 mtree_err("invalid checksum `%s'", val);
462 break;
463 case F_DEV:
464 ip->st_rdev = parsedev(val);
465 break;
466 case F_FLAGS:
467 if (strcmp("none", val) == 0)
468 ip->st_flags = 0;
469 else if (string_to_flags(&val, &ip->st_flags, NULL)
470 != 0)
471 mtree_err("invalid flag `%s'", val);
472 break;
473 case F_GID:
474 ip->st_gid = (gid_t)strtoul(val, &ep, 10);
475 if (*ep)
476 mtree_err("invalid gid `%s'", val);
477 break;
478 case F_GNAME:
479 if (Wflag) /* don't parse if whacking */
480 break;
481 if (gid_from_group(val, &gid) == -1)
482 mtree_err("unknown group `%s'", val);
483 ip->st_gid = gid;
484 break;
485 case F_IGN:
486 /* just set flag bit */
487 break;
488 case F_MD5:
489 if (val[0]=='0' && val[1]=='x')
490 md=&val[2];
491 else
492 md=val;
493 if ((ip->md5digest = strdup(md)) == NULL)
494 mtree_err("memory allocation error");
495 break;
496 case F_MODE:
497 if ((m = setmode(val)) == NULL)
498 mtree_err("invalid file mode `%s'", val);
499 ip->st_mode = getmode(m, 0);
500 free(m);
501 break;
502 case F_NLINK:
503 ip->st_nlink = (nlink_t)strtoul(val, &ep, 10);
504 if (*ep)
505 mtree_err("invalid link count `%s'", val);
506 break;
507 case F_OPT:
508 /* just set flag bit */
509 break;
510 case F_RMD160:
511 if (val[0]=='0' && val[1]=='x')
512 md=&val[2];
513 else
514 md=val;
515 if ((ip->rmd160digest = strdup(md)) == NULL)
516 mtree_err("memory allocation error");
517 break;
518 case F_SHA1:
519 if (val[0]=='0' && val[1]=='x')
520 md=&val[2];
521 else
522 md=val;
523 if ((ip->sha1digest = strdup(md)) == NULL)
524 mtree_err("memory allocation error");
525 break;
526 case F_SIZE:
527 ip->st_size = (off_t)strtoll(val, &ep, 10);
528 if (*ep)
529 mtree_err("invalid size `%s'", val);
530 break;
531 case F_SLINK:
532 if ((ip->slink = strdup(val)) == NULL)
533 mtree_err("memory allocation error");
534 break;
535 case F_TAGS:
536 len = strlen(val) + 3; /* "," + str + ",\0" */
537 if ((ip->tags = malloc(len)) == NULL)
538 mtree_err("memory allocation error");
539 snprintf(ip->tags, len, ",%s,", val);
540 break;
541 case F_TIME:
542 ip->st_mtimespec.tv_sec =
543 (time_t)strtoul(val, &ep, 10);
544 if (*ep != '.')
545 mtree_err("invalid time `%s'", val);
546 val = ep + 1;
547 ip->st_mtimespec.tv_nsec = strtoul(val, &ep, 10);
548 if (*ep)
549 mtree_err("invalid time `%s'", val);
550 break;
551 case F_TYPE:
552 ip->type = parsetype(val);
553 break;
554 case F_UID:
555 ip->st_uid = (uid_t)strtoul(val, &ep, 10);
556 if (*ep)
557 mtree_err("invalid uid `%s'", val);
558 break;
559 case F_UNAME:
560 if (Wflag) /* don't parse if whacking */
561 break;
562 if (uid_from_user(val, &uid) == -1)
563 mtree_err("unknown user `%s'", val);
564 ip->st_uid = uid;
565 break;
566 default:
567 mtree_err(
568 "set(): unsupported key type 0x%x (INTERNAL ERROR)",
569 type);
570 /* NOTREACHED */
571 }
572 }
573 }
574
575 static void
576 unset(char *t, NODE *ip)
577 {
578 char *p;
579
580 while ((p = strsep(&t, " \t")) != NULL) {
581 if (*p == '\0')
582 continue;
583 ip->flags &= ~parsekey(p, NULL);
584 }
585 }
586