mtree.c revision 247043
12893Sdfr/*- 22893Sdfr * Copyright (c) 2011 Marcel Moolenaar 32893Sdfr * All rights reserved. 42893Sdfr * 52893Sdfr * Redistribution and use in source and binary forms, with or without 62893Sdfr * modification, are permitted provided that the following conditions 72893Sdfr * are met: 82893Sdfr * 1. Redistributions of source code must retain the above copyright 92893Sdfr * notice, this list of conditions and the following disclaimer. 102893Sdfr * 2. Redistributions in binary form must reproduce the above copyright 112893Sdfr * notice, this list of conditions and the following disclaimer in the 122893Sdfr * documentation and/or other materials provided with the distribution. 132893Sdfr * 142893Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 152893Sdfr * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 162893Sdfr * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 172893Sdfr * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 182893Sdfr * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 192893Sdfr * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 202893Sdfr * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 212893Sdfr * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 222893Sdfr * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 232893Sdfr * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 242893Sdfr */ 252893Sdfr 262893Sdfr#include <sys/cdefs.h> 272893Sdfr__FBSDID("$FreeBSD: head/usr.sbin/makefs/mtree.c 247043 2013-02-20 15:28:40Z brooks $"); 282893Sdfr 292893Sdfr#include <sys/param.h> 302893Sdfr#include <sys/queue.h> 312893Sdfr#include <sys/sbuf.h> 322893Sdfr#include <sys/stat.h> 332893Sdfr#include <sys/types.h> 342893Sdfr#include <assert.h> 352893Sdfr#include <errno.h> 362893Sdfr#include <fcntl.h> 372893Sdfr#include <grp.h> 382893Sdfr#include <inttypes.h> 392893Sdfr#include <pwd.h> 402893Sdfr#include <stdarg.h> 412893Sdfr#include <stdbool.h> 422893Sdfr#include <stddef.h> 432893Sdfr#include <stdio.h> 442893Sdfr#include <stdlib.h> 452893Sdfr#include <string.h> 462893Sdfr#include <strings.h> 472893Sdfr#include <unistd.h> 482893Sdfr 492893Sdfr#include "makefs.h" 502893Sdfr 512893Sdfr#define IS_DOT(nm) ((nm)[0] == '.' && (nm)[1] == '\0') 522893Sdfr#define IS_DOTDOT(nm) ((nm)[0] == '.' && (nm)[1] == '.' && (nm)[2] == '\0') 532893Sdfr 542893Sdfrstruct mtree_fileinfo { 552893Sdfr SLIST_ENTRY(mtree_fileinfo) next; 562893Sdfr FILE *fp; 572893Sdfr const char *name; 582893Sdfr u_int line; 592893Sdfr}; 602893Sdfr 612893Sdfr/* Global state used while parsing. */ 622893Sdfrstatic SLIST_HEAD(, mtree_fileinfo) mtree_fileinfo = 632893Sdfr SLIST_HEAD_INITIALIZER(mtree_fileinfo); 642893Sdfrstatic fsnode *mtree_root; 652893Sdfrstatic fsnode *mtree_current; 662893Sdfrstatic fsnode mtree_global; 672893Sdfrstatic fsinode mtree_global_inode; 682893Sdfrstatic u_int errors, warnings; 692893Sdfr 702893Sdfrstatic void mtree_error(const char *, ...) __printflike(1, 2); 712893Sdfrstatic void mtree_warning(const char *, ...) __printflike(1, 2); 722893Sdfr 732893Sdfrstatic int 742893Sdfrmtree_file_push(const char *name, FILE *fp) 752893Sdfr{ 762893Sdfr struct mtree_fileinfo *fi; 772893Sdfr 782893Sdfr fi = malloc(sizeof(*fi)); 792893Sdfr if (fi == NULL) 802893Sdfr return (ENOMEM); 812893Sdfr 822893Sdfr if (strcmp(name, "-") == 0) 832893Sdfr fi->name = strdup("(stdin)"); 842893Sdfr else 852893Sdfr fi->name = strdup(name); 862893Sdfr if (fi->name == NULL) { 872893Sdfr free(fi); 882893Sdfr return (ENOMEM); 892893Sdfr } 902893Sdfr 912893Sdfr fi->fp = fp; 922893Sdfr fi->line = 0; 932893Sdfr 942893Sdfr SLIST_INSERT_HEAD(&mtree_fileinfo, fi, next); 952893Sdfr return (0); 962893Sdfr} 972893Sdfr 982893Sdfrstatic void 992893Sdfrmtree_print(const char *msgtype, const char *fmt, va_list ap) 1002893Sdfr{ 1012893Sdfr struct mtree_fileinfo *fi; 1022893Sdfr 1032893Sdfr if (msgtype != NULL) { 1042893Sdfr fi = SLIST_FIRST(&mtree_fileinfo); 1052893Sdfr if (fi != NULL) 1062893Sdfr fprintf(stderr, "%s:%u: ", fi->name, fi->line); 1072893Sdfr fprintf(stderr, "%s: ", msgtype); 1082893Sdfr } 1092893Sdfr vfprintf(stderr, fmt, ap); 1102893Sdfr} 1112893Sdfr 1122893Sdfrstatic void 1132893Sdfrmtree_error(const char *fmt, ...) 1142893Sdfr{ 1152893Sdfr va_list ap; 1162893Sdfr 1172893Sdfr va_start(ap, fmt); 1182893Sdfr mtree_print("error", fmt, ap); 1192893Sdfr va_end(ap); 1202893Sdfr 1212893Sdfr errors++; 1222893Sdfr fputc('\n', stderr); 1232893Sdfr} 1242893Sdfr 1252893Sdfrstatic void 1262893Sdfrmtree_warning(const char *fmt, ...) 1272893Sdfr{ 1282893Sdfr va_list ap; 1292893Sdfr 1302893Sdfr va_start(ap, fmt); 1312893Sdfr mtree_print("warning", fmt, ap); 1322893Sdfr va_end(ap); 1332893Sdfr 1342893Sdfr warnings++; 1352893Sdfr fputc('\n', stderr); 1362893Sdfr} 1372893Sdfr 1382893Sdfr#ifndef MAKEFS_MAX_TREE_DEPTH 1392893Sdfr# define MAKEFS_MAX_TREE_DEPTH (MAXPATHLEN/2) 1402893Sdfr#endif 1412893Sdfr 1422893Sdfr/* construct path to node->name */ 1432893Sdfrstatic char * 1442893Sdfrmtree_file_path(fsnode *node) 1452893Sdfr{ 1462893Sdfr fsnode *pnode; 1472893Sdfr struct sbuf *sb; 1482893Sdfr char *res, *rp[MAKEFS_MAX_TREE_DEPTH]; 1492893Sdfr int depth; 1502893Sdfr 1512893Sdfr depth = 0; 1522893Sdfr rp[depth] = node->name; 1532893Sdfr for (pnode = node->parent; pnode && depth < MAKEFS_MAX_TREE_DEPTH; 1542893Sdfr pnode = pnode->parent) { 1552893Sdfr if (strcmp(pnode->name, ".") == 0) 1562893Sdfr break; 1572893Sdfr rp[++depth] = pnode->name; 1582893Sdfr } 1592893Sdfr 1602893Sdfr sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND); 1612893Sdfr if (sb == NULL) { 1622893Sdfr errno = ENOMEM; 1632893Sdfr return (NULL); 1642893Sdfr } 1652893Sdfr while (depth > 0) { 1662893Sdfr sbuf_cat(sb, rp[depth--]); 1672893Sdfr sbuf_putc(sb, '/'); 1682893Sdfr } 1692893Sdfr sbuf_cat(sb, rp[depth]); 1702893Sdfr sbuf_finish(sb); 1712893Sdfr res = strdup(sbuf_data(sb)); 1722893Sdfr sbuf_delete(sb); 1732893Sdfr if (res == NULL) 1742893Sdfr errno = ENOMEM; 1752893Sdfr return res; 1762893Sdfr 1772893Sdfr} 1782893Sdfr 1792893Sdfr/* mtree_resolve() sets errno to indicate why NULL was returned. */ 1802893Sdfrstatic char * 1812893Sdfrmtree_resolve(const char *spec, int *istemp) 1822893Sdfr{ 1832893Sdfr struct sbuf *sb; 1842893Sdfr char *res, *var; 1852893Sdfr const char *base, *p, *v; 1862893Sdfr size_t len; 1872893Sdfr int c, error, quoted, subst; 1882893Sdfr 1892893Sdfr len = strlen(spec); 1902893Sdfr if (len == 0) { 1912893Sdfr errno = EINVAL; 1922893Sdfr return (NULL); 1932893Sdfr } 1942893Sdfr 1952893Sdfr c = (len > 1) ? (spec[0] == spec[len - 1]) ? spec[0] : 0 : 0; 1962893Sdfr *istemp = (c == '`') ? 1 : 0; 1972893Sdfr subst = (c == '`' || c == '"') ? 1 : 0; 1982893Sdfr quoted = (subst || c == '\'') ? 1 : 0; 1992893Sdfr 2002893Sdfr if (!subst) { 2012893Sdfr res = strdup(spec + quoted); 2022893Sdfr if (res != NULL && quoted) 2032893Sdfr res[len - 2] = '\0'; 2042893Sdfr return (res); 2052893Sdfr } 2062893Sdfr 2072893Sdfr sb = sbuf_new_auto(); 2082893Sdfr if (sb == NULL) { 2092893Sdfr errno = ENOMEM; 2102893Sdfr return (NULL); 2112893Sdfr } 2122893Sdfr 2132893Sdfr base = spec + 1; 2142893Sdfr len -= 2; 2152893Sdfr error = 0; 2162893Sdfr while (len > 0) { 2172893Sdfr p = strchr(base, '$'); 2182893Sdfr if (p == NULL) { 2192893Sdfr sbuf_bcat(sb, base, len); 2202893Sdfr base += len; 2212893Sdfr len = 0; 2222893Sdfr continue; 2232893Sdfr } 2242893Sdfr /* The following is safe. spec always starts with a quote. */ 2252893Sdfr if (p[-1] == '\\') 2262893Sdfr p--; 2272893Sdfr if (base != p) { 2282893Sdfr sbuf_bcat(sb, base, p - base); 2292893Sdfr len -= p - base; 2302893Sdfr base = p; 2312893Sdfr } 2322893Sdfr if (*p == '\\') { 2332893Sdfr sbuf_putc(sb, '$'); 2342893Sdfr base += 2; 2352893Sdfr len -= 2; 2362893Sdfr continue; 2372893Sdfr } 2382893Sdfr /* Skip the '$'. */ 2392893Sdfr base++; 2402893Sdfr len--; 2412893Sdfr /* Handle ${X} vs $X. */ 2422893Sdfr v = base; 2432893Sdfr if (*base == '{') { 2442893Sdfr p = strchr(v, '}'); 2452893Sdfr if (p == NULL) 2462893Sdfr p = v; 2472893Sdfr } else 2482893Sdfr p = v; 2492893Sdfr len -= (p + 1) - base; 2502893Sdfr base = p + 1; 2512893Sdfr 2522893Sdfr if (v == p) { 2532893Sdfr sbuf_putc(sb, *v); 2542893Sdfr continue; 2552893Sdfr } 2562893Sdfr 2572893Sdfr error = ENOMEM; 2582893Sdfr var = calloc(p - v, 1); 2592893Sdfr if (var == NULL) 2602893Sdfr break; 2612893Sdfr 2622893Sdfr memcpy(var, v + 1, p - v - 1); 2632893Sdfr if (strcmp(var, ".CURDIR") == 0) { 2642893Sdfr res = getcwd(NULL, 0); 2652893Sdfr if (res == NULL) 2662893Sdfr break; 2672893Sdfr } else if (strcmp(var, ".PROG") == 0) { 2682893Sdfr res = strdup(getprogname()); 2692893Sdfr if (res == NULL) 2702893Sdfr break; 2712893Sdfr } else { 2722893Sdfr v = getenv(var); 2732893Sdfr if (v != NULL) { 2742893Sdfr res = strdup(v); 2752893Sdfr if (res == NULL) 2762893Sdfr break; 2772893Sdfr } else 2782893Sdfr res = NULL; 2792893Sdfr } 2802893Sdfr error = 0; 2812893Sdfr 2822893Sdfr if (res != NULL) { 2832893Sdfr sbuf_cat(sb, res); 2842893Sdfr free(res); 2852893Sdfr } 2862893Sdfr free(var); 2872893Sdfr } 2882893Sdfr 2892893Sdfr sbuf_finish(sb); 2902893Sdfr res = (error == 0) ? strdup(sbuf_data(sb)) : NULL; 2912893Sdfr sbuf_delete(sb); 2922893Sdfr if (res == NULL) 2932893Sdfr errno = ENOMEM; 2942893Sdfr return (res); 2952893Sdfr} 2962893Sdfr 2972893Sdfrstatic int 2982893Sdfrskip_over(FILE *fp, const char *cs) 2992893Sdfr{ 3002893Sdfr int c; 3012893Sdfr 3022893Sdfr c = getc(fp); 3032893Sdfr while (c != EOF && strchr(cs, c) != NULL) 3042893Sdfr c = getc(fp); 3052893Sdfr if (c != EOF) { 3062893Sdfr ungetc(c, fp); 3072893Sdfr return (0); 3082893Sdfr } 3092893Sdfr return (ferror(fp) ? errno : -1); 3102893Sdfr} 3112893Sdfr 3122893Sdfrstatic int 3132893Sdfrskip_to(FILE *fp, const char *cs) 3142893Sdfr{ 3152893Sdfr int c; 3162893Sdfr 3172893Sdfr c = getc(fp); 3182893Sdfr while (c != EOF && strchr(cs, c) == NULL) 3192893Sdfr c = getc(fp); 3202893Sdfr if (c != EOF) { 3212893Sdfr ungetc(c, fp); 3222893Sdfr return (0); 3232893Sdfr } 3242893Sdfr return (ferror(fp) ? errno : -1); 3252893Sdfr} 3262893Sdfr 3272893Sdfrstatic int 3282893Sdfrread_word(FILE *fp, char *buf, size_t bufsz) 3292893Sdfr{ 3302893Sdfr struct mtree_fileinfo *fi; 3312893Sdfr size_t idx, qidx; 3322893Sdfr int c, done, error, esc, qlvl; 3332893Sdfr 3342893Sdfr if (bufsz == 0) 3352893Sdfr return (EINVAL); 3362893Sdfr 3372893Sdfr done = 0; 3382893Sdfr esc = 0; 3392893Sdfr idx = 0; 3402893Sdfr qidx = -1; 3412893Sdfr qlvl = 0; 3422893Sdfr do { 3432893Sdfr c = getc(fp); 3442893Sdfr switch (c) { 3452893Sdfr case EOF: 3462893Sdfr buf[idx] = '\0'; 3472893Sdfr error = ferror(fp) ? errno : -1; 3482893Sdfr if (error == -1) 3492893Sdfr mtree_error("unexpected end of file"); 3502893Sdfr return (error); 3512893Sdfr case '\\': 3522893Sdfr esc++; 3532893Sdfr if (esc == 1) 3542893Sdfr continue; 3552893Sdfr break; 3562893Sdfr case '`': 3572893Sdfr case '\'': 3582893Sdfr case '"': 3592893Sdfr if (esc) 3602893Sdfr break; 3612893Sdfr if (qlvl == 0) { 3622893Sdfr qlvl++; 3632893Sdfr qidx = idx; 3642893Sdfr } else if (c == buf[qidx]) { 3652893Sdfr qlvl--; 3662893Sdfr if (qlvl > 0) { 3672893Sdfr do { 3682893Sdfr qidx--; 3692893Sdfr } while (buf[qidx] != '`' && 3702893Sdfr buf[qidx] != '\'' && 3712893Sdfr buf[qidx] != '"'); 3722893Sdfr } else 3732893Sdfr qidx = -1; 3742893Sdfr } else { 3752893Sdfr qlvl++; 3762893Sdfr qidx = idx; 3772893Sdfr } 3782893Sdfr break; 3792893Sdfr case ' ': 3802893Sdfr case '\t': 3812893Sdfr case '\n': 3822893Sdfr if (!esc && qlvl == 0) { 3832893Sdfr ungetc(c, fp); 3842893Sdfr c = '\0'; 3852893Sdfr done = 1; 3862893Sdfr break; 3872893Sdfr } 3882893Sdfr if (c == '\n') { 3892893Sdfr /* 3902893Sdfr * We going to eat the newline ourselves. 3912893Sdfr */ 3922893Sdfr if (qlvl > 0) 3932893Sdfr mtree_warning("quoted word straddles " 3942893Sdfr "onto next line."); 3952893Sdfr fi = SLIST_FIRST(&mtree_fileinfo); 3962893Sdfr fi->line++; 3972893Sdfr } 3982893Sdfr break; 3992893Sdfr case 'a': 4002893Sdfr if (esc) 4012893Sdfr c = '\a'; 4022893Sdfr break; 4032893Sdfr case 'b': 4042893Sdfr if (esc) 4052893Sdfr c = '\b'; 4062893Sdfr break; 4072893Sdfr case 'f': 4082893Sdfr if (esc) 4092893Sdfr c = '\f'; 4102893Sdfr break; 4112893Sdfr case 'n': 4122893Sdfr if (esc) 4132893Sdfr c = '\n'; 4142893Sdfr break; 4152893Sdfr case 'r': 4162893Sdfr if (esc) 4172893Sdfr c = '\r'; 4182893Sdfr break; 4192893Sdfr case 't': 4202893Sdfr if (esc) 4212893Sdfr c = '\t'; 4222893Sdfr break; 4232893Sdfr case 'v': 4242893Sdfr if (esc) 4252893Sdfr c = '\v'; 4262893Sdfr break; 4272893Sdfr } 4282893Sdfr buf[idx++] = c; 4292893Sdfr esc = 0; 4302893Sdfr } while (idx < bufsz && !done); 4312893Sdfr 4322893Sdfr if (idx >= bufsz) { 4332893Sdfr mtree_error("word too long to fit buffer (max %zu characters)", 4342893Sdfr bufsz); 4352893Sdfr skip_to(fp, " \t\n"); 4362893Sdfr } 4372893Sdfr return (0); 4382893Sdfr} 4392893Sdfr 4402893Sdfrstatic fsnode * 4412893Sdfrcreate_node(const char *name, u_int type, fsnode *parent, fsnode *global) 4422893Sdfr{ 4432893Sdfr fsnode *n; 4442893Sdfr 4452893Sdfr n = calloc(1, sizeof(*n)); 4462893Sdfr if (n == NULL) 4472893Sdfr return (NULL); 4482893Sdfr 4492893Sdfr n->name = strdup(name); 4502893Sdfr if (n->name == NULL) { 4512893Sdfr free(n); 4522893Sdfr return (NULL); 4532893Sdfr } 4542893Sdfr 4552893Sdfr n->type = (type == 0) ? global->type : type; 4562893Sdfr n->parent = parent; 4572893Sdfr 4582893Sdfr n->inode = calloc(1, sizeof(*n->inode)); 4592893Sdfr if (n->inode == NULL) { 4602893Sdfr free(n->name); 4612893Sdfr free(n); 4622893Sdfr return (NULL); 4632893Sdfr } 4642893Sdfr 4652893Sdfr /* Assign global options/defaults. */ 4662893Sdfr bcopy(global->inode, n->inode, sizeof(*n->inode)); 4672893Sdfr n->inode->st.st_mode = (n->inode->st.st_mode & ~S_IFMT) | n->type; 4682893Sdfr 4692893Sdfr if (n->type == S_IFLNK) 4702893Sdfr n->symlink = global->symlink; 4712893Sdfr else if (n->type == S_IFREG) 4722893Sdfr n->contents = global->contents; 4732893Sdfr 4742893Sdfr return (n); 4752893Sdfr} 4762893Sdfr 4772893Sdfrstatic void 4782893Sdfrdestroy_node(fsnode *n) 4792893Sdfr{ 4802893Sdfr 4812893Sdfr assert(n != NULL); 4822893Sdfr assert(n->name != NULL); 4832893Sdfr assert(n->inode != NULL); 4842893Sdfr 4852893Sdfr free(n->inode); 4862893Sdfr free(n->name); 4872893Sdfr free(n); 4882893Sdfr} 4892893Sdfr 4902893Sdfrstatic int 4912893Sdfrread_number(const char *tok, u_int base, intmax_t *res, intmax_t min, 4922893Sdfr intmax_t max) 4932893Sdfr{ 4942893Sdfr char *end; 4952893Sdfr intmax_t val; 4962893Sdfr 4972893Sdfr val = strtoimax(tok, &end, base); 4982893Sdfr if (end == tok || end[0] != '\0') 4992893Sdfr return (EINVAL); 5002893Sdfr if (val < min || val > max) 5012893Sdfr return (EDOM); 5022893Sdfr *res = val; 5032893Sdfr return (0); 5042893Sdfr} 5052893Sdfr 5062893Sdfrstatic int 5072893Sdfrread_mtree_keywords(FILE *fp, fsnode *node) 5082893Sdfr{ 5092893Sdfr char keyword[PATH_MAX]; 5102893Sdfr char *name, *p, *value; 5112893Sdfr gid_t gid; 5122893Sdfr uid_t uid; 5132893Sdfr struct stat *st, sb; 5142893Sdfr intmax_t num; 5152893Sdfr u_long flset, flclr; 5162893Sdfr int error, istemp, type; 5172893Sdfr 5182893Sdfr st = &node->inode->st; 5192893Sdfr do { 5202893Sdfr error = skip_over(fp, " \t"); 5212893Sdfr if (error) 5222893Sdfr break; 5232893Sdfr 5242893Sdfr error = read_word(fp, keyword, sizeof(keyword)); 5252893Sdfr if (error) 5262893Sdfr break; 5272893Sdfr 5282893Sdfr if (keyword[0] == '\0') 5292893Sdfr break; 5302893Sdfr 5312893Sdfr value = strchr(keyword, '='); 5322893Sdfr if (value != NULL) 5332893Sdfr *value++ = '\0'; 5342893Sdfr 5352893Sdfr /* 5362893Sdfr * We use EINVAL, ENOATTR, ENOSYS and ENXIO to signal 5372893Sdfr * certain conditions: 5382893Sdfr * EINVAL - Value provided for a keyword that does 5392893Sdfr * not take a value. The value is ignored. 5402893Sdfr * ENOATTR - Value missing for a keyword that needs 5412893Sdfr * a value. The keyword is ignored. 5422893Sdfr * ENOSYS - Unsupported keyword encountered. The 5432893Sdfr * keyword is ignored. 5442893Sdfr * ENXIO - Value provided for a keyword that does 5452893Sdfr * not take a value. The value is ignored. 5462893Sdfr */ 5472893Sdfr switch (keyword[0]) { 5482893Sdfr case 'c': 5492893Sdfr if (strcmp(keyword, "contents") == 0) { 5502893Sdfr if (value == NULL) { 5512893Sdfr error = ENOATTR; 5522893Sdfr break; 5532893Sdfr } 5542893Sdfr node->contents = strdup(value); 5552893Sdfr } else 5562893Sdfr error = ENOSYS; 5572893Sdfr break; 5582893Sdfr case 'f': 5592893Sdfr if (strcmp(keyword, "flags") == 0) { 5602893Sdfr if (value == NULL) { 5612893Sdfr error = ENOATTR; 5622893Sdfr break; 5632893Sdfr } 5642893Sdfr flset = flclr = 0; 5652893Sdfr if (!strtofflags(&value, &flset, &flclr)) { 5662893Sdfr st->st_flags &= ~flclr; 5672893Sdfr st->st_flags |= flset; 5682893Sdfr } else 5692893Sdfr error = errno; 5702893Sdfr } else 5712893Sdfr error = ENOSYS; 5722893Sdfr break; 5732893Sdfr case 'g': 5742893Sdfr if (strcmp(keyword, "gid") == 0) { 5752893Sdfr if (value == NULL) { 5762893Sdfr error = ENOATTR; 5772893Sdfr break; 5782893Sdfr } 5792893Sdfr error = read_number(value, 10, &num, 5802893Sdfr 0, UINT_MAX); 5812893Sdfr if (!error) 5822893Sdfr st->st_gid = num; 5832893Sdfr } else if (strcmp(keyword, "gname") == 0) { 5842893Sdfr if (value == NULL) { 5852893Sdfr error = ENOATTR; 5862893Sdfr break; 5872893Sdfr } 5882893Sdfr if (gid_from_group(value, &gid) == 0) 5892893Sdfr st->st_gid = gid; 5902893Sdfr else 5912893Sdfr error = EINVAL; 5922893Sdfr } else 5932893Sdfr error = ENOSYS; 5942893Sdfr break; 5952893Sdfr case 'l': 5962893Sdfr if (strcmp(keyword, "link") == 0) { 5972893Sdfr if (value == NULL) { 5982893Sdfr error = ENOATTR; 5992893Sdfr break; 6002893Sdfr } 6012893Sdfr node->symlink = strdup(value); 6022893Sdfr } else 6032893Sdfr error = ENOSYS; 6042893Sdfr break; 6052893Sdfr case 'm': 6062893Sdfr if (strcmp(keyword, "mode") == 0) { 6072893Sdfr if (value == NULL) { 6082893Sdfr error = ENOATTR; 6092893Sdfr break; 6102893Sdfr } 6112893Sdfr if (value[0] >= '0' && value[0] <= '9') { 6122893Sdfr error = read_number(value, 8, &num, 6132893Sdfr 0, 07777); 6142893Sdfr if (!error) { 6152893Sdfr st->st_mode &= S_IFMT; 6162893Sdfr st->st_mode |= num; 6172893Sdfr } 6182893Sdfr } else { 6192893Sdfr /* Symbolic mode not supported. */ 6202893Sdfr error = EINVAL; 6212893Sdfr break; 6222893Sdfr } 6232893Sdfr } else 6242893Sdfr error = ENOSYS; 6252893Sdfr break; 6262893Sdfr case 'o': 6272893Sdfr if (strcmp(keyword, "optional") == 0) { 6282893Sdfr if (value != NULL) 6292893Sdfr error = ENXIO; 6302893Sdfr node->flags |= FSNODE_F_OPTIONAL; 6312893Sdfr } else 6322893Sdfr error = ENOSYS; 6332893Sdfr break; 6342893Sdfr case 's': 6352893Sdfr if (strcmp(keyword, "size") == 0) { 6362893Sdfr if (value == NULL) { 6372893Sdfr error = ENOATTR; 6382893Sdfr break; 6392893Sdfr } 6402893Sdfr error = read_number(value, 10, &num, 6412893Sdfr 0, INTMAX_MAX); 6422893Sdfr if (!error) 6432893Sdfr st->st_size = num; 6442893Sdfr } else 6452893Sdfr error = ENOSYS; 6462893Sdfr break; 6472893Sdfr case 't': 6482893Sdfr if (strcmp(keyword, "time") == 0) { 6492893Sdfr if (value == NULL) { 6502893Sdfr error = ENOATTR; 6512893Sdfr break; 6522893Sdfr } 6532893Sdfr p = strchr(value, '.'); 6542893Sdfr if (p != NULL) 6552893Sdfr *p++ = '\0'; 6562893Sdfr error = read_number(value, 10, &num, 0, 6572893Sdfr INTMAX_MAX); 6582893Sdfr if (error) 6592893Sdfr break; 6602893Sdfr st->st_atime = num; 6612893Sdfr st->st_ctime = num; 6622893Sdfr st->st_mtime = num; 6632893Sdfr error = read_number(p, 10, &num, 0, 6642893Sdfr INTMAX_MAX); 6652893Sdfr if (error) 6662893Sdfr break; 6672893Sdfr if (num != 0) 6682893Sdfr error = EINVAL; 6692893Sdfr } else if (strcmp(keyword, "type") == 0) { 6702893Sdfr if (value == NULL) { 6712893Sdfr error = ENOATTR; 6722893Sdfr break; 6732893Sdfr } 6742893Sdfr if (strcmp(value, "dir") == 0) 6752893Sdfr node->type = S_IFDIR; 6762893Sdfr else if (strcmp(value, "file") == 0) 6772893Sdfr node->type = S_IFREG; 6782893Sdfr else if (strcmp(value, "link") == 0) 6792893Sdfr node->type = S_IFLNK; 6802893Sdfr else 6812893Sdfr error = EINVAL; 6822893Sdfr } else 6832893Sdfr error = ENOSYS; 6842893Sdfr break; 6852893Sdfr case 'u': 6862893Sdfr if (strcmp(keyword, "uid") == 0) { 6872893Sdfr if (value == NULL) { 6882893Sdfr error = ENOATTR; 6892893Sdfr break; 6902893Sdfr } 6912893Sdfr error = read_number(value, 10, &num, 6922893Sdfr 0, UINT_MAX); 6932893Sdfr if (!error) 6942893Sdfr st->st_uid = num; 6952893Sdfr } else if (strcmp(keyword, "uname") == 0) { 6962893Sdfr if (value == NULL) { 6972893Sdfr error = ENOATTR; 6982893Sdfr break; 6992893Sdfr } 7002893Sdfr if (uid_from_user(value, &uid) == 0) 7012893Sdfr st->st_uid = uid; 7022893Sdfr else 7032893Sdfr error = EINVAL; 7042893Sdfr } else 7052893Sdfr error = ENOSYS; 7062893Sdfr break; 7072893Sdfr default: 7082893Sdfr error = ENOSYS; 7092893Sdfr break; 7102893Sdfr } 7112893Sdfr 7122893Sdfr switch (error) { 7132893Sdfr case EINVAL: 7142893Sdfr mtree_error("%s: invalid value '%s'", keyword, value); 7152893Sdfr break; 7162893Sdfr case ENOATTR: 7172893Sdfr mtree_error("%s: keyword needs a value", keyword); 7182893Sdfr break; 7192893Sdfr case ENOSYS: 7202893Sdfr mtree_warning("%s: unsupported keyword", keyword); 7212893Sdfr break; 7222893Sdfr case ENXIO: 7232893Sdfr mtree_error("%s: keyword does not take a value", 7242893Sdfr keyword); 7252893Sdfr break; 7262893Sdfr } 7272893Sdfr } while (1); 7282893Sdfr 7292893Sdfr if (error) 7302893Sdfr return (error); 7312893Sdfr 7322893Sdfr st->st_mode = (st->st_mode & ~S_IFMT) | node->type; 7332893Sdfr 734 /* Nothing more to do for the global defaults. */ 735 if (node->name == NULL) 736 return (0); 737 738 /* 739 * Be intelligent about the file type. 740 */ 741 if (node->contents != NULL) { 742 if (node->symlink != NULL) { 743 mtree_error("%s: both link and contents keywords " 744 "defined", node->name); 745 return (0); 746 } 747 type = S_IFREG; 748 } else if (node->type != 0) { 749 type = node->type; 750 if (type == S_IFREG) { 751 /* the named path is the default contents */ 752 node->contents = mtree_file_path(node); 753 } 754 } else 755 type = (node->symlink != NULL) ? S_IFLNK : S_IFDIR; 756 757 if (node->type == 0) 758 node->type = type; 759 760 if (node->type != type) { 761 mtree_error("%s: file type and defined keywords to not match", 762 node->name); 763 return (0); 764 } 765 766 st->st_mode = (st->st_mode & ~S_IFMT) | node->type; 767 768 if (node->contents == NULL) 769 return (0); 770 771 name = mtree_resolve(node->contents, &istemp); 772 if (name == NULL) 773 return (errno); 774 775 if (stat(name, &sb) != 0) { 776 mtree_error("%s: contents file '%s' not found", node->name, 777 name); 778 free(name); 779 return (0); 780 } 781 782 free(node->contents); 783 node->contents = name; 784 st->st_size = sb.st_size; 785 return (0); 786} 787 788static int 789read_mtree_command(FILE *fp) 790{ 791 char cmd[10]; 792 int error; 793 794 error = read_word(fp, cmd, sizeof(cmd)); 795 if (error) 796 goto out; 797 798 error = read_mtree_keywords(fp, &mtree_global); 799 800 out: 801 skip_to(fp, "\n"); 802 (void)getc(fp); 803 return (error); 804} 805 806static int 807read_mtree_spec1(FILE *fp, bool def, const char *name) 808{ 809 fsnode *last, *node, *parent; 810 u_int type; 811 int error; 812 813 assert(name[0] != '\0'); 814 815 /* 816 * Treat '..' specially, because it only changes our current 817 * directory. We don't create a node for it. We simply ignore 818 * any keywords that may appear on the line as well. 819 * Going up a directory is a little non-obvious. A directory 820 * node has a corresponding '.' child. The parent of '.' is 821 * not the '.' node of the parent directory, but the directory 822 * node within the parent to which the child relates. However, 823 * going up a directory means we need to find the '.' node to 824 * which the directoy node is linked. This we can do via the 825 * first * pointer, because '.' is always the first entry in a 826 * directory. 827 */ 828 if (IS_DOTDOT(name)) { 829 /* This deals with NULL pointers as well. */ 830 if (mtree_current == mtree_root) { 831 mtree_warning("ignoring .. in root directory"); 832 return (0); 833 } 834 835 node = mtree_current; 836 837 assert(node != NULL); 838 assert(IS_DOT(node->name)); 839 assert(node->first == node); 840 841 /* Get the corresponding directory node in the parent. */ 842 node = mtree_current->parent; 843 844 assert(node != NULL); 845 assert(!IS_DOT(node->name)); 846 847 node = node->first; 848 849 assert(node != NULL); 850 assert(IS_DOT(node->name)); 851 assert(node->first == node); 852 853 mtree_current = node; 854 return (0); 855 } 856 857 /* 858 * If we don't have a current directory and the first specification 859 * (either implicit or defined) is not '.', then we need to create 860 * a '.' node first (using a recursive call). 861 */ 862 if (!IS_DOT(name) && mtree_current == NULL) { 863 error = read_mtree_spec1(fp, false, "."); 864 if (error) 865 return (error); 866 } 867 868 /* 869 * Lookup the name in the current directory (if we have a current 870 * directory) to make sure we do not create multiple nodes for the 871 * same component. For non-definitions, if we find a node with the 872 * same name, simply change the current directory. For definitions 873 * more happens. 874 */ 875 last = NULL; 876 node = mtree_current; 877 while (node != NULL) { 878 assert(node->first == mtree_current); 879 880 if (strcmp(name, node->name) == 0) { 881 if (def == true) { 882 if (!dupsok) 883 mtree_error( 884 "duplicate definition of %s", 885 name); 886 else 887 mtree_warning( 888 "duplicate definition of %s", 889 name); 890 return (0); 891 } 892 893 if (node->type != S_IFDIR) { 894 mtree_error("%s is not a directory", name); 895 return (0); 896 } 897 898 assert(!IS_DOT(name)); 899 900 node = node->child; 901 902 assert(node != NULL); 903 assert(IS_DOT(node->name)); 904 905 mtree_current = node; 906 return (0); 907 } 908 909 last = node; 910 node = last->next; 911 } 912 913 parent = (mtree_current != NULL) ? mtree_current->parent : NULL; 914 type = (def == false || IS_DOT(name)) ? S_IFDIR : 0; 915 node = create_node(name, type, parent, &mtree_global); 916 if (node == NULL) 917 return (ENOMEM); 918 919 if (def == true) { 920 error = read_mtree_keywords(fp, node); 921 if (error) { 922 destroy_node(node); 923 return (error); 924 } 925 } 926 927 node->first = (mtree_current != NULL) ? mtree_current : node; 928 929 if (last != NULL) 930 last->next = node; 931 932 if (node->type != S_IFDIR) 933 return (0); 934 935 if (!IS_DOT(node->name)) { 936 parent = node; 937 node = create_node(".", S_IFDIR, parent, parent); 938 if (node == NULL) { 939 last->next = NULL; 940 destroy_node(parent); 941 return (ENOMEM); 942 } 943 parent->child = node; 944 node->first = node; 945 } 946 947 assert(node != NULL); 948 assert(IS_DOT(node->name)); 949 assert(node->first == node); 950 951 mtree_current = node; 952 if (mtree_root == NULL) 953 mtree_root = node; 954 955 return (0); 956} 957 958static int 959read_mtree_spec(FILE *fp) 960{ 961 char pathspec[PATH_MAX]; 962 char *cp; 963 int error; 964 965 error = read_word(fp, pathspec, sizeof(pathspec)); 966 if (error) 967 goto out; 968 969 cp = strchr(pathspec, '/'); 970 if (cp != NULL) { 971 /* Absolute pathname */ 972 mtree_current = mtree_root; 973 974 do { 975 *cp++ = '\0'; 976 977 /* Disallow '..' as a component. */ 978 if (IS_DOTDOT(pathspec)) { 979 mtree_error("absolute path cannot contain " 980 ".. component"); 981 goto out; 982 } 983 984 /* Ignore multiple adjacent slashes and '.'. */ 985 if (pathspec[0] != '\0' && !IS_DOT(pathspec)) 986 error = read_mtree_spec1(fp, false, pathspec); 987 memmove(pathspec, cp, strlen(cp) + 1); 988 cp = strchr(pathspec, '/'); 989 } while (!error && cp != NULL); 990 991 /* Disallow '.' and '..' as the last component. */ 992 if (!error && (IS_DOT(pathspec) || IS_DOTDOT(pathspec))) { 993 mtree_error("absolute path cannot contain . or .. " 994 "components"); 995 goto out; 996 } 997 } 998 999 /* Ignore absolute specfications that end with a slash. */ 1000 if (!error && pathspec[0] != '\0') 1001 error = read_mtree_spec1(fp, true, pathspec); 1002 1003 out: 1004 skip_to(fp, "\n"); 1005 (void)getc(fp); 1006 return (error); 1007} 1008 1009fsnode * 1010read_mtree(const char *fname, fsnode *node) 1011{ 1012 struct mtree_fileinfo *fi; 1013 FILE *fp; 1014 int c, error; 1015 1016 /* We do not yet support nesting... */ 1017 assert(node == NULL); 1018 1019 if (strcmp(fname, "-") == 0) 1020 fp = stdin; 1021 else { 1022 fp = fopen(fname, "r"); 1023 if (fp == NULL) 1024 err(1, "Can't open `%s'", fname); 1025 } 1026 1027 error = mtree_file_push(fname, fp); 1028 if (error) 1029 goto out; 1030 1031 bzero(&mtree_global, sizeof(mtree_global)); 1032 bzero(&mtree_global_inode, sizeof(mtree_global_inode)); 1033 mtree_global.inode = &mtree_global_inode; 1034 mtree_global_inode.nlink = 1; 1035 mtree_global_inode.st.st_atime = mtree_global_inode.st.st_ctime = 1036 mtree_global_inode.st.st_mtime = time(NULL); 1037 errors = warnings = 0; 1038 1039 setgroupent(1); 1040 setpassent(1); 1041 1042 mtree_root = node; 1043 mtree_current = node; 1044 do { 1045 /* Start of a new line... */ 1046 fi = SLIST_FIRST(&mtree_fileinfo); 1047 fi->line++; 1048 1049 error = skip_over(fp, " \t"); 1050 if (error) 1051 break; 1052 1053 c = getc(fp); 1054 if (c == EOF) { 1055 error = ferror(fp) ? errno : -1; 1056 break; 1057 } 1058 1059 switch (c) { 1060 case '\n': /* empty line */ 1061 error = 0; 1062 break; 1063 case '#': /* comment -- skip to end of line. */ 1064 error = skip_to(fp, "\n"); 1065 if (!error) 1066 (void)getc(fp); 1067 break; 1068 case '/': /* special commands */ 1069 error = read_mtree_command(fp); 1070 break; 1071 default: /* specification */ 1072 ungetc(c, fp); 1073 error = read_mtree_spec(fp); 1074 break; 1075 } 1076 } while (!error); 1077 1078 endpwent(); 1079 endgrent(); 1080 1081 if (error <= 0 && (errors || warnings)) { 1082 warnx("%u error(s) and %u warning(s) in mtree manifest", 1083 errors, warnings); 1084 if (errors) 1085 exit(1); 1086 } 1087 1088 out: 1089 if (error > 0) 1090 errc(1, error, "Error reading mtree file"); 1091 1092 if (fp != stdin) 1093 fclose(fp); 1094 1095 if (mtree_root != NULL) 1096 return (mtree_root); 1097 1098 /* Handle empty specifications. */ 1099 node = create_node(".", S_IFDIR, NULL, &mtree_global); 1100 node->first = node; 1101 return (node); 1102} 1103