1223306Smarcel/*- 2223306Smarcel * Copyright (c) 2011 Marcel Moolenaar 3223306Smarcel * All rights reserved. 4223306Smarcel * 5223306Smarcel * Redistribution and use in source and binary forms, with or without 6223306Smarcel * modification, are permitted provided that the following conditions 7223306Smarcel * are met: 8223306Smarcel * 1. Redistributions of source code must retain the above copyright 9223306Smarcel * notice, this list of conditions and the following disclaimer. 10223306Smarcel * 2. Redistributions in binary form must reproduce the above copyright 11223306Smarcel * notice, this list of conditions and the following disclaimer in the 12223306Smarcel * documentation and/or other materials provided with the distribution. 13223306Smarcel * 14223306Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 15223306Smarcel * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16223306Smarcel * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17223306Smarcel * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 18223306Smarcel * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19223306Smarcel * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20223306Smarcel * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21223306Smarcel * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22223306Smarcel * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23223306Smarcel * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24223306Smarcel */ 25223306Smarcel 26223306Smarcel#include <sys/cdefs.h> 27223306Smarcel__FBSDID("$FreeBSD$"); 28223306Smarcel 29223306Smarcel#include <sys/param.h> 30223306Smarcel#include <sys/queue.h> 31223306Smarcel#include <sys/sbuf.h> 32223306Smarcel#include <sys/stat.h> 33223306Smarcel#include <sys/types.h> 34223306Smarcel#include <assert.h> 35223306Smarcel#include <errno.h> 36223306Smarcel#include <fcntl.h> 37223306Smarcel#include <grp.h> 38223306Smarcel#include <inttypes.h> 39223306Smarcel#include <pwd.h> 40223306Smarcel#include <stdarg.h> 41223306Smarcel#include <stdbool.h> 42223306Smarcel#include <stddef.h> 43223306Smarcel#include <stdio.h> 44223306Smarcel#include <stdlib.h> 45223306Smarcel#include <string.h> 46223306Smarcel#include <strings.h> 47223306Smarcel#include <unistd.h> 48223306Smarcel 49223306Smarcel#include "makefs.h" 50223306Smarcel 51223306Smarcel#define IS_DOT(nm) ((nm)[0] == '.' && (nm)[1] == '\0') 52223306Smarcel#define IS_DOTDOT(nm) ((nm)[0] == '.' && (nm)[1] == '.' && (nm)[2] == '\0') 53223306Smarcel 54223306Smarcelstruct mtree_fileinfo { 55223306Smarcel SLIST_ENTRY(mtree_fileinfo) next; 56223306Smarcel FILE *fp; 57223306Smarcel const char *name; 58223306Smarcel u_int line; 59223306Smarcel}; 60223306Smarcel 61223306Smarcel/* Global state used while parsing. */ 62223306Smarcelstatic SLIST_HEAD(, mtree_fileinfo) mtree_fileinfo = 63223306Smarcel SLIST_HEAD_INITIALIZER(mtree_fileinfo); 64223306Smarcelstatic fsnode *mtree_root; 65223306Smarcelstatic fsnode *mtree_current; 66223306Smarcelstatic fsnode mtree_global; 67223306Smarcelstatic fsinode mtree_global_inode; 68223306Smarcelstatic u_int errors, warnings; 69223306Smarcel 70223306Smarcelstatic void mtree_error(const char *, ...) __printflike(1, 2); 71223306Smarcelstatic void mtree_warning(const char *, ...) __printflike(1, 2); 72223306Smarcel 73223306Smarcelstatic int 74223306Smarcelmtree_file_push(const char *name, FILE *fp) 75223306Smarcel{ 76223306Smarcel struct mtree_fileinfo *fi; 77223306Smarcel 78223306Smarcel fi = malloc(sizeof(*fi)); 79223306Smarcel if (fi == NULL) 80223306Smarcel return (ENOMEM); 81223306Smarcel 82223306Smarcel if (strcmp(name, "-") == 0) 83223306Smarcel fi->name = strdup("(stdin)"); 84223306Smarcel else 85223306Smarcel fi->name = strdup(name); 86223306Smarcel if (fi->name == NULL) { 87223306Smarcel free(fi); 88223306Smarcel return (ENOMEM); 89223306Smarcel } 90223306Smarcel 91223306Smarcel fi->fp = fp; 92223306Smarcel fi->line = 0; 93223306Smarcel 94223306Smarcel SLIST_INSERT_HEAD(&mtree_fileinfo, fi, next); 95223306Smarcel return (0); 96223306Smarcel} 97223306Smarcel 98223306Smarcelstatic void 99223306Smarcelmtree_print(const char *msgtype, const char *fmt, va_list ap) 100223306Smarcel{ 101223306Smarcel struct mtree_fileinfo *fi; 102223306Smarcel 103223306Smarcel if (msgtype != NULL) { 104223306Smarcel fi = SLIST_FIRST(&mtree_fileinfo); 105223306Smarcel if (fi != NULL) 106223306Smarcel fprintf(stderr, "%s:%u: ", fi->name, fi->line); 107223306Smarcel fprintf(stderr, "%s: ", msgtype); 108223306Smarcel } 109223306Smarcel vfprintf(stderr, fmt, ap); 110223306Smarcel} 111223306Smarcel 112223306Smarcelstatic void 113223306Smarcelmtree_error(const char *fmt, ...) 114223306Smarcel{ 115223306Smarcel va_list ap; 116223306Smarcel 117223306Smarcel va_start(ap, fmt); 118223306Smarcel mtree_print("error", fmt, ap); 119223306Smarcel va_end(ap); 120223306Smarcel 121223306Smarcel errors++; 122223306Smarcel fputc('\n', stderr); 123223306Smarcel} 124223306Smarcel 125223306Smarcelstatic void 126223306Smarcelmtree_warning(const char *fmt, ...) 127223306Smarcel{ 128223306Smarcel va_list ap; 129223306Smarcel 130223306Smarcel va_start(ap, fmt); 131223306Smarcel mtree_print("warning", fmt, ap); 132223306Smarcel va_end(ap); 133223306Smarcel 134223306Smarcel warnings++; 135223306Smarcel fputc('\n', stderr); 136223306Smarcel} 137223306Smarcel 138242501Ssjg#ifndef MAKEFS_MAX_TREE_DEPTH 139242501Ssjg# define MAKEFS_MAX_TREE_DEPTH (MAXPATHLEN/2) 140242501Ssjg#endif 141242501Ssjg 142242501Ssjg/* construct path to node->name */ 143242501Ssjgstatic char * 144242501Ssjgmtree_file_path(fsnode *node) 145242501Ssjg{ 146242501Ssjg fsnode *pnode; 147242501Ssjg struct sbuf *sb; 148242501Ssjg char *res, *rp[MAKEFS_MAX_TREE_DEPTH]; 149242501Ssjg int depth; 150242501Ssjg 151242501Ssjg depth = 0; 152242501Ssjg rp[depth] = node->name; 153242501Ssjg for (pnode = node->parent; pnode && depth < MAKEFS_MAX_TREE_DEPTH; 154242501Ssjg pnode = pnode->parent) { 155242501Ssjg if (strcmp(pnode->name, ".") == 0) 156242501Ssjg break; 157242501Ssjg rp[++depth] = pnode->name; 158242501Ssjg } 159242501Ssjg 160242501Ssjg sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND); 161242501Ssjg if (sb == NULL) { 162242501Ssjg errno = ENOMEM; 163242501Ssjg return (NULL); 164242501Ssjg } 165242501Ssjg while (depth > 0) { 166242501Ssjg sbuf_cat(sb, rp[depth--]); 167242501Ssjg sbuf_putc(sb, '/'); 168242501Ssjg } 169242501Ssjg sbuf_cat(sb, rp[depth]); 170242501Ssjg sbuf_finish(sb); 171242501Ssjg res = strdup(sbuf_data(sb)); 172242501Ssjg sbuf_delete(sb); 173242501Ssjg if (res == NULL) 174242501Ssjg errno = ENOMEM; 175242501Ssjg return res; 176242501Ssjg 177242501Ssjg} 178242501Ssjg 179223306Smarcel/* mtree_resolve() sets errno to indicate why NULL was returned. */ 180223306Smarcelstatic char * 181223306Smarcelmtree_resolve(const char *spec, int *istemp) 182223306Smarcel{ 183223306Smarcel struct sbuf *sb; 184223306Smarcel char *res, *var; 185223306Smarcel const char *base, *p, *v; 186223306Smarcel size_t len; 187223306Smarcel int c, error, quoted, subst; 188223306Smarcel 189223306Smarcel len = strlen(spec); 190223306Smarcel if (len == 0) { 191223306Smarcel errno = EINVAL; 192223306Smarcel return (NULL); 193223306Smarcel } 194223306Smarcel 195223306Smarcel c = (len > 1) ? (spec[0] == spec[len - 1]) ? spec[0] : 0 : 0; 196223306Smarcel *istemp = (c == '`') ? 1 : 0; 197223306Smarcel subst = (c == '`' || c == '"') ? 1 : 0; 198223306Smarcel quoted = (subst || c == '\'') ? 1 : 0; 199223306Smarcel 200223306Smarcel if (!subst) { 201223306Smarcel res = strdup(spec + quoted); 202223306Smarcel if (res != NULL && quoted) 203223306Smarcel res[len - 2] = '\0'; 204223306Smarcel return (res); 205223306Smarcel } 206223306Smarcel 207223306Smarcel sb = sbuf_new_auto(); 208223306Smarcel if (sb == NULL) { 209223306Smarcel errno = ENOMEM; 210223306Smarcel return (NULL); 211223306Smarcel } 212223306Smarcel 213223306Smarcel base = spec + 1; 214223306Smarcel len -= 2; 215223306Smarcel error = 0; 216223306Smarcel while (len > 0) { 217223306Smarcel p = strchr(base, '$'); 218223306Smarcel if (p == NULL) { 219223306Smarcel sbuf_bcat(sb, base, len); 220223306Smarcel base += len; 221223306Smarcel len = 0; 222223306Smarcel continue; 223223306Smarcel } 224223306Smarcel /* The following is safe. spec always starts with a quote. */ 225223306Smarcel if (p[-1] == '\\') 226223306Smarcel p--; 227223306Smarcel if (base != p) { 228223306Smarcel sbuf_bcat(sb, base, p - base); 229223306Smarcel len -= p - base; 230223306Smarcel base = p; 231223306Smarcel } 232223306Smarcel if (*p == '\\') { 233223306Smarcel sbuf_putc(sb, '$'); 234223306Smarcel base += 2; 235223306Smarcel len -= 2; 236223306Smarcel continue; 237223306Smarcel } 238223306Smarcel /* Skip the '$'. */ 239223306Smarcel base++; 240223306Smarcel len--; 241223306Smarcel /* Handle ${X} vs $X. */ 242223306Smarcel v = base; 243223306Smarcel if (*base == '{') { 244223306Smarcel p = strchr(v, '}'); 245223306Smarcel if (p == NULL) 246223306Smarcel p = v; 247223306Smarcel } else 248223306Smarcel p = v; 249223306Smarcel len -= (p + 1) - base; 250223306Smarcel base = p + 1; 251223306Smarcel 252223306Smarcel if (v == p) { 253223306Smarcel sbuf_putc(sb, *v); 254223306Smarcel continue; 255223306Smarcel } 256223306Smarcel 257223306Smarcel error = ENOMEM; 258223306Smarcel var = calloc(p - v, 1); 259223306Smarcel if (var == NULL) 260223306Smarcel break; 261223306Smarcel 262223306Smarcel memcpy(var, v + 1, p - v - 1); 263223306Smarcel if (strcmp(var, ".CURDIR") == 0) { 264223306Smarcel res = getcwd(NULL, 0); 265223306Smarcel if (res == NULL) 266223306Smarcel break; 267223306Smarcel } else if (strcmp(var, ".PROG") == 0) { 268223306Smarcel res = strdup(getprogname()); 269223306Smarcel if (res == NULL) 270223306Smarcel break; 271223306Smarcel } else { 272223306Smarcel v = getenv(var); 273223306Smarcel if (v != NULL) { 274223306Smarcel res = strdup(v); 275223306Smarcel if (res == NULL) 276223306Smarcel break; 277223306Smarcel } else 278223306Smarcel res = NULL; 279223306Smarcel } 280223306Smarcel error = 0; 281223306Smarcel 282223306Smarcel if (res != NULL) { 283223306Smarcel sbuf_cat(sb, res); 284223306Smarcel free(res); 285223306Smarcel } 286223306Smarcel free(var); 287223306Smarcel } 288223306Smarcel 289223306Smarcel sbuf_finish(sb); 290223306Smarcel res = (error == 0) ? strdup(sbuf_data(sb)) : NULL; 291223306Smarcel sbuf_delete(sb); 292223306Smarcel if (res == NULL) 293223306Smarcel errno = ENOMEM; 294223306Smarcel return (res); 295223306Smarcel} 296223306Smarcel 297223306Smarcelstatic int 298223306Smarcelskip_over(FILE *fp, const char *cs) 299223306Smarcel{ 300223306Smarcel int c; 301223306Smarcel 302223306Smarcel c = getc(fp); 303223306Smarcel while (c != EOF && strchr(cs, c) != NULL) 304223306Smarcel c = getc(fp); 305223306Smarcel if (c != EOF) { 306223306Smarcel ungetc(c, fp); 307223306Smarcel return (0); 308223306Smarcel } 309223306Smarcel return (ferror(fp) ? errno : -1); 310223306Smarcel} 311223306Smarcel 312223306Smarcelstatic int 313223306Smarcelskip_to(FILE *fp, const char *cs) 314223306Smarcel{ 315223306Smarcel int c; 316223306Smarcel 317223306Smarcel c = getc(fp); 318223306Smarcel while (c != EOF && strchr(cs, c) == NULL) 319223306Smarcel c = getc(fp); 320223306Smarcel if (c != EOF) { 321223306Smarcel ungetc(c, fp); 322223306Smarcel return (0); 323223306Smarcel } 324223306Smarcel return (ferror(fp) ? errno : -1); 325223306Smarcel} 326223306Smarcel 327223306Smarcelstatic int 328223306Smarcelread_word(FILE *fp, char *buf, size_t bufsz) 329223306Smarcel{ 330223306Smarcel struct mtree_fileinfo *fi; 331223306Smarcel size_t idx, qidx; 332223306Smarcel int c, done, error, esc, qlvl; 333223306Smarcel 334223306Smarcel if (bufsz == 0) 335223306Smarcel return (EINVAL); 336223306Smarcel 337223306Smarcel done = 0; 338223306Smarcel esc = 0; 339223306Smarcel idx = 0; 340223306Smarcel qidx = -1; 341223306Smarcel qlvl = 0; 342223306Smarcel do { 343223306Smarcel c = getc(fp); 344223306Smarcel switch (c) { 345223306Smarcel case EOF: 346223306Smarcel buf[idx] = '\0'; 347223306Smarcel error = ferror(fp) ? errno : -1; 348223306Smarcel if (error == -1) 349223306Smarcel mtree_error("unexpected end of file"); 350223306Smarcel return (error); 351223306Smarcel case '\\': 352223306Smarcel esc++; 353223306Smarcel if (esc == 1) 354223306Smarcel continue; 355223306Smarcel break; 356223306Smarcel case '`': 357223306Smarcel case '\'': 358223306Smarcel case '"': 359223306Smarcel if (esc) 360223306Smarcel break; 361223306Smarcel if (qlvl == 0) { 362223306Smarcel qlvl++; 363223306Smarcel qidx = idx; 364223306Smarcel } else if (c == buf[qidx]) { 365223306Smarcel qlvl--; 366223306Smarcel if (qlvl > 0) { 367223306Smarcel do { 368223306Smarcel qidx--; 369223306Smarcel } while (buf[qidx] != '`' && 370223306Smarcel buf[qidx] != '\'' && 371223306Smarcel buf[qidx] != '"'); 372223306Smarcel } else 373223306Smarcel qidx = -1; 374223306Smarcel } else { 375223306Smarcel qlvl++; 376223306Smarcel qidx = idx; 377223306Smarcel } 378223306Smarcel break; 379223306Smarcel case ' ': 380223306Smarcel case '\t': 381223306Smarcel case '\n': 382223306Smarcel if (!esc && qlvl == 0) { 383223306Smarcel ungetc(c, fp); 384223306Smarcel c = '\0'; 385223306Smarcel done = 1; 386223306Smarcel break; 387223306Smarcel } 388223306Smarcel if (c == '\n') { 389223306Smarcel /* 390223306Smarcel * We going to eat the newline ourselves. 391223306Smarcel */ 392223306Smarcel if (qlvl > 0) 393223306Smarcel mtree_warning("quoted word straddles " 394223306Smarcel "onto next line."); 395223306Smarcel fi = SLIST_FIRST(&mtree_fileinfo); 396223306Smarcel fi->line++; 397223306Smarcel } 398223306Smarcel break; 399223306Smarcel case 'a': 400223306Smarcel if (esc) 401223306Smarcel c = '\a'; 402223306Smarcel break; 403223306Smarcel case 'b': 404223306Smarcel if (esc) 405223306Smarcel c = '\b'; 406223306Smarcel break; 407223306Smarcel case 'f': 408223306Smarcel if (esc) 409223306Smarcel c = '\f'; 410223306Smarcel break; 411223306Smarcel case 'n': 412223306Smarcel if (esc) 413223306Smarcel c = '\n'; 414223306Smarcel break; 415223306Smarcel case 'r': 416223306Smarcel if (esc) 417223306Smarcel c = '\r'; 418223306Smarcel break; 419223306Smarcel case 't': 420223306Smarcel if (esc) 421223306Smarcel c = '\t'; 422223306Smarcel break; 423223306Smarcel case 'v': 424223306Smarcel if (esc) 425223306Smarcel c = '\v'; 426223306Smarcel break; 427223306Smarcel } 428223306Smarcel buf[idx++] = c; 429223306Smarcel esc = 0; 430223306Smarcel } while (idx < bufsz && !done); 431223306Smarcel 432223306Smarcel if (idx >= bufsz) { 433223306Smarcel mtree_error("word too long to fit buffer (max %zu characters)", 434223306Smarcel bufsz); 435223306Smarcel skip_to(fp, " \t\n"); 436223306Smarcel } 437223306Smarcel return (0); 438223306Smarcel} 439223306Smarcel 440223306Smarcelstatic fsnode * 441223306Smarcelcreate_node(const char *name, u_int type, fsnode *parent, fsnode *global) 442223306Smarcel{ 443223306Smarcel fsnode *n; 444223306Smarcel 445223306Smarcel n = calloc(1, sizeof(*n)); 446223306Smarcel if (n == NULL) 447223306Smarcel return (NULL); 448223306Smarcel 449223306Smarcel n->name = strdup(name); 450223306Smarcel if (n->name == NULL) { 451223306Smarcel free(n); 452223306Smarcel return (NULL); 453223306Smarcel } 454223306Smarcel 455223306Smarcel n->type = (type == 0) ? global->type : type; 456223306Smarcel n->parent = parent; 457223306Smarcel 458223306Smarcel n->inode = calloc(1, sizeof(*n->inode)); 459223306Smarcel if (n->inode == NULL) { 460223306Smarcel free(n->name); 461223306Smarcel free(n); 462223306Smarcel return (NULL); 463223306Smarcel } 464223306Smarcel 465223306Smarcel /* Assign global options/defaults. */ 466223306Smarcel bcopy(global->inode, n->inode, sizeof(*n->inode)); 467223306Smarcel n->inode->st.st_mode = (n->inode->st.st_mode & ~S_IFMT) | n->type; 468223306Smarcel 469223306Smarcel if (n->type == S_IFLNK) 470223306Smarcel n->symlink = global->symlink; 471223306Smarcel else if (n->type == S_IFREG) 472223306Smarcel n->contents = global->contents; 473223306Smarcel 474223306Smarcel return (n); 475223306Smarcel} 476223306Smarcel 477223306Smarcelstatic void 478223306Smarceldestroy_node(fsnode *n) 479223306Smarcel{ 480223306Smarcel 481223306Smarcel assert(n != NULL); 482223306Smarcel assert(n->name != NULL); 483223306Smarcel assert(n->inode != NULL); 484223306Smarcel 485223306Smarcel free(n->inode); 486223306Smarcel free(n->name); 487223306Smarcel free(n); 488223306Smarcel} 489223306Smarcel 490223306Smarcelstatic int 491223306Smarcelread_number(const char *tok, u_int base, intmax_t *res, intmax_t min, 492223306Smarcel intmax_t max) 493223306Smarcel{ 494223306Smarcel char *end; 495223306Smarcel intmax_t val; 496223306Smarcel 497223306Smarcel val = strtoimax(tok, &end, base); 498223306Smarcel if (end == tok || end[0] != '\0') 499223306Smarcel return (EINVAL); 500223306Smarcel if (val < min || val > max) 501223306Smarcel return (EDOM); 502223306Smarcel *res = val; 503223306Smarcel return (0); 504223306Smarcel} 505223306Smarcel 506223306Smarcelstatic int 507223306Smarcelread_mtree_keywords(FILE *fp, fsnode *node) 508223306Smarcel{ 509223306Smarcel char keyword[PATH_MAX]; 510223306Smarcel char *name, *p, *value; 511247042Sbrooks gid_t gid; 512247042Sbrooks uid_t uid; 513223306Smarcel struct stat *st, sb; 514223306Smarcel intmax_t num; 515223306Smarcel u_long flset, flclr; 516223306Smarcel int error, istemp, type; 517223306Smarcel 518223306Smarcel st = &node->inode->st; 519223306Smarcel do { 520223306Smarcel error = skip_over(fp, " \t"); 521223306Smarcel if (error) 522223306Smarcel break; 523223306Smarcel 524223306Smarcel error = read_word(fp, keyword, sizeof(keyword)); 525223306Smarcel if (error) 526223306Smarcel break; 527223306Smarcel 528223306Smarcel if (keyword[0] == '\0') 529223306Smarcel break; 530223306Smarcel 531223306Smarcel value = strchr(keyword, '='); 532223306Smarcel if (value != NULL) 533223306Smarcel *value++ = '\0'; 534223306Smarcel 535223306Smarcel /* 536223306Smarcel * We use EINVAL, ENOATTR, ENOSYS and ENXIO to signal 537223306Smarcel * certain conditions: 538223306Smarcel * EINVAL - Value provided for a keyword that does 539223306Smarcel * not take a value. The value is ignored. 540223306Smarcel * ENOATTR - Value missing for a keyword that needs 541223306Smarcel * a value. The keyword is ignored. 542223306Smarcel * ENOSYS - Unsupported keyword encountered. The 543223306Smarcel * keyword is ignored. 544223306Smarcel * ENXIO - Value provided for a keyword that does 545223306Smarcel * not take a value. The value is ignored. 546223306Smarcel */ 547223306Smarcel switch (keyword[0]) { 548223306Smarcel case 'c': 549223306Smarcel if (strcmp(keyword, "contents") == 0) { 550223306Smarcel if (value == NULL) { 551223306Smarcel error = ENOATTR; 552223306Smarcel break; 553223306Smarcel } 554223306Smarcel node->contents = strdup(value); 555223306Smarcel } else 556223306Smarcel error = ENOSYS; 557223306Smarcel break; 558223306Smarcel case 'f': 559223306Smarcel if (strcmp(keyword, "flags") == 0) { 560223306Smarcel if (value == NULL) { 561223306Smarcel error = ENOATTR; 562223306Smarcel break; 563223306Smarcel } 564223306Smarcel flset = flclr = 0; 565223306Smarcel if (!strtofflags(&value, &flset, &flclr)) { 566223306Smarcel st->st_flags &= ~flclr; 567223306Smarcel st->st_flags |= flset; 568223306Smarcel } else 569223306Smarcel error = errno; 570223306Smarcel } else 571223306Smarcel error = ENOSYS; 572223306Smarcel break; 573223306Smarcel case 'g': 574223306Smarcel if (strcmp(keyword, "gid") == 0) { 575223306Smarcel if (value == NULL) { 576223306Smarcel error = ENOATTR; 577223306Smarcel break; 578223306Smarcel } 579223306Smarcel error = read_number(value, 10, &num, 580223306Smarcel 0, UINT_MAX); 581223306Smarcel if (!error) 582223306Smarcel st->st_gid = num; 583223306Smarcel } else if (strcmp(keyword, "gname") == 0) { 584223306Smarcel if (value == NULL) { 585223306Smarcel error = ENOATTR; 586223306Smarcel break; 587223306Smarcel } 588247042Sbrooks if (gid_from_group(value, &gid) == 0) 589247042Sbrooks st->st_gid = gid; 590223306Smarcel else 591247042Sbrooks error = EINVAL; 592223306Smarcel } else 593223306Smarcel error = ENOSYS; 594223306Smarcel break; 595223306Smarcel case 'l': 596223306Smarcel if (strcmp(keyword, "link") == 0) { 597223306Smarcel if (value == NULL) { 598223306Smarcel error = ENOATTR; 599223306Smarcel break; 600223306Smarcel } 601223306Smarcel node->symlink = strdup(value); 602223306Smarcel } else 603223306Smarcel error = ENOSYS; 604223306Smarcel break; 605223306Smarcel case 'm': 606223306Smarcel if (strcmp(keyword, "mode") == 0) { 607223306Smarcel if (value == NULL) { 608223306Smarcel error = ENOATTR; 609223306Smarcel break; 610223306Smarcel } 611223306Smarcel if (value[0] >= '0' && value[0] <= '9') { 612223306Smarcel error = read_number(value, 8, &num, 613223306Smarcel 0, 07777); 614223306Smarcel if (!error) { 615223306Smarcel st->st_mode &= S_IFMT; 616223306Smarcel st->st_mode |= num; 617223306Smarcel } 618223306Smarcel } else { 619223306Smarcel /* Symbolic mode not supported. */ 620223306Smarcel error = EINVAL; 621223306Smarcel break; 622223306Smarcel } 623223306Smarcel } else 624223306Smarcel error = ENOSYS; 625223306Smarcel break; 626223306Smarcel case 'o': 627223306Smarcel if (strcmp(keyword, "optional") == 0) { 628223306Smarcel if (value != NULL) 629223306Smarcel error = ENXIO; 630223306Smarcel node->flags |= FSNODE_F_OPTIONAL; 631223306Smarcel } else 632223306Smarcel error = ENOSYS; 633223306Smarcel break; 634223306Smarcel case 's': 635223306Smarcel if (strcmp(keyword, "size") == 0) { 636223306Smarcel if (value == NULL) { 637223306Smarcel error = ENOATTR; 638223306Smarcel break; 639223306Smarcel } 640223306Smarcel error = read_number(value, 10, &num, 641223306Smarcel 0, INTMAX_MAX); 642223306Smarcel if (!error) 643223306Smarcel st->st_size = num; 644223306Smarcel } else 645223306Smarcel error = ENOSYS; 646223306Smarcel break; 647223306Smarcel case 't': 648223306Smarcel if (strcmp(keyword, "time") == 0) { 649223306Smarcel if (value == NULL) { 650223306Smarcel error = ENOATTR; 651223306Smarcel break; 652223306Smarcel } 653223306Smarcel p = strchr(value, '.'); 654223306Smarcel if (p != NULL) 655223306Smarcel *p++ = '\0'; 656223306Smarcel error = read_number(value, 10, &num, 0, 657223306Smarcel INTMAX_MAX); 658223306Smarcel if (error) 659223306Smarcel break; 660223306Smarcel st->st_atime = num; 661223306Smarcel st->st_ctime = num; 662223306Smarcel st->st_mtime = num; 663223306Smarcel error = read_number(p, 10, &num, 0, 664223306Smarcel INTMAX_MAX); 665223306Smarcel if (error) 666223306Smarcel break; 667223306Smarcel if (num != 0) 668223306Smarcel error = EINVAL; 669223306Smarcel } else if (strcmp(keyword, "type") == 0) { 670223306Smarcel if (value == NULL) { 671223306Smarcel error = ENOATTR; 672223306Smarcel break; 673223306Smarcel } 674223306Smarcel if (strcmp(value, "dir") == 0) 675223306Smarcel node->type = S_IFDIR; 676223306Smarcel else if (strcmp(value, "file") == 0) 677223306Smarcel node->type = S_IFREG; 678223306Smarcel else if (strcmp(value, "link") == 0) 679223306Smarcel node->type = S_IFLNK; 680223306Smarcel else 681223306Smarcel error = EINVAL; 682223306Smarcel } else 683223306Smarcel error = ENOSYS; 684223306Smarcel break; 685223306Smarcel case 'u': 686223306Smarcel if (strcmp(keyword, "uid") == 0) { 687223306Smarcel if (value == NULL) { 688223306Smarcel error = ENOATTR; 689223306Smarcel break; 690223306Smarcel } 691223306Smarcel error = read_number(value, 10, &num, 692223306Smarcel 0, UINT_MAX); 693223306Smarcel if (!error) 694223306Smarcel st->st_uid = num; 695223306Smarcel } else if (strcmp(keyword, "uname") == 0) { 696223306Smarcel if (value == NULL) { 697223306Smarcel error = ENOATTR; 698223306Smarcel break; 699223306Smarcel } 700247042Sbrooks if (uid_from_user(value, &uid) == 0) 701247042Sbrooks st->st_uid = uid; 702223306Smarcel else 703247042Sbrooks error = EINVAL; 704223306Smarcel } else 705223306Smarcel error = ENOSYS; 706223306Smarcel break; 707223306Smarcel default: 708223306Smarcel error = ENOSYS; 709223306Smarcel break; 710223306Smarcel } 711223306Smarcel 712223306Smarcel switch (error) { 713223306Smarcel case EINVAL: 714223306Smarcel mtree_error("%s: invalid value '%s'", keyword, value); 715223306Smarcel break; 716223306Smarcel case ENOATTR: 717223306Smarcel mtree_error("%s: keyword needs a value", keyword); 718223306Smarcel break; 719223306Smarcel case ENOSYS: 720223306Smarcel mtree_warning("%s: unsupported keyword", keyword); 721223306Smarcel break; 722223306Smarcel case ENXIO: 723223306Smarcel mtree_error("%s: keyword does not take a value", 724223306Smarcel keyword); 725223306Smarcel break; 726223306Smarcel } 727223306Smarcel } while (1); 728223306Smarcel 729223306Smarcel if (error) 730223306Smarcel return (error); 731223306Smarcel 732223306Smarcel st->st_mode = (st->st_mode & ~S_IFMT) | node->type; 733223306Smarcel 734223306Smarcel /* Nothing more to do for the global defaults. */ 735223306Smarcel if (node->name == NULL) 736223306Smarcel return (0); 737223306Smarcel 738223306Smarcel /* 739223306Smarcel * Be intelligent about the file type. 740223306Smarcel */ 741223306Smarcel if (node->contents != NULL) { 742223306Smarcel if (node->symlink != NULL) { 743223306Smarcel mtree_error("%s: both link and contents keywords " 744223306Smarcel "defined", node->name); 745223306Smarcel return (0); 746223306Smarcel } 747223306Smarcel type = S_IFREG; 748242501Ssjg } else if (node->type != 0) { 749242501Ssjg type = node->type; 750242501Ssjg if (type == S_IFREG) { 751242501Ssjg /* the named path is the default contents */ 752242501Ssjg node->contents = mtree_file_path(node); 753242501Ssjg } 754223306Smarcel } else 755223306Smarcel type = (node->symlink != NULL) ? S_IFLNK : S_IFDIR; 756223306Smarcel 757223306Smarcel if (node->type == 0) 758223306Smarcel node->type = type; 759223306Smarcel 760223306Smarcel if (node->type != type) { 761223306Smarcel mtree_error("%s: file type and defined keywords to not match", 762223306Smarcel node->name); 763223306Smarcel return (0); 764223306Smarcel } 765223306Smarcel 766223306Smarcel st->st_mode = (st->st_mode & ~S_IFMT) | node->type; 767223306Smarcel 768223306Smarcel if (node->contents == NULL) 769223306Smarcel return (0); 770223306Smarcel 771223306Smarcel name = mtree_resolve(node->contents, &istemp); 772223306Smarcel if (name == NULL) 773223306Smarcel return (errno); 774223306Smarcel 775223306Smarcel if (stat(name, &sb) != 0) { 776223306Smarcel mtree_error("%s: contents file '%s' not found", node->name, 777223306Smarcel name); 778223306Smarcel free(name); 779223306Smarcel return (0); 780223306Smarcel } 781223306Smarcel 782247052Sbrooks /* 783247052Sbrooks * Check for hardlinks. If the contents key is used, then the check 784247052Sbrooks * will only trigger if the contents file is a link even if it is used 785247052Sbrooks * by more than one file 786247052Sbrooks */ 787247052Sbrooks if (sb.st_nlink > 1) { 788247052Sbrooks fsinode *curino; 789247052Sbrooks 790247052Sbrooks st->st_ino = sb.st_ino; 791247052Sbrooks st->st_dev = sb.st_dev; 792247052Sbrooks curino = link_check(node->inode); 793247052Sbrooks if (curino != NULL) { 794247052Sbrooks free(node->inode); 795247052Sbrooks node->inode = curino; 796247052Sbrooks node->inode->nlink++; 797247052Sbrooks } 798247052Sbrooks } 799247052Sbrooks 800223306Smarcel free(node->contents); 801223306Smarcel node->contents = name; 802223306Smarcel st->st_size = sb.st_size; 803223306Smarcel return (0); 804223306Smarcel} 805223306Smarcel 806223306Smarcelstatic int 807223306Smarcelread_mtree_command(FILE *fp) 808223306Smarcel{ 809223306Smarcel char cmd[10]; 810223306Smarcel int error; 811223306Smarcel 812223306Smarcel error = read_word(fp, cmd, sizeof(cmd)); 813223306Smarcel if (error) 814223306Smarcel goto out; 815223306Smarcel 816223306Smarcel error = read_mtree_keywords(fp, &mtree_global); 817223306Smarcel 818223306Smarcel out: 819223306Smarcel skip_to(fp, "\n"); 820223306Smarcel (void)getc(fp); 821223306Smarcel return (error); 822223306Smarcel} 823223306Smarcel 824223306Smarcelstatic int 825223306Smarcelread_mtree_spec1(FILE *fp, bool def, const char *name) 826223306Smarcel{ 827223306Smarcel fsnode *last, *node, *parent; 828223306Smarcel u_int type; 829223306Smarcel int error; 830223306Smarcel 831223306Smarcel assert(name[0] != '\0'); 832223306Smarcel 833223306Smarcel /* 834223306Smarcel * Treat '..' specially, because it only changes our current 835223306Smarcel * directory. We don't create a node for it. We simply ignore 836223306Smarcel * any keywords that may appear on the line as well. 837223306Smarcel * Going up a directory is a little non-obvious. A directory 838223306Smarcel * node has a corresponding '.' child. The parent of '.' is 839223306Smarcel * not the '.' node of the parent directory, but the directory 840223306Smarcel * node within the parent to which the child relates. However, 841223306Smarcel * going up a directory means we need to find the '.' node to 842223306Smarcel * which the directoy node is linked. This we can do via the 843223306Smarcel * first * pointer, because '.' is always the first entry in a 844223306Smarcel * directory. 845223306Smarcel */ 846223306Smarcel if (IS_DOTDOT(name)) { 847223306Smarcel /* This deals with NULL pointers as well. */ 848223306Smarcel if (mtree_current == mtree_root) { 849223306Smarcel mtree_warning("ignoring .. in root directory"); 850223306Smarcel return (0); 851223306Smarcel } 852223306Smarcel 853223306Smarcel node = mtree_current; 854223306Smarcel 855223306Smarcel assert(node != NULL); 856223306Smarcel assert(IS_DOT(node->name)); 857223306Smarcel assert(node->first == node); 858223306Smarcel 859223306Smarcel /* Get the corresponding directory node in the parent. */ 860223306Smarcel node = mtree_current->parent; 861223306Smarcel 862223306Smarcel assert(node != NULL); 863223306Smarcel assert(!IS_DOT(node->name)); 864223306Smarcel 865223306Smarcel node = node->first; 866223306Smarcel 867223306Smarcel assert(node != NULL); 868223306Smarcel assert(IS_DOT(node->name)); 869223306Smarcel assert(node->first == node); 870223306Smarcel 871223306Smarcel mtree_current = node; 872223306Smarcel return (0); 873223306Smarcel } 874223306Smarcel 875223306Smarcel /* 876223306Smarcel * If we don't have a current directory and the first specification 877223306Smarcel * (either implicit or defined) is not '.', then we need to create 878223306Smarcel * a '.' node first (using a recursive call). 879223306Smarcel */ 880223306Smarcel if (!IS_DOT(name) && mtree_current == NULL) { 881223306Smarcel error = read_mtree_spec1(fp, false, "."); 882223306Smarcel if (error) 883223306Smarcel return (error); 884223306Smarcel } 885223306Smarcel 886223306Smarcel /* 887223306Smarcel * Lookup the name in the current directory (if we have a current 888223306Smarcel * directory) to make sure we do not create multiple nodes for the 889223306Smarcel * same component. For non-definitions, if we find a node with the 890223306Smarcel * same name, simply change the current directory. For definitions 891223306Smarcel * more happens. 892223306Smarcel */ 893223306Smarcel last = NULL; 894223306Smarcel node = mtree_current; 895223306Smarcel while (node != NULL) { 896223306Smarcel assert(node->first == mtree_current); 897223306Smarcel 898223306Smarcel if (strcmp(name, node->name) == 0) { 899223306Smarcel if (def == true) { 900247041Sbrooks if (!dupsok) 901247041Sbrooks mtree_error( 902247041Sbrooks "duplicate definition of %s", 903247041Sbrooks name); 904247041Sbrooks else 905247041Sbrooks mtree_warning( 906247041Sbrooks "duplicate definition of %s", 907247041Sbrooks name); 908223306Smarcel return (0); 909223306Smarcel } 910223306Smarcel 911223306Smarcel if (node->type != S_IFDIR) { 912223306Smarcel mtree_error("%s is not a directory", name); 913223306Smarcel return (0); 914223306Smarcel } 915223306Smarcel 916223306Smarcel assert(!IS_DOT(name)); 917223306Smarcel 918223306Smarcel node = node->child; 919223306Smarcel 920223306Smarcel assert(node != NULL); 921223306Smarcel assert(IS_DOT(node->name)); 922223306Smarcel 923223306Smarcel mtree_current = node; 924223306Smarcel return (0); 925223306Smarcel } 926223306Smarcel 927223306Smarcel last = node; 928223306Smarcel node = last->next; 929223306Smarcel } 930223306Smarcel 931223306Smarcel parent = (mtree_current != NULL) ? mtree_current->parent : NULL; 932223306Smarcel type = (def == false || IS_DOT(name)) ? S_IFDIR : 0; 933223306Smarcel node = create_node(name, type, parent, &mtree_global); 934223306Smarcel if (node == NULL) 935223306Smarcel return (ENOMEM); 936223306Smarcel 937223306Smarcel if (def == true) { 938223306Smarcel error = read_mtree_keywords(fp, node); 939223306Smarcel if (error) { 940223306Smarcel destroy_node(node); 941223306Smarcel return (error); 942223306Smarcel } 943223306Smarcel } 944223306Smarcel 945223306Smarcel node->first = (mtree_current != NULL) ? mtree_current : node; 946223306Smarcel 947223306Smarcel if (last != NULL) 948223306Smarcel last->next = node; 949223306Smarcel 950223306Smarcel if (node->type != S_IFDIR) 951223306Smarcel return (0); 952223306Smarcel 953223306Smarcel if (!IS_DOT(node->name)) { 954223306Smarcel parent = node; 955223306Smarcel node = create_node(".", S_IFDIR, parent, parent); 956223306Smarcel if (node == NULL) { 957223306Smarcel last->next = NULL; 958223306Smarcel destroy_node(parent); 959223306Smarcel return (ENOMEM); 960223306Smarcel } 961223306Smarcel parent->child = node; 962223306Smarcel node->first = node; 963223306Smarcel } 964223306Smarcel 965223306Smarcel assert(node != NULL); 966223306Smarcel assert(IS_DOT(node->name)); 967223306Smarcel assert(node->first == node); 968223306Smarcel 969223306Smarcel mtree_current = node; 970223306Smarcel if (mtree_root == NULL) 971223306Smarcel mtree_root = node; 972223306Smarcel 973223306Smarcel return (0); 974223306Smarcel} 975223306Smarcel 976223306Smarcelstatic int 977223306Smarcelread_mtree_spec(FILE *fp) 978223306Smarcel{ 979223306Smarcel char pathspec[PATH_MAX]; 980223306Smarcel char *cp; 981223306Smarcel int error; 982223306Smarcel 983223306Smarcel error = read_word(fp, pathspec, sizeof(pathspec)); 984223306Smarcel if (error) 985223306Smarcel goto out; 986223306Smarcel 987223306Smarcel cp = strchr(pathspec, '/'); 988223306Smarcel if (cp != NULL) { 989223306Smarcel /* Absolute pathname */ 990223306Smarcel mtree_current = mtree_root; 991223306Smarcel 992223306Smarcel do { 993223306Smarcel *cp++ = '\0'; 994223306Smarcel 995247043Sbrooks /* Disallow '..' as a component. */ 996247043Sbrooks if (IS_DOTDOT(pathspec)) { 997247043Sbrooks mtree_error("absolute path cannot contain " 998247043Sbrooks ".. component"); 999223306Smarcel goto out; 1000223306Smarcel } 1001223306Smarcel 1002247043Sbrooks /* Ignore multiple adjacent slashes and '.'. */ 1003247043Sbrooks if (pathspec[0] != '\0' && !IS_DOT(pathspec)) 1004223306Smarcel error = read_mtree_spec1(fp, false, pathspec); 1005223306Smarcel memmove(pathspec, cp, strlen(cp) + 1); 1006223306Smarcel cp = strchr(pathspec, '/'); 1007223306Smarcel } while (!error && cp != NULL); 1008223306Smarcel 1009223306Smarcel /* Disallow '.' and '..' as the last component. */ 1010223306Smarcel if (!error && (IS_DOT(pathspec) || IS_DOTDOT(pathspec))) { 1011223306Smarcel mtree_error("absolute path cannot contain . or .. " 1012223306Smarcel "components"); 1013223306Smarcel goto out; 1014223306Smarcel } 1015223306Smarcel } 1016223306Smarcel 1017223306Smarcel /* Ignore absolute specfications that end with a slash. */ 1018223306Smarcel if (!error && pathspec[0] != '\0') 1019223306Smarcel error = read_mtree_spec1(fp, true, pathspec); 1020223306Smarcel 1021223306Smarcel out: 1022223306Smarcel skip_to(fp, "\n"); 1023223306Smarcel (void)getc(fp); 1024223306Smarcel return (error); 1025223306Smarcel} 1026223306Smarcel 1027223306Smarcelfsnode * 1028223306Smarcelread_mtree(const char *fname, fsnode *node) 1029223306Smarcel{ 1030223306Smarcel struct mtree_fileinfo *fi; 1031223306Smarcel FILE *fp; 1032223306Smarcel int c, error; 1033223306Smarcel 1034223306Smarcel /* We do not yet support nesting... */ 1035223306Smarcel assert(node == NULL); 1036223306Smarcel 1037223306Smarcel if (strcmp(fname, "-") == 0) 1038223306Smarcel fp = stdin; 1039223306Smarcel else { 1040223306Smarcel fp = fopen(fname, "r"); 1041223306Smarcel if (fp == NULL) 1042223306Smarcel err(1, "Can't open `%s'", fname); 1043223306Smarcel } 1044223306Smarcel 1045223306Smarcel error = mtree_file_push(fname, fp); 1046223306Smarcel if (error) 1047223306Smarcel goto out; 1048223306Smarcel 1049223306Smarcel bzero(&mtree_global, sizeof(mtree_global)); 1050223306Smarcel bzero(&mtree_global_inode, sizeof(mtree_global_inode)); 1051223306Smarcel mtree_global.inode = &mtree_global_inode; 1052223306Smarcel mtree_global_inode.nlink = 1; 1053250605Smarcel mtree_global_inode.st.st_nlink = 1; 1054223306Smarcel mtree_global_inode.st.st_atime = mtree_global_inode.st.st_ctime = 1055223306Smarcel mtree_global_inode.st.st_mtime = time(NULL); 1056223306Smarcel errors = warnings = 0; 1057223306Smarcel 1058223306Smarcel setgroupent(1); 1059223306Smarcel setpassent(1); 1060223306Smarcel 1061223306Smarcel mtree_root = node; 1062223306Smarcel mtree_current = node; 1063223306Smarcel do { 1064223306Smarcel /* Start of a new line... */ 1065223306Smarcel fi = SLIST_FIRST(&mtree_fileinfo); 1066223306Smarcel fi->line++; 1067223306Smarcel 1068223306Smarcel error = skip_over(fp, " \t"); 1069223306Smarcel if (error) 1070223306Smarcel break; 1071223306Smarcel 1072223306Smarcel c = getc(fp); 1073223306Smarcel if (c == EOF) { 1074223306Smarcel error = ferror(fp) ? errno : -1; 1075223306Smarcel break; 1076223306Smarcel } 1077223306Smarcel 1078223306Smarcel switch (c) { 1079223306Smarcel case '\n': /* empty line */ 1080223306Smarcel error = 0; 1081223306Smarcel break; 1082223306Smarcel case '#': /* comment -- skip to end of line. */ 1083223306Smarcel error = skip_to(fp, "\n"); 1084223306Smarcel if (!error) 1085223306Smarcel (void)getc(fp); 1086223306Smarcel break; 1087223306Smarcel case '/': /* special commands */ 1088223306Smarcel error = read_mtree_command(fp); 1089223306Smarcel break; 1090223306Smarcel default: /* specification */ 1091223306Smarcel ungetc(c, fp); 1092223306Smarcel error = read_mtree_spec(fp); 1093223306Smarcel break; 1094223306Smarcel } 1095223306Smarcel } while (!error); 1096223306Smarcel 1097223306Smarcel endpwent(); 1098223306Smarcel endgrent(); 1099223306Smarcel 1100223306Smarcel if (error <= 0 && (errors || warnings)) { 1101223306Smarcel warnx("%u error(s) and %u warning(s) in mtree manifest", 1102223306Smarcel errors, warnings); 1103223306Smarcel if (errors) 1104223306Smarcel exit(1); 1105223306Smarcel } 1106223306Smarcel 1107223306Smarcel out: 1108223306Smarcel if (error > 0) 1109223306Smarcel errc(1, error, "Error reading mtree file"); 1110223306Smarcel 1111223306Smarcel if (fp != stdin) 1112223306Smarcel fclose(fp); 1113223306Smarcel 1114223306Smarcel if (mtree_root != NULL) 1115223306Smarcel return (mtree_root); 1116223306Smarcel 1117223306Smarcel /* Handle empty specifications. */ 1118223306Smarcel node = create_node(".", S_IFDIR, NULL, &mtree_global); 1119223306Smarcel node->first = node; 1120223306Smarcel return (node); 1121223306Smarcel} 1122