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: releng/11.0/usr.sbin/makefs/mtree.c 299953 2016-05-16 16:16:46Z truckman $"); 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; 153299953Struckman for (pnode = node->parent; pnode && depth < MAKEFS_MAX_TREE_DEPTH - 1; 154242501Ssjg pnode = pnode->parent) { 155242501Ssjg if (strcmp(pnode->name, ".") == 0) 156242501Ssjg break; 157242501Ssjg rp[++depth] = pnode->name; 158242501Ssjg } 159242501Ssjg 160264186Smarcel sb = sbuf_new_auto(); 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; 184287417Sdelphij char *res, *var = NULL; 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); 287287417Sdelphij var = NULL; 288223306Smarcel } 289223306Smarcel 290287417Sdelphij free(var); 291223306Smarcel sbuf_finish(sb); 292223306Smarcel res = (error == 0) ? strdup(sbuf_data(sb)) : NULL; 293223306Smarcel sbuf_delete(sb); 294223306Smarcel if (res == NULL) 295223306Smarcel errno = ENOMEM; 296223306Smarcel return (res); 297223306Smarcel} 298223306Smarcel 299223306Smarcelstatic int 300223306Smarcelskip_over(FILE *fp, const char *cs) 301223306Smarcel{ 302223306Smarcel int c; 303223306Smarcel 304223306Smarcel c = getc(fp); 305223306Smarcel while (c != EOF && strchr(cs, c) != NULL) 306223306Smarcel c = getc(fp); 307223306Smarcel if (c != EOF) { 308223306Smarcel ungetc(c, fp); 309223306Smarcel return (0); 310223306Smarcel } 311223306Smarcel return (ferror(fp) ? errno : -1); 312223306Smarcel} 313223306Smarcel 314223306Smarcelstatic int 315223306Smarcelskip_to(FILE *fp, const char *cs) 316223306Smarcel{ 317223306Smarcel int c; 318223306Smarcel 319223306Smarcel c = getc(fp); 320223306Smarcel while (c != EOF && strchr(cs, c) == NULL) 321223306Smarcel c = getc(fp); 322223306Smarcel if (c != EOF) { 323223306Smarcel ungetc(c, fp); 324223306Smarcel return (0); 325223306Smarcel } 326223306Smarcel return (ferror(fp) ? errno : -1); 327223306Smarcel} 328223306Smarcel 329223306Smarcelstatic int 330223306Smarcelread_word(FILE *fp, char *buf, size_t bufsz) 331223306Smarcel{ 332223306Smarcel struct mtree_fileinfo *fi; 333223306Smarcel size_t idx, qidx; 334223306Smarcel int c, done, error, esc, qlvl; 335223306Smarcel 336223306Smarcel if (bufsz == 0) 337223306Smarcel return (EINVAL); 338223306Smarcel 339223306Smarcel done = 0; 340223306Smarcel esc = 0; 341223306Smarcel idx = 0; 342223306Smarcel qidx = -1; 343223306Smarcel qlvl = 0; 344223306Smarcel do { 345223306Smarcel c = getc(fp); 346223306Smarcel switch (c) { 347223306Smarcel case EOF: 348223306Smarcel buf[idx] = '\0'; 349223306Smarcel error = ferror(fp) ? errno : -1; 350223306Smarcel if (error == -1) 351223306Smarcel mtree_error("unexpected end of file"); 352223306Smarcel return (error); 353262749Ssjg case '#': /* comment -- skip to end of line. */ 354262749Ssjg if (!esc) { 355262749Ssjg error = skip_to(fp, "\n"); 356262749Ssjg if (!error) 357262749Ssjg continue; 358262749Ssjg } 359262749Ssjg break; 360223306Smarcel case '\\': 361223306Smarcel esc++; 362223306Smarcel if (esc == 1) 363223306Smarcel continue; 364223306Smarcel break; 365223306Smarcel case '`': 366223306Smarcel case '\'': 367223306Smarcel case '"': 368223306Smarcel if (esc) 369223306Smarcel break; 370223306Smarcel if (qlvl == 0) { 371223306Smarcel qlvl++; 372223306Smarcel qidx = idx; 373223306Smarcel } else if (c == buf[qidx]) { 374223306Smarcel qlvl--; 375223306Smarcel if (qlvl > 0) { 376223306Smarcel do { 377223306Smarcel qidx--; 378223306Smarcel } while (buf[qidx] != '`' && 379223306Smarcel buf[qidx] != '\'' && 380223306Smarcel buf[qidx] != '"'); 381223306Smarcel } else 382223306Smarcel qidx = -1; 383223306Smarcel } else { 384223306Smarcel qlvl++; 385223306Smarcel qidx = idx; 386223306Smarcel } 387223306Smarcel break; 388223306Smarcel case ' ': 389223306Smarcel case '\t': 390223306Smarcel case '\n': 391223306Smarcel if (!esc && qlvl == 0) { 392223306Smarcel ungetc(c, fp); 393223306Smarcel c = '\0'; 394223306Smarcel done = 1; 395223306Smarcel break; 396223306Smarcel } 397223306Smarcel if (c == '\n') { 398223306Smarcel /* 399223306Smarcel * We going to eat the newline ourselves. 400223306Smarcel */ 401223306Smarcel if (qlvl > 0) 402223306Smarcel mtree_warning("quoted word straddles " 403223306Smarcel "onto next line."); 404223306Smarcel fi = SLIST_FIRST(&mtree_fileinfo); 405223306Smarcel fi->line++; 406223306Smarcel } 407223306Smarcel break; 408223306Smarcel case 'a': 409223306Smarcel if (esc) 410223306Smarcel c = '\a'; 411223306Smarcel break; 412223306Smarcel case 'b': 413223306Smarcel if (esc) 414223306Smarcel c = '\b'; 415223306Smarcel break; 416223306Smarcel case 'f': 417223306Smarcel if (esc) 418223306Smarcel c = '\f'; 419223306Smarcel break; 420223306Smarcel case 'n': 421223306Smarcel if (esc) 422223306Smarcel c = '\n'; 423223306Smarcel break; 424223306Smarcel case 'r': 425223306Smarcel if (esc) 426223306Smarcel c = '\r'; 427223306Smarcel break; 428223306Smarcel case 't': 429223306Smarcel if (esc) 430223306Smarcel c = '\t'; 431223306Smarcel break; 432223306Smarcel case 'v': 433223306Smarcel if (esc) 434223306Smarcel c = '\v'; 435223306Smarcel break; 436223306Smarcel } 437223306Smarcel buf[idx++] = c; 438223306Smarcel esc = 0; 439223306Smarcel } while (idx < bufsz && !done); 440223306Smarcel 441223306Smarcel if (idx >= bufsz) { 442223306Smarcel mtree_error("word too long to fit buffer (max %zu characters)", 443223306Smarcel bufsz); 444223306Smarcel skip_to(fp, " \t\n"); 445223306Smarcel } 446223306Smarcel return (0); 447223306Smarcel} 448223306Smarcel 449223306Smarcelstatic fsnode * 450223306Smarcelcreate_node(const char *name, u_int type, fsnode *parent, fsnode *global) 451223306Smarcel{ 452223306Smarcel fsnode *n; 453223306Smarcel 454223306Smarcel n = calloc(1, sizeof(*n)); 455223306Smarcel if (n == NULL) 456223306Smarcel return (NULL); 457223306Smarcel 458223306Smarcel n->name = strdup(name); 459223306Smarcel if (n->name == NULL) { 460223306Smarcel free(n); 461223306Smarcel return (NULL); 462223306Smarcel } 463223306Smarcel 464223306Smarcel n->type = (type == 0) ? global->type : type; 465223306Smarcel n->parent = parent; 466223306Smarcel 467223306Smarcel n->inode = calloc(1, sizeof(*n->inode)); 468223306Smarcel if (n->inode == NULL) { 469223306Smarcel free(n->name); 470223306Smarcel free(n); 471223306Smarcel return (NULL); 472223306Smarcel } 473223306Smarcel 474223306Smarcel /* Assign global options/defaults. */ 475223306Smarcel bcopy(global->inode, n->inode, sizeof(*n->inode)); 476223306Smarcel n->inode->st.st_mode = (n->inode->st.st_mode & ~S_IFMT) | n->type; 477223306Smarcel 478223306Smarcel if (n->type == S_IFLNK) 479223306Smarcel n->symlink = global->symlink; 480223306Smarcel else if (n->type == S_IFREG) 481223306Smarcel n->contents = global->contents; 482223306Smarcel 483223306Smarcel return (n); 484223306Smarcel} 485223306Smarcel 486223306Smarcelstatic void 487223306Smarceldestroy_node(fsnode *n) 488223306Smarcel{ 489223306Smarcel 490223306Smarcel assert(n != NULL); 491223306Smarcel assert(n->name != NULL); 492223306Smarcel assert(n->inode != NULL); 493223306Smarcel 494223306Smarcel free(n->inode); 495223306Smarcel free(n->name); 496223306Smarcel free(n); 497223306Smarcel} 498223306Smarcel 499223306Smarcelstatic int 500223306Smarcelread_number(const char *tok, u_int base, intmax_t *res, intmax_t min, 501223306Smarcel intmax_t max) 502223306Smarcel{ 503223306Smarcel char *end; 504223306Smarcel intmax_t val; 505223306Smarcel 506223306Smarcel val = strtoimax(tok, &end, base); 507223306Smarcel if (end == tok || end[0] != '\0') 508223306Smarcel return (EINVAL); 509223306Smarcel if (val < min || val > max) 510223306Smarcel return (EDOM); 511223306Smarcel *res = val; 512223306Smarcel return (0); 513223306Smarcel} 514223306Smarcel 515223306Smarcelstatic int 516223306Smarcelread_mtree_keywords(FILE *fp, fsnode *node) 517223306Smarcel{ 518223306Smarcel char keyword[PATH_MAX]; 519223306Smarcel char *name, *p, *value; 520247042Sbrooks gid_t gid; 521247042Sbrooks uid_t uid; 522223306Smarcel struct stat *st, sb; 523223306Smarcel intmax_t num; 524223306Smarcel u_long flset, flclr; 525223306Smarcel int error, istemp, type; 526223306Smarcel 527223306Smarcel st = &node->inode->st; 528223306Smarcel do { 529223306Smarcel error = skip_over(fp, " \t"); 530223306Smarcel if (error) 531223306Smarcel break; 532223306Smarcel 533223306Smarcel error = read_word(fp, keyword, sizeof(keyword)); 534223306Smarcel if (error) 535223306Smarcel break; 536223306Smarcel 537223306Smarcel if (keyword[0] == '\0') 538223306Smarcel break; 539223306Smarcel 540223306Smarcel value = strchr(keyword, '='); 541223306Smarcel if (value != NULL) 542223306Smarcel *value++ = '\0'; 543223306Smarcel 544223306Smarcel /* 545223306Smarcel * We use EINVAL, ENOATTR, ENOSYS and ENXIO to signal 546223306Smarcel * certain conditions: 547223306Smarcel * EINVAL - Value provided for a keyword that does 548223306Smarcel * not take a value. The value is ignored. 549223306Smarcel * ENOATTR - Value missing for a keyword that needs 550223306Smarcel * a value. The keyword is ignored. 551223306Smarcel * ENOSYS - Unsupported keyword encountered. The 552223306Smarcel * keyword is ignored. 553223306Smarcel * ENXIO - Value provided for a keyword that does 554223306Smarcel * not take a value. The value is ignored. 555223306Smarcel */ 556223306Smarcel switch (keyword[0]) { 557223306Smarcel case 'c': 558223306Smarcel if (strcmp(keyword, "contents") == 0) { 559223306Smarcel if (value == NULL) { 560223306Smarcel error = ENOATTR; 561223306Smarcel break; 562223306Smarcel } 563223306Smarcel node->contents = strdup(value); 564223306Smarcel } else 565223306Smarcel error = ENOSYS; 566223306Smarcel break; 567223306Smarcel case 'f': 568223306Smarcel if (strcmp(keyword, "flags") == 0) { 569223306Smarcel if (value == NULL) { 570223306Smarcel error = ENOATTR; 571223306Smarcel break; 572223306Smarcel } 573223306Smarcel flset = flclr = 0; 574223306Smarcel if (!strtofflags(&value, &flset, &flclr)) { 575223306Smarcel st->st_flags &= ~flclr; 576223306Smarcel st->st_flags |= flset; 577223306Smarcel } else 578223306Smarcel error = errno; 579223306Smarcel } else 580223306Smarcel error = ENOSYS; 581223306Smarcel break; 582223306Smarcel case 'g': 583223306Smarcel if (strcmp(keyword, "gid") == 0) { 584223306Smarcel if (value == NULL) { 585223306Smarcel error = ENOATTR; 586223306Smarcel break; 587223306Smarcel } 588223306Smarcel error = read_number(value, 10, &num, 589223306Smarcel 0, UINT_MAX); 590223306Smarcel if (!error) 591223306Smarcel st->st_gid = num; 592223306Smarcel } else if (strcmp(keyword, "gname") == 0) { 593223306Smarcel if (value == NULL) { 594223306Smarcel error = ENOATTR; 595223306Smarcel break; 596223306Smarcel } 597247042Sbrooks if (gid_from_group(value, &gid) == 0) 598247042Sbrooks st->st_gid = gid; 599223306Smarcel else 600247042Sbrooks error = EINVAL; 601223306Smarcel } else 602223306Smarcel error = ENOSYS; 603223306Smarcel break; 604223306Smarcel case 'l': 605223306Smarcel if (strcmp(keyword, "link") == 0) { 606223306Smarcel if (value == NULL) { 607223306Smarcel error = ENOATTR; 608223306Smarcel break; 609223306Smarcel } 610223306Smarcel node->symlink = strdup(value); 611223306Smarcel } else 612223306Smarcel error = ENOSYS; 613223306Smarcel break; 614223306Smarcel case 'm': 615223306Smarcel if (strcmp(keyword, "mode") == 0) { 616223306Smarcel if (value == NULL) { 617223306Smarcel error = ENOATTR; 618223306Smarcel break; 619223306Smarcel } 620223306Smarcel if (value[0] >= '0' && value[0] <= '9') { 621223306Smarcel error = read_number(value, 8, &num, 622223306Smarcel 0, 07777); 623223306Smarcel if (!error) { 624223306Smarcel st->st_mode &= S_IFMT; 625223306Smarcel st->st_mode |= num; 626223306Smarcel } 627223306Smarcel } else { 628223306Smarcel /* Symbolic mode not supported. */ 629223306Smarcel error = EINVAL; 630223306Smarcel break; 631223306Smarcel } 632223306Smarcel } else 633223306Smarcel error = ENOSYS; 634223306Smarcel break; 635223306Smarcel case 'o': 636223306Smarcel if (strcmp(keyword, "optional") == 0) { 637223306Smarcel if (value != NULL) 638223306Smarcel error = ENXIO; 639223306Smarcel node->flags |= FSNODE_F_OPTIONAL; 640223306Smarcel } else 641223306Smarcel error = ENOSYS; 642223306Smarcel break; 643223306Smarcel case 's': 644223306Smarcel if (strcmp(keyword, "size") == 0) { 645223306Smarcel if (value == NULL) { 646223306Smarcel error = ENOATTR; 647223306Smarcel break; 648223306Smarcel } 649223306Smarcel error = read_number(value, 10, &num, 650223306Smarcel 0, INTMAX_MAX); 651223306Smarcel if (!error) 652223306Smarcel st->st_size = num; 653223306Smarcel } else 654223306Smarcel error = ENOSYS; 655223306Smarcel break; 656223306Smarcel case 't': 657223306Smarcel if (strcmp(keyword, "time") == 0) { 658223306Smarcel if (value == NULL) { 659223306Smarcel error = ENOATTR; 660223306Smarcel break; 661223306Smarcel } 662223306Smarcel p = strchr(value, '.'); 663223306Smarcel if (p != NULL) 664223306Smarcel *p++ = '\0'; 665223306Smarcel error = read_number(value, 10, &num, 0, 666223306Smarcel INTMAX_MAX); 667223306Smarcel if (error) 668223306Smarcel break; 669223306Smarcel st->st_atime = num; 670223306Smarcel st->st_ctime = num; 671223306Smarcel st->st_mtime = num; 672264186Smarcel if (p == NULL) 673264186Smarcel break; 674223306Smarcel error = read_number(p, 10, &num, 0, 675223306Smarcel INTMAX_MAX); 676223306Smarcel if (error) 677223306Smarcel break; 678223306Smarcel if (num != 0) 679223306Smarcel error = EINVAL; 680223306Smarcel } else if (strcmp(keyword, "type") == 0) { 681223306Smarcel if (value == NULL) { 682223306Smarcel error = ENOATTR; 683223306Smarcel break; 684223306Smarcel } 685223306Smarcel if (strcmp(value, "dir") == 0) 686223306Smarcel node->type = S_IFDIR; 687223306Smarcel else if (strcmp(value, "file") == 0) 688223306Smarcel node->type = S_IFREG; 689223306Smarcel else if (strcmp(value, "link") == 0) 690223306Smarcel node->type = S_IFLNK; 691223306Smarcel else 692223306Smarcel error = EINVAL; 693223306Smarcel } else 694223306Smarcel error = ENOSYS; 695223306Smarcel break; 696223306Smarcel case 'u': 697223306Smarcel if (strcmp(keyword, "uid") == 0) { 698223306Smarcel if (value == NULL) { 699223306Smarcel error = ENOATTR; 700223306Smarcel break; 701223306Smarcel } 702223306Smarcel error = read_number(value, 10, &num, 703223306Smarcel 0, UINT_MAX); 704223306Smarcel if (!error) 705223306Smarcel st->st_uid = num; 706223306Smarcel } else if (strcmp(keyword, "uname") == 0) { 707223306Smarcel if (value == NULL) { 708223306Smarcel error = ENOATTR; 709223306Smarcel break; 710223306Smarcel } 711247042Sbrooks if (uid_from_user(value, &uid) == 0) 712247042Sbrooks st->st_uid = uid; 713223306Smarcel else 714247042Sbrooks error = EINVAL; 715223306Smarcel } else 716223306Smarcel error = ENOSYS; 717223306Smarcel break; 718223306Smarcel default: 719223306Smarcel error = ENOSYS; 720223306Smarcel break; 721223306Smarcel } 722223306Smarcel 723223306Smarcel switch (error) { 724223306Smarcel case EINVAL: 725223306Smarcel mtree_error("%s: invalid value '%s'", keyword, value); 726223306Smarcel break; 727223306Smarcel case ENOATTR: 728223306Smarcel mtree_error("%s: keyword needs a value", keyword); 729223306Smarcel break; 730223306Smarcel case ENOSYS: 731223306Smarcel mtree_warning("%s: unsupported keyword", keyword); 732223306Smarcel break; 733223306Smarcel case ENXIO: 734223306Smarcel mtree_error("%s: keyword does not take a value", 735223306Smarcel keyword); 736223306Smarcel break; 737223306Smarcel } 738223306Smarcel } while (1); 739223306Smarcel 740223306Smarcel if (error) 741223306Smarcel return (error); 742223306Smarcel 743223306Smarcel st->st_mode = (st->st_mode & ~S_IFMT) | node->type; 744223306Smarcel 745223306Smarcel /* Nothing more to do for the global defaults. */ 746223306Smarcel if (node->name == NULL) 747223306Smarcel return (0); 748223306Smarcel 749223306Smarcel /* 750223306Smarcel * Be intelligent about the file type. 751223306Smarcel */ 752223306Smarcel if (node->contents != NULL) { 753223306Smarcel if (node->symlink != NULL) { 754223306Smarcel mtree_error("%s: both link and contents keywords " 755223306Smarcel "defined", node->name); 756223306Smarcel return (0); 757223306Smarcel } 758223306Smarcel type = S_IFREG; 759242501Ssjg } else if (node->type != 0) { 760242501Ssjg type = node->type; 761242501Ssjg if (type == S_IFREG) { 762242501Ssjg /* the named path is the default contents */ 763242501Ssjg node->contents = mtree_file_path(node); 764242501Ssjg } 765223306Smarcel } else 766223306Smarcel type = (node->symlink != NULL) ? S_IFLNK : S_IFDIR; 767223306Smarcel 768223306Smarcel if (node->type == 0) 769223306Smarcel node->type = type; 770223306Smarcel 771223306Smarcel if (node->type != type) { 772223306Smarcel mtree_error("%s: file type and defined keywords to not match", 773223306Smarcel node->name); 774223306Smarcel return (0); 775223306Smarcel } 776223306Smarcel 777223306Smarcel st->st_mode = (st->st_mode & ~S_IFMT) | node->type; 778223306Smarcel 779223306Smarcel if (node->contents == NULL) 780223306Smarcel return (0); 781223306Smarcel 782223306Smarcel name = mtree_resolve(node->contents, &istemp); 783223306Smarcel if (name == NULL) 784223306Smarcel return (errno); 785223306Smarcel 786223306Smarcel if (stat(name, &sb) != 0) { 787223306Smarcel mtree_error("%s: contents file '%s' not found", node->name, 788223306Smarcel name); 789223306Smarcel free(name); 790223306Smarcel return (0); 791223306Smarcel } 792223306Smarcel 793247052Sbrooks /* 794247052Sbrooks * Check for hardlinks. If the contents key is used, then the check 795247052Sbrooks * will only trigger if the contents file is a link even if it is used 796247052Sbrooks * by more than one file 797247052Sbrooks */ 798247052Sbrooks if (sb.st_nlink > 1) { 799247052Sbrooks fsinode *curino; 800247052Sbrooks 801247052Sbrooks st->st_ino = sb.st_ino; 802247052Sbrooks st->st_dev = sb.st_dev; 803247052Sbrooks curino = link_check(node->inode); 804247052Sbrooks if (curino != NULL) { 805247052Sbrooks free(node->inode); 806247052Sbrooks node->inode = curino; 807247052Sbrooks node->inode->nlink++; 808247052Sbrooks } 809247052Sbrooks } 810247052Sbrooks 811223306Smarcel free(node->contents); 812223306Smarcel node->contents = name; 813223306Smarcel st->st_size = sb.st_size; 814223306Smarcel return (0); 815223306Smarcel} 816223306Smarcel 817223306Smarcelstatic int 818223306Smarcelread_mtree_command(FILE *fp) 819223306Smarcel{ 820223306Smarcel char cmd[10]; 821223306Smarcel int error; 822223306Smarcel 823223306Smarcel error = read_word(fp, cmd, sizeof(cmd)); 824223306Smarcel if (error) 825223306Smarcel goto out; 826223306Smarcel 827223306Smarcel error = read_mtree_keywords(fp, &mtree_global); 828223306Smarcel 829223306Smarcel out: 830223306Smarcel skip_to(fp, "\n"); 831223306Smarcel (void)getc(fp); 832223306Smarcel return (error); 833223306Smarcel} 834223306Smarcel 835223306Smarcelstatic int 836223306Smarcelread_mtree_spec1(FILE *fp, bool def, const char *name) 837223306Smarcel{ 838223306Smarcel fsnode *last, *node, *parent; 839223306Smarcel u_int type; 840223306Smarcel int error; 841223306Smarcel 842223306Smarcel assert(name[0] != '\0'); 843223306Smarcel 844223306Smarcel /* 845223306Smarcel * Treat '..' specially, because it only changes our current 846223306Smarcel * directory. We don't create a node for it. We simply ignore 847223306Smarcel * any keywords that may appear on the line as well. 848223306Smarcel * Going up a directory is a little non-obvious. A directory 849223306Smarcel * node has a corresponding '.' child. The parent of '.' is 850223306Smarcel * not the '.' node of the parent directory, but the directory 851223306Smarcel * node within the parent to which the child relates. However, 852223306Smarcel * going up a directory means we need to find the '.' node to 853223306Smarcel * which the directoy node is linked. This we can do via the 854223306Smarcel * first * pointer, because '.' is always the first entry in a 855223306Smarcel * directory. 856223306Smarcel */ 857223306Smarcel if (IS_DOTDOT(name)) { 858223306Smarcel /* This deals with NULL pointers as well. */ 859223306Smarcel if (mtree_current == mtree_root) { 860223306Smarcel mtree_warning("ignoring .. in root directory"); 861223306Smarcel return (0); 862223306Smarcel } 863223306Smarcel 864223306Smarcel node = mtree_current; 865223306Smarcel 866223306Smarcel assert(node != NULL); 867223306Smarcel assert(IS_DOT(node->name)); 868223306Smarcel assert(node->first == node); 869223306Smarcel 870223306Smarcel /* Get the corresponding directory node in the parent. */ 871223306Smarcel node = mtree_current->parent; 872223306Smarcel 873223306Smarcel assert(node != NULL); 874223306Smarcel assert(!IS_DOT(node->name)); 875223306Smarcel 876223306Smarcel node = node->first; 877223306Smarcel 878223306Smarcel assert(node != NULL); 879223306Smarcel assert(IS_DOT(node->name)); 880223306Smarcel assert(node->first == node); 881223306Smarcel 882223306Smarcel mtree_current = node; 883223306Smarcel return (0); 884223306Smarcel } 885223306Smarcel 886223306Smarcel /* 887223306Smarcel * If we don't have a current directory and the first specification 888223306Smarcel * (either implicit or defined) is not '.', then we need to create 889223306Smarcel * a '.' node first (using a recursive call). 890223306Smarcel */ 891223306Smarcel if (!IS_DOT(name) && mtree_current == NULL) { 892223306Smarcel error = read_mtree_spec1(fp, false, "."); 893223306Smarcel if (error) 894223306Smarcel return (error); 895223306Smarcel } 896223306Smarcel 897223306Smarcel /* 898223306Smarcel * Lookup the name in the current directory (if we have a current 899223306Smarcel * directory) to make sure we do not create multiple nodes for the 900223306Smarcel * same component. For non-definitions, if we find a node with the 901223306Smarcel * same name, simply change the current directory. For definitions 902223306Smarcel * more happens. 903223306Smarcel */ 904223306Smarcel last = NULL; 905223306Smarcel node = mtree_current; 906223306Smarcel while (node != NULL) { 907223306Smarcel assert(node->first == mtree_current); 908223306Smarcel 909223306Smarcel if (strcmp(name, node->name) == 0) { 910223306Smarcel if (def == true) { 911247041Sbrooks if (!dupsok) 912247041Sbrooks mtree_error( 913247041Sbrooks "duplicate definition of %s", 914247041Sbrooks name); 915247041Sbrooks else 916247041Sbrooks mtree_warning( 917247041Sbrooks "duplicate definition of %s", 918247041Sbrooks name); 919223306Smarcel return (0); 920223306Smarcel } 921223306Smarcel 922223306Smarcel if (node->type != S_IFDIR) { 923223306Smarcel mtree_error("%s is not a directory", name); 924223306Smarcel return (0); 925223306Smarcel } 926223306Smarcel 927223306Smarcel assert(!IS_DOT(name)); 928223306Smarcel 929223306Smarcel node = node->child; 930223306Smarcel 931223306Smarcel assert(node != NULL); 932223306Smarcel assert(IS_DOT(node->name)); 933223306Smarcel 934223306Smarcel mtree_current = node; 935223306Smarcel return (0); 936223306Smarcel } 937223306Smarcel 938223306Smarcel last = node; 939223306Smarcel node = last->next; 940223306Smarcel } 941223306Smarcel 942223306Smarcel parent = (mtree_current != NULL) ? mtree_current->parent : NULL; 943223306Smarcel type = (def == false || IS_DOT(name)) ? S_IFDIR : 0; 944223306Smarcel node = create_node(name, type, parent, &mtree_global); 945223306Smarcel if (node == NULL) 946223306Smarcel return (ENOMEM); 947223306Smarcel 948223306Smarcel if (def == true) { 949223306Smarcel error = read_mtree_keywords(fp, node); 950223306Smarcel if (error) { 951223306Smarcel destroy_node(node); 952223306Smarcel return (error); 953223306Smarcel } 954223306Smarcel } 955223306Smarcel 956223306Smarcel node->first = (mtree_current != NULL) ? mtree_current : node; 957223306Smarcel 958223306Smarcel if (last != NULL) 959223306Smarcel last->next = node; 960223306Smarcel 961223306Smarcel if (node->type != S_IFDIR) 962223306Smarcel return (0); 963223306Smarcel 964223306Smarcel if (!IS_DOT(node->name)) { 965223306Smarcel parent = node; 966223306Smarcel node = create_node(".", S_IFDIR, parent, parent); 967223306Smarcel if (node == NULL) { 968223306Smarcel last->next = NULL; 969223306Smarcel destroy_node(parent); 970223306Smarcel return (ENOMEM); 971223306Smarcel } 972223306Smarcel parent->child = node; 973223306Smarcel node->first = node; 974223306Smarcel } 975223306Smarcel 976223306Smarcel assert(node != NULL); 977223306Smarcel assert(IS_DOT(node->name)); 978223306Smarcel assert(node->first == node); 979223306Smarcel 980223306Smarcel mtree_current = node; 981223306Smarcel if (mtree_root == NULL) 982223306Smarcel mtree_root = node; 983223306Smarcel 984223306Smarcel return (0); 985223306Smarcel} 986223306Smarcel 987223306Smarcelstatic int 988223306Smarcelread_mtree_spec(FILE *fp) 989223306Smarcel{ 990223306Smarcel char pathspec[PATH_MAX]; 991223306Smarcel char *cp; 992223306Smarcel int error; 993223306Smarcel 994223306Smarcel error = read_word(fp, pathspec, sizeof(pathspec)); 995223306Smarcel if (error) 996223306Smarcel goto out; 997223306Smarcel 998223306Smarcel cp = strchr(pathspec, '/'); 999223306Smarcel if (cp != NULL) { 1000223306Smarcel /* Absolute pathname */ 1001223306Smarcel mtree_current = mtree_root; 1002223306Smarcel 1003223306Smarcel do { 1004223306Smarcel *cp++ = '\0'; 1005223306Smarcel 1006247043Sbrooks /* Disallow '..' as a component. */ 1007247043Sbrooks if (IS_DOTDOT(pathspec)) { 1008247043Sbrooks mtree_error("absolute path cannot contain " 1009247043Sbrooks ".. component"); 1010223306Smarcel goto out; 1011223306Smarcel } 1012223306Smarcel 1013247043Sbrooks /* Ignore multiple adjacent slashes and '.'. */ 1014247043Sbrooks if (pathspec[0] != '\0' && !IS_DOT(pathspec)) 1015223306Smarcel error = read_mtree_spec1(fp, false, pathspec); 1016223306Smarcel memmove(pathspec, cp, strlen(cp) + 1); 1017223306Smarcel cp = strchr(pathspec, '/'); 1018223306Smarcel } while (!error && cp != NULL); 1019223306Smarcel 1020223306Smarcel /* Disallow '.' and '..' as the last component. */ 1021223306Smarcel if (!error && (IS_DOT(pathspec) || IS_DOTDOT(pathspec))) { 1022223306Smarcel mtree_error("absolute path cannot contain . or .. " 1023223306Smarcel "components"); 1024223306Smarcel goto out; 1025223306Smarcel } 1026223306Smarcel } 1027223306Smarcel 1028223306Smarcel /* Ignore absolute specfications that end with a slash. */ 1029223306Smarcel if (!error && pathspec[0] != '\0') 1030223306Smarcel error = read_mtree_spec1(fp, true, pathspec); 1031223306Smarcel 1032223306Smarcel out: 1033223306Smarcel skip_to(fp, "\n"); 1034223306Smarcel (void)getc(fp); 1035223306Smarcel return (error); 1036223306Smarcel} 1037223306Smarcel 1038223306Smarcelfsnode * 1039223306Smarcelread_mtree(const char *fname, fsnode *node) 1040223306Smarcel{ 1041223306Smarcel struct mtree_fileinfo *fi; 1042223306Smarcel FILE *fp; 1043223306Smarcel int c, error; 1044223306Smarcel 1045223306Smarcel /* We do not yet support nesting... */ 1046223306Smarcel assert(node == NULL); 1047223306Smarcel 1048223306Smarcel if (strcmp(fname, "-") == 0) 1049223306Smarcel fp = stdin; 1050223306Smarcel else { 1051223306Smarcel fp = fopen(fname, "r"); 1052223306Smarcel if (fp == NULL) 1053223306Smarcel err(1, "Can't open `%s'", fname); 1054223306Smarcel } 1055223306Smarcel 1056223306Smarcel error = mtree_file_push(fname, fp); 1057223306Smarcel if (error) 1058223306Smarcel goto out; 1059223306Smarcel 1060223306Smarcel bzero(&mtree_global, sizeof(mtree_global)); 1061223306Smarcel bzero(&mtree_global_inode, sizeof(mtree_global_inode)); 1062223306Smarcel mtree_global.inode = &mtree_global_inode; 1063223306Smarcel mtree_global_inode.nlink = 1; 1064250605Smarcel mtree_global_inode.st.st_nlink = 1; 1065223306Smarcel mtree_global_inode.st.st_atime = mtree_global_inode.st.st_ctime = 1066223306Smarcel mtree_global_inode.st.st_mtime = time(NULL); 1067223306Smarcel errors = warnings = 0; 1068223306Smarcel 1069223306Smarcel setgroupent(1); 1070223306Smarcel setpassent(1); 1071223306Smarcel 1072223306Smarcel mtree_root = node; 1073223306Smarcel mtree_current = node; 1074223306Smarcel do { 1075223306Smarcel /* Start of a new line... */ 1076223306Smarcel fi = SLIST_FIRST(&mtree_fileinfo); 1077223306Smarcel fi->line++; 1078223306Smarcel 1079223306Smarcel error = skip_over(fp, " \t"); 1080223306Smarcel if (error) 1081223306Smarcel break; 1082223306Smarcel 1083223306Smarcel c = getc(fp); 1084223306Smarcel if (c == EOF) { 1085223306Smarcel error = ferror(fp) ? errno : -1; 1086223306Smarcel break; 1087223306Smarcel } 1088223306Smarcel 1089223306Smarcel switch (c) { 1090223306Smarcel case '\n': /* empty line */ 1091223306Smarcel error = 0; 1092223306Smarcel break; 1093223306Smarcel case '#': /* comment -- skip to end of line. */ 1094223306Smarcel error = skip_to(fp, "\n"); 1095223306Smarcel if (!error) 1096223306Smarcel (void)getc(fp); 1097223306Smarcel break; 1098223306Smarcel case '/': /* special commands */ 1099223306Smarcel error = read_mtree_command(fp); 1100223306Smarcel break; 1101223306Smarcel default: /* specification */ 1102223306Smarcel ungetc(c, fp); 1103223306Smarcel error = read_mtree_spec(fp); 1104223306Smarcel break; 1105223306Smarcel } 1106223306Smarcel } while (!error); 1107223306Smarcel 1108223306Smarcel endpwent(); 1109223306Smarcel endgrent(); 1110223306Smarcel 1111223306Smarcel if (error <= 0 && (errors || warnings)) { 1112223306Smarcel warnx("%u error(s) and %u warning(s) in mtree manifest", 1113223306Smarcel errors, warnings); 1114223306Smarcel if (errors) 1115223306Smarcel exit(1); 1116223306Smarcel } 1117223306Smarcel 1118223306Smarcel out: 1119223306Smarcel if (error > 0) 1120223306Smarcel errc(1, error, "Error reading mtree file"); 1121223306Smarcel 1122223306Smarcel if (fp != stdin) 1123223306Smarcel fclose(fp); 1124223306Smarcel 1125223306Smarcel if (mtree_root != NULL) 1126223306Smarcel return (mtree_root); 1127223306Smarcel 1128223306Smarcel /* Handle empty specifications. */ 1129223306Smarcel node = create_node(".", S_IFDIR, NULL, &mtree_global); 1130223306Smarcel node->first = node; 1131223306Smarcel return (node); 1132223306Smarcel} 1133