1 /*- 2 * Copyright (c) 2011 Marcel Moolenaar 3 * 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 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include <sys/cdefs.h> 27 __FBSDID("$FreeBSD$"); 28 29 #include <sys/param.h> 30 #include <sys/queue.h> 31 #include <sys/sbuf.h> 32 #include <sys/stat.h> 33 #include <sys/types.h> 34 #include <assert.h> 35 #include <errno.h> 36 #include <fcntl.h> 37 #include <grp.h> 38 #include <inttypes.h> 39 #include <pwd.h> 40 #include <stdarg.h> 41 #include <stdbool.h> 42 #include <stddef.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <string.h> 46 #include <strings.h> 47 #include <time.h> 48 #include <unistd.h> 49 #include <util.h> 50 #include <vis.h> 51 52 #include "makefs.h" 53 54 #ifndef ENOATTR 55 #define ENOATTR ENOMSG 56 #endif 57 58 #define IS_DOT(nm) ((nm)[0] == '.' && (nm)[1] == '\0') 59 #define IS_DOTDOT(nm) ((nm)[0] == '.' && (nm)[1] == '.' && (nm)[2] == '\0') 60 61 struct mtree_fileinfo { 62 SLIST_ENTRY(mtree_fileinfo) next; 63 FILE *fp; 64 const char *name; 65 u_int line; 66 }; 67 68 /* Global state used while parsing. */ 69 static SLIST_HEAD(, mtree_fileinfo) mtree_fileinfo = 70 SLIST_HEAD_INITIALIZER(mtree_fileinfo); 71 static fsnode *mtree_root; 72 static fsnode *mtree_current; 73 static fsnode mtree_global; 74 static fsinode mtree_global_inode; 75 static u_int errors, warnings; 76 77 static void mtree_error(const char *, ...) __printflike(1, 2); 78 static void mtree_warning(const char *, ...) __printflike(1, 2); 79 80 static int 81 mtree_file_push(const char *name, FILE *fp) 82 { 83 struct mtree_fileinfo *fi; 84 85 fi = emalloc(sizeof(*fi)); 86 if (strcmp(name, "-") == 0) 87 fi->name = estrdup("(stdin)"); 88 else 89 fi->name = estrdup(name); 90 if (fi->name == NULL) { 91 free(fi); 92 return (ENOMEM); 93 } 94 95 fi->fp = fp; 96 fi->line = 0; 97 98 SLIST_INSERT_HEAD(&mtree_fileinfo, fi, next); 99 return (0); 100 } 101 102 static void 103 mtree_print(const char *msgtype, const char *fmt, va_list ap) 104 { 105 struct mtree_fileinfo *fi; 106 107 if (msgtype != NULL) { 108 fi = SLIST_FIRST(&mtree_fileinfo); 109 if (fi != NULL) 110 fprintf(stderr, "%s:%u: ", fi->name, fi->line); 111 fprintf(stderr, "%s: ", msgtype); 112 } 113 vfprintf(stderr, fmt, ap); 114 } 115 116 static void 117 mtree_error(const char *fmt, ...) 118 { 119 va_list ap; 120 121 va_start(ap, fmt); 122 mtree_print("error", fmt, ap); 123 va_end(ap); 124 125 errors++; 126 fputc('\n', stderr); 127 } 128 129 static void 130 mtree_warning(const char *fmt, ...) 131 { 132 va_list ap; 133 134 va_start(ap, fmt); 135 mtree_print("warning", fmt, ap); 136 va_end(ap); 137 138 warnings++; 139 fputc('\n', stderr); 140 } 141 142 #ifndef MAKEFS_MAX_TREE_DEPTH 143 # define MAKEFS_MAX_TREE_DEPTH (MAXPATHLEN/2) 144 #endif 145 146 /* construct path to node->name */ 147 static char * 148 mtree_file_path(fsnode *node) 149 { 150 fsnode *pnode; 151 struct sbuf *sb; 152 char *res, *rp[MAKEFS_MAX_TREE_DEPTH]; 153 int depth; 154 155 depth = 0; 156 rp[depth] = node->name; 157 for (pnode = node->parent; pnode && depth < MAKEFS_MAX_TREE_DEPTH - 1; 158 pnode = pnode->parent) { 159 if (strcmp(pnode->name, ".") == 0) 160 break; 161 rp[++depth] = pnode->name; 162 } 163 164 sb = sbuf_new_auto(); 165 if (sb == NULL) { 166 errno = ENOMEM; 167 return (NULL); 168 } 169 while (depth > 0) { 170 sbuf_cat(sb, rp[depth--]); 171 sbuf_putc(sb, '/'); 172 } 173 sbuf_cat(sb, rp[depth]); 174 sbuf_finish(sb); 175 res = estrdup(sbuf_data(sb)); 176 sbuf_delete(sb); 177 if (res == NULL) 178 errno = ENOMEM; 179 return res; 180 181 } 182 183 /* mtree_resolve() sets errno to indicate why NULL was returned. */ 184 static char * 185 mtree_resolve(const char *spec, int *istemp) 186 { 187 struct sbuf *sb; 188 char *res, *var = NULL; 189 const char *base, *p, *v; 190 size_t len; 191 int c, error, quoted, subst; 192 193 len = strlen(spec); 194 if (len == 0) { 195 errno = EINVAL; 196 return (NULL); 197 } 198 199 c = (len > 1) ? (spec[0] == spec[len - 1]) ? spec[0] : 0 : 0; 200 *istemp = (c == '`') ? 1 : 0; 201 subst = (c == '`' || c == '"') ? 1 : 0; 202 quoted = (subst || c == '\'') ? 1 : 0; 203 204 if (!subst) { 205 res = estrdup(spec + quoted); 206 if (quoted) 207 res[len - 2] = '\0'; 208 return (res); 209 } 210 211 sb = sbuf_new_auto(); 212 if (sb == NULL) { 213 errno = ENOMEM; 214 return (NULL); 215 } 216 217 base = spec + 1; 218 len -= 2; 219 error = 0; 220 while (len > 0) { 221 p = strchr(base, '$'); 222 if (p == NULL) { 223 sbuf_bcat(sb, base, len); 224 base += len; 225 len = 0; 226 continue; 227 } 228 /* The following is safe. spec always starts with a quote. */ 229 if (p[-1] == '\\') 230 p--; 231 if (base != p) { 232 sbuf_bcat(sb, base, p - base); 233 len -= p - base; 234 base = p; 235 } 236 if (*p == '\\') { 237 sbuf_putc(sb, '$'); 238 base += 2; 239 len -= 2; 240 continue; 241 } 242 /* Skip the '$'. */ 243 base++; 244 len--; 245 /* Handle ${X} vs $X. */ 246 v = base; 247 if (*base == '{') { 248 p = strchr(v, '}'); 249 if (p == NULL) 250 p = v; 251 } else 252 p = v; 253 len -= (p + 1) - base; 254 base = p + 1; 255 256 if (v == p) { 257 sbuf_putc(sb, *v); 258 continue; 259 } 260 261 error = ENOMEM; 262 var = ecalloc(p - v, 1); 263 memcpy(var, v + 1, p - v - 1); 264 if (strcmp(var, ".CURDIR") == 0) { 265 res = getcwd(NULL, 0); 266 if (res == NULL) 267 break; 268 } else if (strcmp(var, ".PROG") == 0) { 269 res = estrdup(getprogname()); 270 } else { 271 v = getenv(var); 272 if (v != NULL) { 273 res = estrdup(v); 274 } else 275 res = NULL; 276 } 277 error = 0; 278 279 if (res != NULL) { 280 sbuf_cat(sb, res); 281 free(res); 282 } 283 free(var); 284 var = NULL; 285 } 286 287 free(var); 288 sbuf_finish(sb); 289 res = (error == 0) ? strdup(sbuf_data(sb)) : NULL; 290 sbuf_delete(sb); 291 if (res == NULL) 292 errno = ENOMEM; 293 return (res); 294 } 295 296 static int 297 skip_over(FILE *fp, const char *cs) 298 { 299 int c; 300 301 c = getc(fp); 302 while (c != EOF && strchr(cs, c) != NULL) 303 c = getc(fp); 304 if (c != EOF) { 305 ungetc(c, fp); 306 return (0); 307 } 308 return (ferror(fp) ? errno : -1); 309 } 310 311 static int 312 skip_to(FILE *fp, const char *cs) 313 { 314 int c; 315 316 c = getc(fp); 317 while (c != EOF && strchr(cs, c) == NULL) 318 c = getc(fp); 319 if (c != EOF) { 320 ungetc(c, fp); 321 return (0); 322 } 323 return (ferror(fp) ? errno : -1); 324 } 325 326 static int 327 read_word(FILE *fp, char *buf, size_t bufsz) 328 { 329 struct mtree_fileinfo *fi; 330 size_t idx, qidx; 331 int c, done, error, esc, qlvl; 332 333 if (bufsz == 0) 334 return (EINVAL); 335 336 done = 0; 337 esc = 0; 338 idx = 0; 339 qidx = -1; 340 qlvl = 0; 341 do { 342 c = getc(fp); 343 switch (c) { 344 case EOF: 345 buf[idx] = '\0'; 346 error = ferror(fp) ? errno : -1; 347 if (error == -1) 348 mtree_error("unexpected end of file"); 349 return (error); 350 case '#': /* comment -- skip to end of line. */ 351 if (!esc) { 352 error = skip_to(fp, "\n"); 353 if (!error) 354 continue; 355 } 356 break; 357 case '\\': 358 esc++; 359 break; 360 case '`': 361 case '\'': 362 case '"': 363 if (esc) 364 break; 365 if (qlvl == 0) { 366 qlvl++; 367 qidx = idx; 368 } else if (c == buf[qidx]) { 369 qlvl--; 370 if (qlvl > 0) { 371 do { 372 qidx--; 373 } while (buf[qidx] != '`' && 374 buf[qidx] != '\'' && 375 buf[qidx] != '"'); 376 } else 377 qidx = -1; 378 } else { 379 qlvl++; 380 qidx = idx; 381 } 382 break; 383 case ' ': 384 case '\t': 385 case '\n': 386 if (!esc && qlvl == 0) { 387 ungetc(c, fp); 388 c = '\0'; 389 done = 1; 390 break; 391 } 392 if (c == '\n') { 393 /* 394 * We going to eat the newline ourselves. 395 */ 396 if (qlvl > 0) 397 mtree_warning("quoted word straddles " 398 "onto next line."); 399 fi = SLIST_FIRST(&mtree_fileinfo); 400 fi->line++; 401 } 402 break; 403 default: 404 if (esc) 405 buf[idx++] = '\\'; 406 break; 407 } 408 buf[idx++] = c; 409 esc = 0; 410 } while (idx < bufsz && !done); 411 412 if (idx >= bufsz) { 413 mtree_error("word too long to fit buffer (max %zu characters)", 414 bufsz); 415 skip_to(fp, " \t\n"); 416 } 417 return (0); 418 } 419 420 static fsnode * 421 create_node(const char *name, u_int type, fsnode *parent, fsnode *global) 422 { 423 fsnode *n; 424 425 n = ecalloc(1, sizeof(*n)); 426 n->name = estrdup(name); 427 n->type = (type == 0) ? global->type : type; 428 n->parent = parent; 429 430 n->inode = ecalloc(1, sizeof(*n->inode)); 431 432 /* Assign global options/defaults. */ 433 memcpy(n->inode, global->inode, sizeof(*n->inode)); 434 n->inode->st.st_mode = (n->inode->st.st_mode & ~S_IFMT) | n->type; 435 436 if (n->type == S_IFLNK) 437 n->symlink = global->symlink; 438 else if (n->type == S_IFREG) 439 n->contents = global->contents; 440 441 return (n); 442 } 443 444 static void 445 destroy_node(fsnode *n) 446 { 447 448 assert(n != NULL); 449 assert(n->name != NULL); 450 assert(n->inode != NULL); 451 452 free(n->inode); 453 free(n->name); 454 free(n); 455 } 456 457 static int 458 read_number(const char *tok, u_int base, intmax_t *res, intmax_t min, 459 intmax_t max) 460 { 461 char *end; 462 intmax_t val; 463 464 val = strtoimax(tok, &end, base); 465 if (end == tok || end[0] != '\0') 466 return (EINVAL); 467 if (val < min || val > max) 468 return (EDOM); 469 *res = val; 470 return (0); 471 } 472 473 static int 474 read_mtree_keywords(FILE *fp, fsnode *node) 475 { 476 char keyword[PATH_MAX]; 477 char *name, *p, *value; 478 gid_t gid; 479 uid_t uid; 480 struct stat *st, sb; 481 intmax_t num; 482 u_long flset, flclr; 483 int error, istemp; 484 uint32_t type; 485 486 st = &node->inode->st; 487 do { 488 error = skip_over(fp, " \t"); 489 if (error) 490 break; 491 492 error = read_word(fp, keyword, sizeof(keyword)); 493 if (error) 494 break; 495 496 if (keyword[0] == '\0') 497 break; 498 499 value = strchr(keyword, '='); 500 if (value != NULL) 501 *value++ = '\0'; 502 503 /* 504 * We use EINVAL, ENOATTR, ENOSYS and ENXIO to signal 505 * certain conditions: 506 * EINVAL - Value provided for a keyword that does 507 * not take a value. The value is ignored. 508 * ENOATTR - Value missing for a keyword that needs 509 * a value. The keyword is ignored. 510 * ENOSYS - Unsupported keyword encountered. The 511 * keyword is ignored. 512 * ENXIO - Value provided for a keyword that does 513 * not take a value. The value is ignored. 514 */ 515 switch (keyword[0]) { 516 case 'c': 517 if (strcmp(keyword, "contents") == 0) { 518 if (value == NULL) { 519 error = ENOATTR; 520 break; 521 } 522 node->contents = estrdup(value); 523 } else 524 error = ENOSYS; 525 break; 526 case 'f': 527 if (strcmp(keyword, "flags") == 0) { 528 if (value == NULL) { 529 error = ENOATTR; 530 break; 531 } 532 flset = flclr = 0; 533 if (!strtofflags(&value, &flset, &flclr)) { 534 st->st_flags &= ~flclr; 535 st->st_flags |= flset; 536 } else 537 error = errno; 538 } else 539 error = ENOSYS; 540 break; 541 case 'g': 542 if (strcmp(keyword, "gid") == 0) { 543 if (value == NULL) { 544 error = ENOATTR; 545 break; 546 } 547 error = read_number(value, 10, &num, 548 0, UINT_MAX); 549 if (!error) 550 st->st_gid = num; 551 } else if (strcmp(keyword, "gname") == 0) { 552 if (value == NULL) { 553 error = ENOATTR; 554 break; 555 } 556 if (gid_from_group(value, &gid) == 0) 557 st->st_gid = gid; 558 else 559 error = EINVAL; 560 } else 561 error = ENOSYS; 562 break; 563 case 'l': 564 if (strcmp(keyword, "link") == 0) { 565 if (value == NULL) { 566 error = ENOATTR; 567 break; 568 } 569 node->symlink = emalloc(strlen(value) + 1); 570 if (node->symlink == NULL) { 571 error = errno; 572 break; 573 } 574 if (strunvis(node->symlink, value) < 0) { 575 error = errno; 576 break; 577 } 578 } else 579 error = ENOSYS; 580 break; 581 case 'm': 582 if (strcmp(keyword, "mode") == 0) { 583 if (value == NULL) { 584 error = ENOATTR; 585 break; 586 } 587 if (value[0] >= '0' && value[0] <= '9') { 588 error = read_number(value, 8, &num, 589 0, 07777); 590 if (!error) { 591 st->st_mode &= S_IFMT; 592 st->st_mode |= num; 593 } 594 } else { 595 /* Symbolic mode not supported. */ 596 error = EINVAL; 597 break; 598 } 599 } else 600 error = ENOSYS; 601 break; 602 case 'o': 603 if (strcmp(keyword, "optional") == 0) { 604 if (value != NULL) 605 error = ENXIO; 606 node->flags |= FSNODE_F_OPTIONAL; 607 } else 608 error = ENOSYS; 609 break; 610 case 's': 611 if (strcmp(keyword, "size") == 0) { 612 if (value == NULL) { 613 error = ENOATTR; 614 break; 615 } 616 error = read_number(value, 10, &num, 617 0, INTMAX_MAX); 618 if (!error) 619 st->st_size = num; 620 } else 621 error = ENOSYS; 622 break; 623 case 't': 624 if (strcmp(keyword, "time") == 0) { 625 if (value == NULL) { 626 error = ENOATTR; 627 break; 628 } 629 p = strchr(value, '.'); 630 if (p != NULL) 631 *p++ = '\0'; 632 error = read_number(value, 10, &num, 0, 633 INTMAX_MAX); 634 if (error) 635 break; 636 st->st_atime = num; 637 st->st_ctime = num; 638 st->st_mtime = num; 639 if (p == NULL) 640 break; 641 error = read_number(p, 10, &num, 0, 642 INTMAX_MAX); 643 if (error) 644 break; 645 if (num != 0) 646 error = EINVAL; 647 } else if (strcmp(keyword, "type") == 0) { 648 if (value == NULL) { 649 error = ENOATTR; 650 break; 651 } 652 if (strcmp(value, "dir") == 0) 653 node->type = S_IFDIR; 654 else if (strcmp(value, "file") == 0) 655 node->type = S_IFREG; 656 else if (strcmp(value, "link") == 0) 657 node->type = S_IFLNK; 658 else 659 error = EINVAL; 660 } else 661 error = ENOSYS; 662 break; 663 case 'u': 664 if (strcmp(keyword, "uid") == 0) { 665 if (value == NULL) { 666 error = ENOATTR; 667 break; 668 } 669 error = read_number(value, 10, &num, 670 0, UINT_MAX); 671 if (!error) 672 st->st_uid = num; 673 } else if (strcmp(keyword, "uname") == 0) { 674 if (value == NULL) { 675 error = ENOATTR; 676 break; 677 } 678 if (uid_from_user(value, &uid) == 0) 679 st->st_uid = uid; 680 else 681 error = EINVAL; 682 } else 683 error = ENOSYS; 684 break; 685 default: 686 error = ENOSYS; 687 break; 688 } 689 690 switch (error) { 691 case EINVAL: 692 mtree_error("%s: invalid value '%s'", keyword, value); 693 break; 694 case ENOATTR: 695 mtree_error("%s: keyword needs a value", keyword); 696 break; 697 case ENOSYS: 698 mtree_warning("%s: unsupported keyword", keyword); 699 break; 700 case ENXIO: 701 mtree_error("%s: keyword does not take a value", 702 keyword); 703 break; 704 } 705 } while (1); 706 707 if (error) 708 return (error); 709 710 st->st_mode = (st->st_mode & ~S_IFMT) | node->type; 711 712 /* Nothing more to do for the global defaults. */ 713 if (node->name == NULL) 714 return (0); 715 716 /* 717 * Be intelligent about the file type. 718 */ 719 if (node->contents != NULL) { 720 if (node->symlink != NULL) { 721 mtree_error("%s: both link and contents keywords " 722 "defined", node->name); 723 return (0); 724 } 725 type = S_IFREG; 726 } else if (node->type != 0) { 727 type = node->type; 728 if (type == S_IFREG) { 729 /* the named path is the default contents */ 730 node->contents = mtree_file_path(node); 731 } 732 } else 733 type = (node->symlink != NULL) ? S_IFLNK : S_IFDIR; 734 735 if (node->type == 0) 736 node->type = type; 737 738 if (node->type != type) { 739 mtree_error("%s: file type and defined keywords to not match", 740 node->name); 741 return (0); 742 } 743 744 st->st_mode = (st->st_mode & ~S_IFMT) | node->type; 745 746 if (node->contents == NULL) 747 return (0); 748 749 name = mtree_resolve(node->contents, &istemp); 750 if (name == NULL) 751 return (errno); 752 753 if (stat(name, &sb) != 0) { 754 mtree_error("%s: contents file '%s' not found", node->name, 755 name); 756 free(name); 757 return (0); 758 } 759 760 /* 761 * Check for hardlinks. If the contents key is used, then the check 762 * will only trigger if the contents file is a link even if it is used 763 * by more than one file 764 */ 765 if (sb.st_nlink > 1) { 766 fsinode *curino; 767 768 st->st_ino = sb.st_ino; 769 st->st_dev = sb.st_dev; 770 curino = link_check(node->inode); 771 if (curino != NULL) { 772 free(node->inode); 773 node->inode = curino; 774 node->inode->nlink++; 775 } 776 } 777 778 free(node->contents); 779 node->contents = name; 780 st->st_size = sb.st_size; 781 return (0); 782 } 783 784 static int 785 read_mtree_command(FILE *fp) 786 { 787 char cmd[10]; 788 int error; 789 790 error = read_word(fp, cmd, sizeof(cmd)); 791 if (error) 792 goto out; 793 794 error = read_mtree_keywords(fp, &mtree_global); 795 796 out: 797 skip_to(fp, "\n"); 798 (void)getc(fp); 799 return (error); 800 } 801 802 static int 803 read_mtree_spec1(FILE *fp, bool def, const char *name) 804 { 805 fsnode *last, *node, *parent; 806 u_int type; 807 int error; 808 809 assert(name[0] != '\0'); 810 811 /* 812 * Treat '..' specially, because it only changes our current 813 * directory. We don't create a node for it. We simply ignore 814 * any keywords that may appear on the line as well. 815 * Going up a directory is a little non-obvious. A directory 816 * node has a corresponding '.' child. The parent of '.' is 817 * not the '.' node of the parent directory, but the directory 818 * node within the parent to which the child relates. However, 819 * going up a directory means we need to find the '.' node to 820 * which the directoy node is linked. This we can do via the 821 * first * pointer, because '.' is always the first entry in a 822 * directory. 823 */ 824 if (IS_DOTDOT(name)) { 825 /* This deals with NULL pointers as well. */ 826 if (mtree_current == mtree_root) { 827 mtree_warning("ignoring .. in root directory"); 828 return (0); 829 } 830 831 node = mtree_current; 832 833 assert(node != NULL); 834 assert(IS_DOT(node->name)); 835 assert(node->first == node); 836 837 /* Get the corresponding directory node in the parent. */ 838 node = mtree_current->parent; 839 840 assert(node != NULL); 841 assert(!IS_DOT(node->name)); 842 843 node = node->first; 844 845 assert(node != NULL); 846 assert(IS_DOT(node->name)); 847 assert(node->first == node); 848 849 mtree_current = node; 850 return (0); 851 } 852 853 /* 854 * If we don't have a current directory and the first specification 855 * (either implicit or defined) is not '.', then we need to create 856 * a '.' node first (using a recursive call). 857 */ 858 if (!IS_DOT(name) && mtree_current == NULL) { 859 error = read_mtree_spec1(fp, false, "."); 860 if (error) 861 return (error); 862 } 863 864 /* 865 * Lookup the name in the current directory (if we have a current 866 * directory) to make sure we do not create multiple nodes for the 867 * same component. For non-definitions, if we find a node with the 868 * same name, simply change the current directory. For definitions 869 * more happens. 870 */ 871 last = NULL; 872 node = mtree_current; 873 while (node != NULL) { 874 assert(node->first == mtree_current); 875 876 if (strcmp(name, node->name) == 0) { 877 if (def == true) { 878 if (!dupsok) 879 mtree_error( 880 "duplicate definition of %s", 881 name); 882 else 883 mtree_warning( 884 "duplicate definition of %s", 885 name); 886 return (0); 887 } 888 889 if (node->type != S_IFDIR) { 890 mtree_error("%s is not a directory", name); 891 return (0); 892 } 893 894 assert(!IS_DOT(name)); 895 896 node = node->child; 897 898 assert(node != NULL); 899 assert(IS_DOT(node->name)); 900 901 mtree_current = node; 902 return (0); 903 } 904 905 last = node; 906 node = last->next; 907 } 908 909 parent = (mtree_current != NULL) ? mtree_current->parent : NULL; 910 type = (def == false || IS_DOT(name)) ? S_IFDIR : 0; 911 node = create_node(name, type, parent, &mtree_global); 912 if (node == NULL) 913 return (ENOMEM); 914 915 if (def == true) { 916 error = read_mtree_keywords(fp, node); 917 if (error) { 918 destroy_node(node); 919 return (error); 920 } 921 } 922 923 node->first = (mtree_current != NULL) ? mtree_current : node; 924 925 if (last != NULL) 926 last->next = node; 927 928 if (node->type != S_IFDIR) 929 return (0); 930 931 if (!IS_DOT(node->name)) { 932 parent = node; 933 node = create_node(".", S_IFDIR, parent, parent); 934 if (node == NULL) { 935 last->next = NULL; 936 destroy_node(parent); 937 return (ENOMEM); 938 } 939 parent->child = node; 940 node->first = node; 941 } 942 943 assert(node != NULL); 944 assert(IS_DOT(node->name)); 945 assert(node->first == node); 946 947 mtree_current = node; 948 if (mtree_root == NULL) 949 mtree_root = node; 950 951 return (0); 952 } 953 954 static int 955 read_mtree_spec(FILE *fp) 956 { 957 char pathspec[PATH_MAX], pathtmp[4*PATH_MAX + 1]; 958 char *cp; 959 int error; 960 961 error = read_word(fp, pathtmp, sizeof(pathtmp)); 962 if (error) 963 goto out; 964 if (strnunvis(pathspec, PATH_MAX, pathtmp) == -1) { 965 error = errno; 966 goto out; 967 } 968 error = 0; 969 970 cp = strchr(pathspec, '/'); 971 if (cp != NULL) { 972 /* Absolute pathname */ 973 mtree_current = mtree_root; 974 975 do { 976 *cp++ = '\0'; 977 978 /* Disallow '..' as a component. */ 979 if (IS_DOTDOT(pathspec)) { 980 mtree_error("absolute path cannot contain " 981 ".. component"); 982 goto out; 983 } 984 985 /* Ignore multiple adjacent slashes and '.'. */ 986 if (pathspec[0] != '\0' && !IS_DOT(pathspec)) 987 error = read_mtree_spec1(fp, false, pathspec); 988 memmove(pathspec, cp, strlen(cp) + 1); 989 cp = strchr(pathspec, '/'); 990 } while (!error && cp != NULL); 991 992 /* Disallow '.' and '..' as the last component. */ 993 if (!error && (IS_DOT(pathspec) || IS_DOTDOT(pathspec))) { 994 mtree_error("absolute path cannot contain . or .. " 995 "components"); 996 goto out; 997 } 998 } 999 1000 /* Ignore absolute specfications that end with a slash. */ 1001 if (!error && pathspec[0] != '\0') 1002 error = read_mtree_spec1(fp, true, pathspec); 1003 1004 out: 1005 skip_to(fp, "\n"); 1006 (void)getc(fp); 1007 return (error); 1008 } 1009 1010 fsnode * 1011 read_mtree(const char *fname, fsnode *node) 1012 { 1013 struct mtree_fileinfo *fi; 1014 FILE *fp; 1015 int c, error; 1016 1017 /* We do not yet support nesting... */ 1018 assert(node == NULL); 1019 1020 if (strcmp(fname, "-") == 0) 1021 fp = stdin; 1022 else { 1023 fp = fopen(fname, "r"); 1024 if (fp == NULL) 1025 err(1, "Can't open `%s'", fname); 1026 } 1027 1028 error = mtree_file_push(fname, fp); 1029 if (error) 1030 goto out; 1031 1032 memset(&mtree_global, 0, sizeof(mtree_global)); 1033 memset(&mtree_global_inode, 0, sizeof(mtree_global_inode)); 1034 mtree_global.inode = &mtree_global_inode; 1035 mtree_global_inode.nlink = 1; 1036 mtree_global_inode.st.st_nlink = 1; 1037 mtree_global_inode.st.st_atime = mtree_global_inode.st.st_ctime = 1038 mtree_global_inode.st.st_mtime = time(NULL); 1039 errors = warnings = 0; 1040 1041 setgroupent(1); 1042 setpassent(1); 1043 1044 mtree_root = node; 1045 mtree_current = node; 1046 do { 1047 /* Start of a new line... */ 1048 fi = SLIST_FIRST(&mtree_fileinfo); 1049 fi->line++; 1050 1051 error = skip_over(fp, " \t"); 1052 if (error) 1053 break; 1054 1055 c = getc(fp); 1056 if (c == EOF) { 1057 error = ferror(fp) ? errno : -1; 1058 break; 1059 } 1060 1061 switch (c) { 1062 case '\n': /* empty line */ 1063 error = 0; 1064 break; 1065 case '#': /* comment -- skip to end of line. */ 1066 error = skip_to(fp, "\n"); 1067 if (!error) 1068 (void)getc(fp); 1069 break; 1070 case '/': /* special commands */ 1071 error = read_mtree_command(fp); 1072 break; 1073 default: /* specification */ 1074 ungetc(c, fp); 1075 error = read_mtree_spec(fp); 1076 break; 1077 } 1078 } while (!error); 1079 1080 endpwent(); 1081 endgrent(); 1082 1083 if (error <= 0 && (errors || warnings)) { 1084 warnx("%u error(s) and %u warning(s) in mtree manifest", 1085 errors, warnings); 1086 if (errors) 1087 exit(1); 1088 } 1089 1090 out: 1091 if (error > 0) 1092 errc(1, error, "Error reading mtree file"); 1093 1094 if (fp != stdin) 1095 fclose(fp); 1096 1097 if (mtree_root != NULL) 1098 return (mtree_root); 1099 1100 /* Handle empty specifications. */ 1101 node = create_node(".", S_IFDIR, NULL, &mtree_global); 1102 node->first = node; 1103 return (node); 1104 } 1105