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