mtree.c revision 242501
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: head/usr.sbin/makefs/mtree.c 242501 2012-11-03 00:10:30Z sjg $"); 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; 511223306Smarcel struct group *grent; 512223306Smarcel struct passwd *pwent; 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 } 588223306Smarcel grent = getgrnam(value); 589223306Smarcel if (grent != NULL) 590223306Smarcel st->st_gid = grent->gr_gid; 591223306Smarcel else 592223306Smarcel error = errno; 593223306Smarcel } else 594223306Smarcel error = ENOSYS; 595223306Smarcel break; 596223306Smarcel case 'l': 597223306Smarcel if (strcmp(keyword, "link") == 0) { 598223306Smarcel if (value == NULL) { 599223306Smarcel error = ENOATTR; 600223306Smarcel break; 601223306Smarcel } 602223306Smarcel node->symlink = strdup(value); 603223306Smarcel } else 604223306Smarcel error = ENOSYS; 605223306Smarcel break; 606223306Smarcel case 'm': 607223306Smarcel if (strcmp(keyword, "mode") == 0) { 608223306Smarcel if (value == NULL) { 609223306Smarcel error = ENOATTR; 610223306Smarcel break; 611223306Smarcel } 612223306Smarcel if (value[0] >= '0' && value[0] <= '9') { 613223306Smarcel error = read_number(value, 8, &num, 614223306Smarcel 0, 07777); 615223306Smarcel if (!error) { 616223306Smarcel st->st_mode &= S_IFMT; 617223306Smarcel st->st_mode |= num; 618223306Smarcel } 619223306Smarcel } else { 620223306Smarcel /* Symbolic mode not supported. */ 621223306Smarcel error = EINVAL; 622223306Smarcel break; 623223306Smarcel } 624223306Smarcel } else 625223306Smarcel error = ENOSYS; 626223306Smarcel break; 627223306Smarcel case 'o': 628223306Smarcel if (strcmp(keyword, "optional") == 0) { 629223306Smarcel if (value != NULL) 630223306Smarcel error = ENXIO; 631223306Smarcel node->flags |= FSNODE_F_OPTIONAL; 632223306Smarcel } else 633223306Smarcel error = ENOSYS; 634223306Smarcel break; 635223306Smarcel case 's': 636223306Smarcel if (strcmp(keyword, "size") == 0) { 637223306Smarcel if (value == NULL) { 638223306Smarcel error = ENOATTR; 639223306Smarcel break; 640223306Smarcel } 641223306Smarcel error = read_number(value, 10, &num, 642223306Smarcel 0, INTMAX_MAX); 643223306Smarcel if (!error) 644223306Smarcel st->st_size = num; 645223306Smarcel } else 646223306Smarcel error = ENOSYS; 647223306Smarcel break; 648223306Smarcel case 't': 649223306Smarcel if (strcmp(keyword, "time") == 0) { 650223306Smarcel if (value == NULL) { 651223306Smarcel error = ENOATTR; 652223306Smarcel break; 653223306Smarcel } 654223306Smarcel p = strchr(value, '.'); 655223306Smarcel if (p != NULL) 656223306Smarcel *p++ = '\0'; 657223306Smarcel error = read_number(value, 10, &num, 0, 658223306Smarcel INTMAX_MAX); 659223306Smarcel if (error) 660223306Smarcel break; 661223306Smarcel st->st_atime = num; 662223306Smarcel st->st_ctime = num; 663223306Smarcel st->st_mtime = num; 664223306Smarcel error = read_number(p, 10, &num, 0, 665223306Smarcel INTMAX_MAX); 666223306Smarcel if (error) 667223306Smarcel break; 668223306Smarcel if (num != 0) 669223306Smarcel error = EINVAL; 670223306Smarcel } else if (strcmp(keyword, "type") == 0) { 671223306Smarcel if (value == NULL) { 672223306Smarcel error = ENOATTR; 673223306Smarcel break; 674223306Smarcel } 675223306Smarcel if (strcmp(value, "dir") == 0) 676223306Smarcel node->type = S_IFDIR; 677223306Smarcel else if (strcmp(value, "file") == 0) 678223306Smarcel node->type = S_IFREG; 679223306Smarcel else if (strcmp(value, "link") == 0) 680223306Smarcel node->type = S_IFLNK; 681223306Smarcel else 682223306Smarcel error = EINVAL; 683223306Smarcel } else 684223306Smarcel error = ENOSYS; 685223306Smarcel break; 686223306Smarcel case 'u': 687223306Smarcel if (strcmp(keyword, "uid") == 0) { 688223306Smarcel if (value == NULL) { 689223306Smarcel error = ENOATTR; 690223306Smarcel break; 691223306Smarcel } 692223306Smarcel error = read_number(value, 10, &num, 693223306Smarcel 0, UINT_MAX); 694223306Smarcel if (!error) 695223306Smarcel st->st_uid = num; 696223306Smarcel } else if (strcmp(keyword, "uname") == 0) { 697223306Smarcel if (value == NULL) { 698223306Smarcel error = ENOATTR; 699223306Smarcel break; 700223306Smarcel } 701223306Smarcel pwent = getpwnam(value); 702223306Smarcel if (pwent != NULL) 703223306Smarcel st->st_uid = pwent->pw_uid; 704223306Smarcel else 705223306Smarcel error = errno; 706223306Smarcel } else 707223306Smarcel error = ENOSYS; 708223306Smarcel break; 709223306Smarcel default: 710223306Smarcel error = ENOSYS; 711223306Smarcel break; 712223306Smarcel } 713223306Smarcel 714223306Smarcel switch (error) { 715223306Smarcel case EINVAL: 716223306Smarcel mtree_error("%s: invalid value '%s'", keyword, value); 717223306Smarcel break; 718223306Smarcel case ENOATTR: 719223306Smarcel mtree_error("%s: keyword needs a value", keyword); 720223306Smarcel break; 721223306Smarcel case ENOSYS: 722223306Smarcel mtree_warning("%s: unsupported keyword", keyword); 723223306Smarcel break; 724223306Smarcel case ENXIO: 725223306Smarcel mtree_error("%s: keyword does not take a value", 726223306Smarcel keyword); 727223306Smarcel break; 728223306Smarcel } 729223306Smarcel } while (1); 730223306Smarcel 731223306Smarcel if (error) 732223306Smarcel return (error); 733223306Smarcel 734223306Smarcel st->st_mode = (st->st_mode & ~S_IFMT) | node->type; 735223306Smarcel 736223306Smarcel /* Nothing more to do for the global defaults. */ 737223306Smarcel if (node->name == NULL) 738223306Smarcel return (0); 739223306Smarcel 740223306Smarcel /* 741223306Smarcel * Be intelligent about the file type. 742223306Smarcel */ 743223306Smarcel if (node->contents != NULL) { 744223306Smarcel if (node->symlink != NULL) { 745223306Smarcel mtree_error("%s: both link and contents keywords " 746223306Smarcel "defined", node->name); 747223306Smarcel return (0); 748223306Smarcel } 749223306Smarcel type = S_IFREG; 750242501Ssjg } else if (node->type != 0) { 751242501Ssjg type = node->type; 752242501Ssjg if (type == S_IFREG) { 753242501Ssjg /* the named path is the default contents */ 754242501Ssjg node->contents = mtree_file_path(node); 755242501Ssjg } 756223306Smarcel } else 757223306Smarcel type = (node->symlink != NULL) ? S_IFLNK : S_IFDIR; 758223306Smarcel 759223306Smarcel if (node->type == 0) 760223306Smarcel node->type = type; 761223306Smarcel 762223306Smarcel if (node->type != type) { 763223306Smarcel mtree_error("%s: file type and defined keywords to not match", 764223306Smarcel node->name); 765223306Smarcel return (0); 766223306Smarcel } 767223306Smarcel 768223306Smarcel st->st_mode = (st->st_mode & ~S_IFMT) | node->type; 769223306Smarcel 770223306Smarcel if (node->contents == NULL) 771223306Smarcel return (0); 772223306Smarcel 773223306Smarcel name = mtree_resolve(node->contents, &istemp); 774223306Smarcel if (name == NULL) 775223306Smarcel return (errno); 776223306Smarcel 777223306Smarcel if (stat(name, &sb) != 0) { 778223306Smarcel mtree_error("%s: contents file '%s' not found", node->name, 779223306Smarcel name); 780223306Smarcel free(name); 781223306Smarcel return (0); 782223306Smarcel } 783223306Smarcel 784223306Smarcel free(node->contents); 785223306Smarcel node->contents = name; 786223306Smarcel st->st_size = sb.st_size; 787223306Smarcel return (0); 788223306Smarcel} 789223306Smarcel 790223306Smarcelstatic int 791223306Smarcelread_mtree_command(FILE *fp) 792223306Smarcel{ 793223306Smarcel char cmd[10]; 794223306Smarcel int error; 795223306Smarcel 796223306Smarcel error = read_word(fp, cmd, sizeof(cmd)); 797223306Smarcel if (error) 798223306Smarcel goto out; 799223306Smarcel 800223306Smarcel error = read_mtree_keywords(fp, &mtree_global); 801223306Smarcel 802223306Smarcel out: 803223306Smarcel skip_to(fp, "\n"); 804223306Smarcel (void)getc(fp); 805223306Smarcel return (error); 806223306Smarcel} 807223306Smarcel 808223306Smarcelstatic int 809223306Smarcelread_mtree_spec1(FILE *fp, bool def, const char *name) 810223306Smarcel{ 811223306Smarcel fsnode *last, *node, *parent; 812223306Smarcel u_int type; 813223306Smarcel int error; 814223306Smarcel 815223306Smarcel assert(name[0] != '\0'); 816223306Smarcel 817223306Smarcel /* 818223306Smarcel * Treat '..' specially, because it only changes our current 819223306Smarcel * directory. We don't create a node for it. We simply ignore 820223306Smarcel * any keywords that may appear on the line as well. 821223306Smarcel * Going up a directory is a little non-obvious. A directory 822223306Smarcel * node has a corresponding '.' child. The parent of '.' is 823223306Smarcel * not the '.' node of the parent directory, but the directory 824223306Smarcel * node within the parent to which the child relates. However, 825223306Smarcel * going up a directory means we need to find the '.' node to 826223306Smarcel * which the directoy node is linked. This we can do via the 827223306Smarcel * first * pointer, because '.' is always the first entry in a 828223306Smarcel * directory. 829223306Smarcel */ 830223306Smarcel if (IS_DOTDOT(name)) { 831223306Smarcel /* This deals with NULL pointers as well. */ 832223306Smarcel if (mtree_current == mtree_root) { 833223306Smarcel mtree_warning("ignoring .. in root directory"); 834223306Smarcel return (0); 835223306Smarcel } 836223306Smarcel 837223306Smarcel node = mtree_current; 838223306Smarcel 839223306Smarcel assert(node != NULL); 840223306Smarcel assert(IS_DOT(node->name)); 841223306Smarcel assert(node->first == node); 842223306Smarcel 843223306Smarcel /* Get the corresponding directory node in the parent. */ 844223306Smarcel node = mtree_current->parent; 845223306Smarcel 846223306Smarcel assert(node != NULL); 847223306Smarcel assert(!IS_DOT(node->name)); 848223306Smarcel 849223306Smarcel node = node->first; 850223306Smarcel 851223306Smarcel assert(node != NULL); 852223306Smarcel assert(IS_DOT(node->name)); 853223306Smarcel assert(node->first == node); 854223306Smarcel 855223306Smarcel mtree_current = node; 856223306Smarcel return (0); 857223306Smarcel } 858223306Smarcel 859223306Smarcel /* 860223306Smarcel * If we don't have a current directory and the first specification 861223306Smarcel * (either implicit or defined) is not '.', then we need to create 862223306Smarcel * a '.' node first (using a recursive call). 863223306Smarcel */ 864223306Smarcel if (!IS_DOT(name) && mtree_current == NULL) { 865223306Smarcel error = read_mtree_spec1(fp, false, "."); 866223306Smarcel if (error) 867223306Smarcel return (error); 868223306Smarcel } 869223306Smarcel 870223306Smarcel /* 871223306Smarcel * Lookup the name in the current directory (if we have a current 872223306Smarcel * directory) to make sure we do not create multiple nodes for the 873223306Smarcel * same component. For non-definitions, if we find a node with the 874223306Smarcel * same name, simply change the current directory. For definitions 875223306Smarcel * more happens. 876223306Smarcel */ 877223306Smarcel last = NULL; 878223306Smarcel node = mtree_current; 879223306Smarcel while (node != NULL) { 880223306Smarcel assert(node->first == mtree_current); 881223306Smarcel 882223306Smarcel if (strcmp(name, node->name) == 0) { 883223306Smarcel if (def == true) { 884223306Smarcel mtree_error("duplicate definition of %s", 885223306Smarcel name); 886223306Smarcel return (0); 887223306Smarcel } 888223306Smarcel 889223306Smarcel if (node->type != S_IFDIR) { 890223306Smarcel mtree_error("%s is not a directory", name); 891223306Smarcel return (0); 892223306Smarcel } 893223306Smarcel 894223306Smarcel assert(!IS_DOT(name)); 895223306Smarcel 896223306Smarcel node = node->child; 897223306Smarcel 898223306Smarcel assert(node != NULL); 899223306Smarcel assert(IS_DOT(node->name)); 900223306Smarcel 901223306Smarcel mtree_current = node; 902223306Smarcel return (0); 903223306Smarcel } 904223306Smarcel 905223306Smarcel last = node; 906223306Smarcel node = last->next; 907223306Smarcel } 908223306Smarcel 909223306Smarcel parent = (mtree_current != NULL) ? mtree_current->parent : NULL; 910223306Smarcel type = (def == false || IS_DOT(name)) ? S_IFDIR : 0; 911223306Smarcel node = create_node(name, type, parent, &mtree_global); 912223306Smarcel if (node == NULL) 913223306Smarcel return (ENOMEM); 914223306Smarcel 915223306Smarcel if (def == true) { 916223306Smarcel error = read_mtree_keywords(fp, node); 917223306Smarcel if (error) { 918223306Smarcel destroy_node(node); 919223306Smarcel return (error); 920223306Smarcel } 921223306Smarcel } 922223306Smarcel 923223306Smarcel node->first = (mtree_current != NULL) ? mtree_current : node; 924223306Smarcel 925223306Smarcel if (last != NULL) 926223306Smarcel last->next = node; 927223306Smarcel 928223306Smarcel if (node->type != S_IFDIR) 929223306Smarcel return (0); 930223306Smarcel 931223306Smarcel if (!IS_DOT(node->name)) { 932223306Smarcel parent = node; 933223306Smarcel node = create_node(".", S_IFDIR, parent, parent); 934223306Smarcel if (node == NULL) { 935223306Smarcel last->next = NULL; 936223306Smarcel destroy_node(parent); 937223306Smarcel return (ENOMEM); 938223306Smarcel } 939223306Smarcel parent->child = node; 940223306Smarcel node->first = node; 941223306Smarcel } 942223306Smarcel 943223306Smarcel assert(node != NULL); 944223306Smarcel assert(IS_DOT(node->name)); 945223306Smarcel assert(node->first == node); 946223306Smarcel 947223306Smarcel mtree_current = node; 948223306Smarcel if (mtree_root == NULL) 949223306Smarcel mtree_root = node; 950223306Smarcel 951223306Smarcel return (0); 952223306Smarcel} 953223306Smarcel 954223306Smarcelstatic int 955223306Smarcelread_mtree_spec(FILE *fp) 956223306Smarcel{ 957223306Smarcel char pathspec[PATH_MAX]; 958223306Smarcel char *cp; 959223306Smarcel int error; 960223306Smarcel 961223306Smarcel error = read_word(fp, pathspec, sizeof(pathspec)); 962223306Smarcel if (error) 963223306Smarcel goto out; 964223306Smarcel 965223306Smarcel cp = strchr(pathspec, '/'); 966223306Smarcel if (cp != NULL) { 967223306Smarcel /* Absolute pathname */ 968223306Smarcel mtree_current = mtree_root; 969223306Smarcel 970223306Smarcel do { 971223306Smarcel *cp++ = '\0'; 972223306Smarcel 973223306Smarcel /* Disallow '.' and '..' as components. */ 974223306Smarcel if (IS_DOT(pathspec) || IS_DOTDOT(pathspec)) { 975223306Smarcel mtree_error("absolute path cannot contain . " 976223306Smarcel "or .. components"); 977223306Smarcel goto out; 978223306Smarcel } 979223306Smarcel 980223306Smarcel /* Ignore multiple adjacent slashes. */ 981223306Smarcel if (pathspec[0] != '\0') 982223306Smarcel error = read_mtree_spec1(fp, false, pathspec); 983223306Smarcel memmove(pathspec, cp, strlen(cp) + 1); 984223306Smarcel cp = strchr(pathspec, '/'); 985223306Smarcel } while (!error && cp != NULL); 986223306Smarcel 987223306Smarcel /* Disallow '.' and '..' as the last component. */ 988223306Smarcel if (!error && (IS_DOT(pathspec) || IS_DOTDOT(pathspec))) { 989223306Smarcel mtree_error("absolute path cannot contain . or .. " 990223306Smarcel "components"); 991223306Smarcel goto out; 992223306Smarcel } 993223306Smarcel } 994223306Smarcel 995223306Smarcel /* Ignore absolute specfications that end with a slash. */ 996223306Smarcel if (!error && pathspec[0] != '\0') 997223306Smarcel error = read_mtree_spec1(fp, true, pathspec); 998223306Smarcel 999223306Smarcel out: 1000223306Smarcel skip_to(fp, "\n"); 1001223306Smarcel (void)getc(fp); 1002223306Smarcel return (error); 1003223306Smarcel} 1004223306Smarcel 1005223306Smarcelfsnode * 1006223306Smarcelread_mtree(const char *fname, fsnode *node) 1007223306Smarcel{ 1008223306Smarcel struct mtree_fileinfo *fi; 1009223306Smarcel FILE *fp; 1010223306Smarcel int c, error; 1011223306Smarcel 1012223306Smarcel /* We do not yet support nesting... */ 1013223306Smarcel assert(node == NULL); 1014223306Smarcel 1015223306Smarcel if (strcmp(fname, "-") == 0) 1016223306Smarcel fp = stdin; 1017223306Smarcel else { 1018223306Smarcel fp = fopen(fname, "r"); 1019223306Smarcel if (fp == NULL) 1020223306Smarcel err(1, "Can't open `%s'", fname); 1021223306Smarcel } 1022223306Smarcel 1023223306Smarcel error = mtree_file_push(fname, fp); 1024223306Smarcel if (error) 1025223306Smarcel goto out; 1026223306Smarcel 1027223306Smarcel bzero(&mtree_global, sizeof(mtree_global)); 1028223306Smarcel bzero(&mtree_global_inode, sizeof(mtree_global_inode)); 1029223306Smarcel mtree_global.inode = &mtree_global_inode; 1030223306Smarcel mtree_global_inode.nlink = 1; 1031223306Smarcel mtree_global_inode.st.st_atime = mtree_global_inode.st.st_ctime = 1032223306Smarcel mtree_global_inode.st.st_mtime = time(NULL); 1033223306Smarcel errors = warnings = 0; 1034223306Smarcel 1035223306Smarcel setgroupent(1); 1036223306Smarcel setpassent(1); 1037223306Smarcel 1038223306Smarcel mtree_root = node; 1039223306Smarcel mtree_current = node; 1040223306Smarcel do { 1041223306Smarcel /* Start of a new line... */ 1042223306Smarcel fi = SLIST_FIRST(&mtree_fileinfo); 1043223306Smarcel fi->line++; 1044223306Smarcel 1045223306Smarcel error = skip_over(fp, " \t"); 1046223306Smarcel if (error) 1047223306Smarcel break; 1048223306Smarcel 1049223306Smarcel c = getc(fp); 1050223306Smarcel if (c == EOF) { 1051223306Smarcel error = ferror(fp) ? errno : -1; 1052223306Smarcel break; 1053223306Smarcel } 1054223306Smarcel 1055223306Smarcel switch (c) { 1056223306Smarcel case '\n': /* empty line */ 1057223306Smarcel error = 0; 1058223306Smarcel break; 1059223306Smarcel case '#': /* comment -- skip to end of line. */ 1060223306Smarcel error = skip_to(fp, "\n"); 1061223306Smarcel if (!error) 1062223306Smarcel (void)getc(fp); 1063223306Smarcel break; 1064223306Smarcel case '/': /* special commands */ 1065223306Smarcel error = read_mtree_command(fp); 1066223306Smarcel break; 1067223306Smarcel default: /* specification */ 1068223306Smarcel ungetc(c, fp); 1069223306Smarcel error = read_mtree_spec(fp); 1070223306Smarcel break; 1071223306Smarcel } 1072223306Smarcel } while (!error); 1073223306Smarcel 1074223306Smarcel endpwent(); 1075223306Smarcel endgrent(); 1076223306Smarcel 1077223306Smarcel if (error <= 0 && (errors || warnings)) { 1078223306Smarcel warnx("%u error(s) and %u warning(s) in mtree manifest", 1079223306Smarcel errors, warnings); 1080223306Smarcel if (errors) 1081223306Smarcel exit(1); 1082223306Smarcel } 1083223306Smarcel 1084223306Smarcel out: 1085223306Smarcel if (error > 0) 1086223306Smarcel errc(1, error, "Error reading mtree file"); 1087223306Smarcel 1088223306Smarcel if (fp != stdin) 1089223306Smarcel fclose(fp); 1090223306Smarcel 1091223306Smarcel if (mtree_root != NULL) 1092223306Smarcel return (mtree_root); 1093223306Smarcel 1094223306Smarcel /* Handle empty specifications. */ 1095223306Smarcel node = create_node(".", S_IFDIR, NULL, &mtree_global); 1096223306Smarcel node->first = node; 1097223306Smarcel return (node); 1098223306Smarcel} 1099