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