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