15978408cSSascha Wildner /*-
25978408cSSascha Wildner * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
35978408cSSascha Wildner *
45978408cSSascha Wildner * Copyright (c) 2011 Marcel Moolenaar
55978408cSSascha Wildner * All rights reserved.
65978408cSSascha Wildner *
75978408cSSascha Wildner * Redistribution and use in source and binary forms, with or without
85978408cSSascha Wildner * modification, are permitted provided that the following conditions
95978408cSSascha Wildner * are met:
105978408cSSascha Wildner * 1. Redistributions of source code must retain the above copyright
115978408cSSascha Wildner * notice, this list of conditions and the following disclaimer.
125978408cSSascha Wildner * 2. Redistributions in binary form must reproduce the above copyright
135978408cSSascha Wildner * notice, this list of conditions and the following disclaimer in the
145978408cSSascha Wildner * documentation and/or other materials provided with the distribution.
155978408cSSascha Wildner *
165978408cSSascha Wildner * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
175978408cSSascha Wildner * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
185978408cSSascha Wildner * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
195978408cSSascha Wildner * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
205978408cSSascha Wildner * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
215978408cSSascha Wildner * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
225978408cSSascha Wildner * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
235978408cSSascha Wildner * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
245978408cSSascha Wildner * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
255978408cSSascha Wildner * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26811c2036SSascha Wildner *
27811c2036SSascha Wildner * $FreeBSD: head/usr.sbin/makefs/mtree.c 332986 2018-04-25 02:43:53Z pfg $
285978408cSSascha Wildner */
295978408cSSascha Wildner
305978408cSSascha Wildner #if HAVE_NBTOOL_CONFIG_H
315978408cSSascha Wildner #include "nbtool_config.h"
325978408cSSascha Wildner #endif
335978408cSSascha Wildner
345978408cSSascha Wildner #include <sys/param.h>
355978408cSSascha Wildner #include <sys/queue.h>
365978408cSSascha Wildner #include <sys/sbuf.h>
375978408cSSascha Wildner #include <sys/stat.h>
385978408cSSascha Wildner #include <sys/types.h>
395978408cSSascha Wildner #include <assert.h>
405978408cSSascha Wildner #include <errno.h>
415978408cSSascha Wildner #include <fcntl.h>
425978408cSSascha Wildner #include <grp.h>
435978408cSSascha Wildner #include <inttypes.h>
445978408cSSascha Wildner #include <pwd.h>
455978408cSSascha Wildner #include <stdarg.h>
465978408cSSascha Wildner #include <stdbool.h>
475978408cSSascha Wildner #include <stddef.h>
485978408cSSascha Wildner #include <stdio.h>
495978408cSSascha Wildner #include <stdlib.h>
505978408cSSascha Wildner #include <string.h>
515978408cSSascha Wildner #include <strings.h>
525978408cSSascha Wildner #include <time.h>
535978408cSSascha Wildner #include <unistd.h>
545978408cSSascha Wildner #include <util.h>
555978408cSSascha Wildner #include <vis.h>
565978408cSSascha Wildner
575978408cSSascha Wildner #include "makefs.h"
585978408cSSascha Wildner
595978408cSSascha Wildner #ifndef ENOATTR
605978408cSSascha Wildner #define ENOATTR ENODATA
615978408cSSascha Wildner #endif
625978408cSSascha Wildner
635978408cSSascha Wildner #define IS_DOT(nm) ((nm)[0] == '.' && (nm)[1] == '\0')
645978408cSSascha Wildner #define IS_DOTDOT(nm) ((nm)[0] == '.' && (nm)[1] == '.' && (nm)[2] == '\0')
655978408cSSascha Wildner
665978408cSSascha Wildner struct mtree_fileinfo {
675978408cSSascha Wildner SLIST_ENTRY(mtree_fileinfo) next;
685978408cSSascha Wildner FILE *fp;
695978408cSSascha Wildner const char *name;
705978408cSSascha Wildner u_int line;
715978408cSSascha Wildner };
725978408cSSascha Wildner
735978408cSSascha Wildner /* Global state used while parsing. */
745978408cSSascha Wildner static SLIST_HEAD(, mtree_fileinfo) mtree_fileinfo =
755978408cSSascha Wildner SLIST_HEAD_INITIALIZER(mtree_fileinfo);
765978408cSSascha Wildner static fsnode *mtree_root;
775978408cSSascha Wildner static fsnode *mtree_current;
785978408cSSascha Wildner static fsnode mtree_global;
795978408cSSascha Wildner static fsinode mtree_global_inode;
805978408cSSascha Wildner static u_int errors, warnings;
815978408cSSascha Wildner
825978408cSSascha Wildner static void mtree_error(const char *, ...) __printflike(1, 2);
835978408cSSascha Wildner static void mtree_warning(const char *, ...) __printflike(1, 2);
845978408cSSascha Wildner
855978408cSSascha Wildner static int
mtree_file_push(const char * name,FILE * fp)865978408cSSascha Wildner mtree_file_push(const char *name, FILE *fp)
875978408cSSascha Wildner {
885978408cSSascha Wildner struct mtree_fileinfo *fi;
895978408cSSascha Wildner
905978408cSSascha Wildner fi = emalloc(sizeof(*fi));
915978408cSSascha Wildner if (strcmp(name, "-") == 0)
925978408cSSascha Wildner fi->name = estrdup("(stdin)");
935978408cSSascha Wildner else
945978408cSSascha Wildner fi->name = estrdup(name);
955978408cSSascha Wildner if (fi->name == NULL) {
965978408cSSascha Wildner free(fi);
975978408cSSascha Wildner return (ENOMEM);
985978408cSSascha Wildner }
995978408cSSascha Wildner
1005978408cSSascha Wildner fi->fp = fp;
1015978408cSSascha Wildner fi->line = 0;
1025978408cSSascha Wildner
1035978408cSSascha Wildner SLIST_INSERT_HEAD(&mtree_fileinfo, fi, next);
1045978408cSSascha Wildner return (0);
1055978408cSSascha Wildner }
1065978408cSSascha Wildner
1075978408cSSascha Wildner static void
mtree_print(const char * msgtype,const char * fmt,va_list ap)1085978408cSSascha Wildner mtree_print(const char *msgtype, const char *fmt, va_list ap)
1095978408cSSascha Wildner {
1105978408cSSascha Wildner struct mtree_fileinfo *fi;
1115978408cSSascha Wildner
1125978408cSSascha Wildner if (msgtype != NULL) {
1135978408cSSascha Wildner fi = SLIST_FIRST(&mtree_fileinfo);
1145978408cSSascha Wildner if (fi != NULL)
1155978408cSSascha Wildner fprintf(stderr, "%s:%u: ", fi->name, fi->line);
1165978408cSSascha Wildner fprintf(stderr, "%s: ", msgtype);
1175978408cSSascha Wildner }
1185978408cSSascha Wildner vfprintf(stderr, fmt, ap);
1195978408cSSascha Wildner }
1205978408cSSascha Wildner
1215978408cSSascha Wildner static void
mtree_error(const char * fmt,...)1225978408cSSascha Wildner mtree_error(const char *fmt, ...)
1235978408cSSascha Wildner {
1245978408cSSascha Wildner va_list ap;
1255978408cSSascha Wildner
1265978408cSSascha Wildner va_start(ap, fmt);
1275978408cSSascha Wildner mtree_print("error", fmt, ap);
1285978408cSSascha Wildner va_end(ap);
1295978408cSSascha Wildner
1305978408cSSascha Wildner errors++;
1315978408cSSascha Wildner fputc('\n', stderr);
1325978408cSSascha Wildner }
1335978408cSSascha Wildner
1345978408cSSascha Wildner static void
mtree_warning(const char * fmt,...)1355978408cSSascha Wildner mtree_warning(const char *fmt, ...)
1365978408cSSascha Wildner {
1375978408cSSascha Wildner va_list ap;
1385978408cSSascha Wildner
1395978408cSSascha Wildner va_start(ap, fmt);
1405978408cSSascha Wildner mtree_print("warning", fmt, ap);
1415978408cSSascha Wildner va_end(ap);
1425978408cSSascha Wildner
1435978408cSSascha Wildner warnings++;
1445978408cSSascha Wildner fputc('\n', stderr);
1455978408cSSascha Wildner }
1465978408cSSascha Wildner
1475978408cSSascha Wildner #ifndef MAKEFS_MAX_TREE_DEPTH
1485978408cSSascha Wildner # define MAKEFS_MAX_TREE_DEPTH (MAXPATHLEN/2)
1495978408cSSascha Wildner #endif
1505978408cSSascha Wildner
1515978408cSSascha Wildner /* construct path to node->name */
1525978408cSSascha Wildner static char *
mtree_file_path(fsnode * node)1535978408cSSascha Wildner mtree_file_path(fsnode *node)
1545978408cSSascha Wildner {
1555978408cSSascha Wildner fsnode *pnode;
1565978408cSSascha Wildner struct sbuf *sb;
1575978408cSSascha Wildner char *res, *rp[MAKEFS_MAX_TREE_DEPTH];
1585978408cSSascha Wildner int depth;
1595978408cSSascha Wildner
1605978408cSSascha Wildner depth = 0;
1615978408cSSascha Wildner rp[depth] = node->name;
1625978408cSSascha Wildner for (pnode = node->parent; pnode && depth < MAKEFS_MAX_TREE_DEPTH - 1;
1635978408cSSascha Wildner pnode = pnode->parent) {
1645978408cSSascha Wildner if (strcmp(pnode->name, ".") == 0)
1655978408cSSascha Wildner break;
1665978408cSSascha Wildner rp[++depth] = pnode->name;
1675978408cSSascha Wildner }
1685978408cSSascha Wildner
1695978408cSSascha Wildner sb = sbuf_new_auto();
1705978408cSSascha Wildner if (sb == NULL) {
1715978408cSSascha Wildner errno = ENOMEM;
1725978408cSSascha Wildner return (NULL);
1735978408cSSascha Wildner }
1745978408cSSascha Wildner while (depth > 0) {
1755978408cSSascha Wildner sbuf_cat(sb, rp[depth--]);
1765978408cSSascha Wildner sbuf_putc(sb, '/');
1775978408cSSascha Wildner }
1785978408cSSascha Wildner sbuf_cat(sb, rp[depth]);
1795978408cSSascha Wildner sbuf_finish(sb);
1805978408cSSascha Wildner res = estrdup(sbuf_data(sb));
1815978408cSSascha Wildner sbuf_delete(sb);
1825978408cSSascha Wildner if (res == NULL)
1835978408cSSascha Wildner errno = ENOMEM;
1845978408cSSascha Wildner return res;
1855978408cSSascha Wildner
1865978408cSSascha Wildner }
1875978408cSSascha Wildner
1885978408cSSascha Wildner /* mtree_resolve() sets errno to indicate why NULL was returned. */
1895978408cSSascha Wildner static char *
mtree_resolve(const char * spec,int * istemp)1905978408cSSascha Wildner mtree_resolve(const char *spec, int *istemp)
1915978408cSSascha Wildner {
1925978408cSSascha Wildner struct sbuf *sb;
1935978408cSSascha Wildner char *res, *var = NULL;
1945978408cSSascha Wildner const char *base, *p, *v;
1955978408cSSascha Wildner size_t len;
1965978408cSSascha Wildner int c, error, quoted, subst;
1975978408cSSascha Wildner
1985978408cSSascha Wildner len = strlen(spec);
1995978408cSSascha Wildner if (len == 0) {
2005978408cSSascha Wildner errno = EINVAL;
2015978408cSSascha Wildner return (NULL);
2025978408cSSascha Wildner }
2035978408cSSascha Wildner
2045978408cSSascha Wildner c = (len > 1) ? (spec[0] == spec[len - 1]) ? spec[0] : 0 : 0;
2055978408cSSascha Wildner *istemp = (c == '`') ? 1 : 0;
2065978408cSSascha Wildner subst = (c == '`' || c == '"') ? 1 : 0;
2075978408cSSascha Wildner quoted = (subst || c == '\'') ? 1 : 0;
2085978408cSSascha Wildner
2095978408cSSascha Wildner if (!subst) {
2105978408cSSascha Wildner res = estrdup(spec + quoted);
2115978408cSSascha Wildner if (quoted)
2125978408cSSascha Wildner res[len - 2] = '\0';
2135978408cSSascha Wildner return (res);
2145978408cSSascha Wildner }
2155978408cSSascha Wildner
2165978408cSSascha Wildner sb = sbuf_new_auto();
2175978408cSSascha Wildner if (sb == NULL) {
2185978408cSSascha Wildner errno = ENOMEM;
2195978408cSSascha Wildner return (NULL);
2205978408cSSascha Wildner }
2215978408cSSascha Wildner
2225978408cSSascha Wildner base = spec + 1;
2235978408cSSascha Wildner len -= 2;
2245978408cSSascha Wildner error = 0;
2255978408cSSascha Wildner while (len > 0) {
2265978408cSSascha Wildner p = strchr(base, '$');
2275978408cSSascha Wildner if (p == NULL) {
2285978408cSSascha Wildner sbuf_bcat(sb, base, len);
2295978408cSSascha Wildner base += len;
2305978408cSSascha Wildner len = 0;
2315978408cSSascha Wildner continue;
2325978408cSSascha Wildner }
2335978408cSSascha Wildner /* The following is safe. spec always starts with a quote. */
2345978408cSSascha Wildner if (p[-1] == '\\')
2355978408cSSascha Wildner p--;
2365978408cSSascha Wildner if (base != p) {
2375978408cSSascha Wildner sbuf_bcat(sb, base, p - base);
2385978408cSSascha Wildner len -= p - base;
2395978408cSSascha Wildner base = p;
2405978408cSSascha Wildner }
2415978408cSSascha Wildner if (*p == '\\') {
2425978408cSSascha Wildner sbuf_putc(sb, '$');
2435978408cSSascha Wildner base += 2;
2445978408cSSascha Wildner len -= 2;
2455978408cSSascha Wildner continue;
2465978408cSSascha Wildner }
2475978408cSSascha Wildner /* Skip the '$'. */
2485978408cSSascha Wildner base++;
2495978408cSSascha Wildner len--;
2505978408cSSascha Wildner /* Handle ${X} vs $X. */
2515978408cSSascha Wildner v = base;
2525978408cSSascha Wildner if (*base == '{') {
2535978408cSSascha Wildner p = strchr(v, '}');
2545978408cSSascha Wildner if (p == NULL)
2555978408cSSascha Wildner p = v;
2565978408cSSascha Wildner } else
2575978408cSSascha Wildner p = v;
2585978408cSSascha Wildner len -= (p + 1) - base;
2595978408cSSascha Wildner base = p + 1;
2605978408cSSascha Wildner
2615978408cSSascha Wildner if (v == p) {
2625978408cSSascha Wildner sbuf_putc(sb, *v);
2635978408cSSascha Wildner continue;
2645978408cSSascha Wildner }
2655978408cSSascha Wildner
2665978408cSSascha Wildner error = ENOMEM;
2675978408cSSascha Wildner var = ecalloc(p - v, 1);
2685978408cSSascha Wildner memcpy(var, v + 1, p - v - 1);
2695978408cSSascha Wildner if (strcmp(var, ".CURDIR") == 0) {
2705978408cSSascha Wildner res = getcwd(NULL, 0);
2715978408cSSascha Wildner if (res == NULL)
2725978408cSSascha Wildner break;
2735978408cSSascha Wildner } else if (strcmp(var, ".PROG") == 0) {
2745978408cSSascha Wildner res = estrdup(getprogname());
2755978408cSSascha Wildner } else {
2765978408cSSascha Wildner v = getenv(var);
2775978408cSSascha Wildner if (v != NULL) {
2785978408cSSascha Wildner res = estrdup(v);
2795978408cSSascha Wildner } else
2805978408cSSascha Wildner res = NULL;
2815978408cSSascha Wildner }
2825978408cSSascha Wildner error = 0;
2835978408cSSascha Wildner
2845978408cSSascha Wildner if (res != NULL) {
2855978408cSSascha Wildner sbuf_cat(sb, res);
2865978408cSSascha Wildner free(res);
2875978408cSSascha Wildner }
2885978408cSSascha Wildner free(var);
2895978408cSSascha Wildner var = NULL;
2905978408cSSascha Wildner }
2915978408cSSascha Wildner
2925978408cSSascha Wildner free(var);
2935978408cSSascha Wildner sbuf_finish(sb);
2945978408cSSascha Wildner res = (error == 0) ? strdup(sbuf_data(sb)) : NULL;
2955978408cSSascha Wildner sbuf_delete(sb);
2965978408cSSascha Wildner if (res == NULL)
2975978408cSSascha Wildner errno = ENOMEM;
2985978408cSSascha Wildner return (res);
2995978408cSSascha Wildner }
3005978408cSSascha Wildner
3015978408cSSascha Wildner static int
skip_over(FILE * fp,const char * cs)3025978408cSSascha Wildner skip_over(FILE *fp, const char *cs)
3035978408cSSascha Wildner {
3045978408cSSascha Wildner int c;
3055978408cSSascha Wildner
3065978408cSSascha Wildner c = getc(fp);
3075978408cSSascha Wildner while (c != EOF && strchr(cs, c) != NULL)
3085978408cSSascha Wildner c = getc(fp);
3095978408cSSascha Wildner if (c != EOF) {
3105978408cSSascha Wildner ungetc(c, fp);
3115978408cSSascha Wildner return (0);
3125978408cSSascha Wildner }
3135978408cSSascha Wildner return (ferror(fp) ? errno : -1);
3145978408cSSascha Wildner }
3155978408cSSascha Wildner
3165978408cSSascha Wildner static int
skip_to(FILE * fp,const char * cs)3175978408cSSascha Wildner skip_to(FILE *fp, const char *cs)
3185978408cSSascha Wildner {
3195978408cSSascha Wildner int c;
3205978408cSSascha Wildner
3215978408cSSascha Wildner c = getc(fp);
3225978408cSSascha Wildner while (c != EOF && strchr(cs, c) == NULL)
3235978408cSSascha Wildner c = getc(fp);
3245978408cSSascha Wildner if (c != EOF) {
3255978408cSSascha Wildner ungetc(c, fp);
3265978408cSSascha Wildner return (0);
3275978408cSSascha Wildner }
3285978408cSSascha Wildner return (ferror(fp) ? errno : -1);
3295978408cSSascha Wildner }
3305978408cSSascha Wildner
3315978408cSSascha Wildner static int
read_word(FILE * fp,char * buf,size_t bufsz)3325978408cSSascha Wildner read_word(FILE *fp, char *buf, size_t bufsz)
3335978408cSSascha Wildner {
3345978408cSSascha Wildner struct mtree_fileinfo *fi;
3355978408cSSascha Wildner size_t idx, qidx;
3365978408cSSascha Wildner int c, done, error, esc, qlvl;
3375978408cSSascha Wildner
3385978408cSSascha Wildner if (bufsz == 0)
3395978408cSSascha Wildner return (EINVAL);
3405978408cSSascha Wildner
3415978408cSSascha Wildner done = 0;
3425978408cSSascha Wildner esc = 0;
3435978408cSSascha Wildner idx = 0;
3445978408cSSascha Wildner qidx = -1;
3455978408cSSascha Wildner qlvl = 0;
3465978408cSSascha Wildner do {
3475978408cSSascha Wildner c = getc(fp);
3485978408cSSascha Wildner switch (c) {
3495978408cSSascha Wildner case EOF:
3505978408cSSascha Wildner buf[idx] = '\0';
3515978408cSSascha Wildner error = ferror(fp) ? errno : -1;
3525978408cSSascha Wildner if (error == -1)
3535978408cSSascha Wildner mtree_error("unexpected end of file");
3545978408cSSascha Wildner return (error);
3555978408cSSascha Wildner case '#': /* comment -- skip to end of line. */
3565978408cSSascha Wildner if (!esc) {
3575978408cSSascha Wildner error = skip_to(fp, "\n");
3585978408cSSascha Wildner if (!error)
3595978408cSSascha Wildner continue;
3605978408cSSascha Wildner }
3615978408cSSascha Wildner break;
3625978408cSSascha Wildner case '\\':
3635978408cSSascha Wildner esc++;
3645978408cSSascha Wildner break;
3655978408cSSascha Wildner case '`':
3665978408cSSascha Wildner case '\'':
3675978408cSSascha Wildner case '"':
3685978408cSSascha Wildner if (esc)
3695978408cSSascha Wildner break;
3705978408cSSascha Wildner if (qlvl == 0) {
3715978408cSSascha Wildner qlvl++;
3725978408cSSascha Wildner qidx = idx;
3735978408cSSascha Wildner } else if (c == buf[qidx]) {
3745978408cSSascha Wildner qlvl--;
3755978408cSSascha Wildner if (qlvl > 0) {
3765978408cSSascha Wildner do {
3775978408cSSascha Wildner qidx--;
3785978408cSSascha Wildner } while (buf[qidx] != '`' &&
3795978408cSSascha Wildner buf[qidx] != '\'' &&
3805978408cSSascha Wildner buf[qidx] != '"');
3815978408cSSascha Wildner } else
3825978408cSSascha Wildner qidx = -1;
3835978408cSSascha Wildner } else {
3845978408cSSascha Wildner qlvl++;
3855978408cSSascha Wildner qidx = idx;
3865978408cSSascha Wildner }
3875978408cSSascha Wildner break;
3885978408cSSascha Wildner case ' ':
3895978408cSSascha Wildner case '\t':
3905978408cSSascha Wildner case '\n':
3915978408cSSascha Wildner if (!esc && qlvl == 0) {
3925978408cSSascha Wildner ungetc(c, fp);
3935978408cSSascha Wildner c = '\0';
3945978408cSSascha Wildner done = 1;
3955978408cSSascha Wildner break;
3965978408cSSascha Wildner }
3975978408cSSascha Wildner if (c == '\n') {
3985978408cSSascha Wildner /*
3995978408cSSascha Wildner * We going to eat the newline ourselves.
4005978408cSSascha Wildner */
4015978408cSSascha Wildner if (qlvl > 0)
4025978408cSSascha Wildner mtree_warning("quoted word straddles "
4035978408cSSascha Wildner "onto next line.");
4045978408cSSascha Wildner fi = SLIST_FIRST(&mtree_fileinfo);
4055978408cSSascha Wildner fi->line++;
4065978408cSSascha Wildner }
4075978408cSSascha Wildner break;
4085978408cSSascha Wildner default:
4095978408cSSascha Wildner if (esc)
4105978408cSSascha Wildner buf[idx++] = '\\';
4115978408cSSascha Wildner break;
4125978408cSSascha Wildner }
4135978408cSSascha Wildner buf[idx++] = c;
4145978408cSSascha Wildner esc = 0;
4155978408cSSascha Wildner } while (idx < bufsz && !done);
4165978408cSSascha Wildner
4175978408cSSascha Wildner if (idx >= bufsz) {
4185978408cSSascha Wildner mtree_error("word too long to fit buffer (max %zu characters)",
4195978408cSSascha Wildner bufsz);
4205978408cSSascha Wildner skip_to(fp, " \t\n");
4215978408cSSascha Wildner }
4225978408cSSascha Wildner return (0);
4235978408cSSascha Wildner }
4245978408cSSascha Wildner
4255978408cSSascha Wildner static fsnode *
create_node(const char * name,u_int type,fsnode * parent,fsnode * global)4265978408cSSascha Wildner create_node(const char *name, u_int type, fsnode *parent, fsnode *global)
4275978408cSSascha Wildner {
4285978408cSSascha Wildner fsnode *n;
4295978408cSSascha Wildner
4305978408cSSascha Wildner n = ecalloc(1, sizeof(*n));
4315978408cSSascha Wildner n->name = estrdup(name);
4325978408cSSascha Wildner n->type = (type == 0) ? global->type : type;
4335978408cSSascha Wildner n->parent = parent;
4345978408cSSascha Wildner
4355978408cSSascha Wildner n->inode = ecalloc(1, sizeof(*n->inode));
4365978408cSSascha Wildner
4375978408cSSascha Wildner /* Assign global options/defaults. */
4385978408cSSascha Wildner memcpy(n->inode, global->inode, sizeof(*n->inode));
4395978408cSSascha Wildner n->inode->st.st_mode = (n->inode->st.st_mode & ~S_IFMT) | n->type;
4405978408cSSascha Wildner
4415978408cSSascha Wildner if (n->type == S_IFLNK)
4425978408cSSascha Wildner n->symlink = global->symlink;
4435978408cSSascha Wildner else if (n->type == S_IFREG)
4445978408cSSascha Wildner n->contents = global->contents;
4455978408cSSascha Wildner
4465978408cSSascha Wildner return (n);
4475978408cSSascha Wildner }
4485978408cSSascha Wildner
4495978408cSSascha Wildner static void
destroy_node(fsnode * n)4505978408cSSascha Wildner destroy_node(fsnode *n)
4515978408cSSascha Wildner {
4525978408cSSascha Wildner
4535978408cSSascha Wildner assert(n != NULL);
4545978408cSSascha Wildner assert(n->name != NULL);
4555978408cSSascha Wildner assert(n->inode != NULL);
4565978408cSSascha Wildner
4575978408cSSascha Wildner free(n->inode);
4585978408cSSascha Wildner free(n->name);
4595978408cSSascha Wildner free(n);
4605978408cSSascha Wildner }
4615978408cSSascha Wildner
4625978408cSSascha Wildner static int
read_number(const char * tok,u_int base,intmax_t * res,intmax_t min,intmax_t max)4635978408cSSascha Wildner read_number(const char *tok, u_int base, intmax_t *res, intmax_t min,
4645978408cSSascha Wildner intmax_t max)
4655978408cSSascha Wildner {
4665978408cSSascha Wildner char *end;
4675978408cSSascha Wildner intmax_t val;
4685978408cSSascha Wildner
4695978408cSSascha Wildner val = strtoimax(tok, &end, base);
4705978408cSSascha Wildner if (end == tok || end[0] != '\0')
4715978408cSSascha Wildner return (EINVAL);
4725978408cSSascha Wildner if (val < min || val > max)
4735978408cSSascha Wildner return (EDOM);
4745978408cSSascha Wildner *res = val;
4755978408cSSascha Wildner return (0);
4765978408cSSascha Wildner }
4775978408cSSascha Wildner
4785978408cSSascha Wildner static int
read_mtree_keywords(FILE * fp,fsnode * node)4795978408cSSascha Wildner read_mtree_keywords(FILE *fp, fsnode *node)
4805978408cSSascha Wildner {
4815978408cSSascha Wildner char keyword[PATH_MAX];
4825978408cSSascha Wildner char *name, *p, *value;
4835978408cSSascha Wildner gid_t gid;
4845978408cSSascha Wildner uid_t uid;
4855978408cSSascha Wildner struct stat *st, sb;
4865978408cSSascha Wildner intmax_t num;
4875978408cSSascha Wildner u_long flset, flclr;
4885978408cSSascha Wildner int error, istemp;
4895978408cSSascha Wildner uint32_t type;
4905978408cSSascha Wildner
4915978408cSSascha Wildner st = &node->inode->st;
4925978408cSSascha Wildner do {
4935978408cSSascha Wildner error = skip_over(fp, " \t");
4945978408cSSascha Wildner if (error)
4955978408cSSascha Wildner break;
4965978408cSSascha Wildner
4975978408cSSascha Wildner error = read_word(fp, keyword, sizeof(keyword));
4985978408cSSascha Wildner if (error)
4995978408cSSascha Wildner break;
5005978408cSSascha Wildner
5015978408cSSascha Wildner if (keyword[0] == '\0')
5025978408cSSascha Wildner break;
5035978408cSSascha Wildner
5045978408cSSascha Wildner value = strchr(keyword, '=');
5055978408cSSascha Wildner if (value != NULL)
5065978408cSSascha Wildner *value++ = '\0';
5075978408cSSascha Wildner
5085978408cSSascha Wildner /*
5095978408cSSascha Wildner * We use EINVAL, ENOATTR, ENOSYS and ENXIO to signal
5105978408cSSascha Wildner * certain conditions:
5115978408cSSascha Wildner * EINVAL - Value provided for a keyword that does
5125978408cSSascha Wildner * not take a value. The value is ignored.
5135978408cSSascha Wildner * ENOATTR - Value missing for a keyword that needs
5145978408cSSascha Wildner * a value. The keyword is ignored.
5155978408cSSascha Wildner * ENOSYS - Unsupported keyword encountered. The
5165978408cSSascha Wildner * keyword is ignored.
5175978408cSSascha Wildner * ENXIO - Value provided for a keyword that does
5185978408cSSascha Wildner * not take a value. The value is ignored.
5195978408cSSascha Wildner */
5205978408cSSascha Wildner switch (keyword[0]) {
5215978408cSSascha Wildner case 'c':
5225978408cSSascha Wildner if (strcmp(keyword, "contents") == 0) {
5235978408cSSascha Wildner if (value == NULL) {
5245978408cSSascha Wildner error = ENOATTR;
5255978408cSSascha Wildner break;
5265978408cSSascha Wildner }
5275978408cSSascha Wildner node->contents = estrdup(value);
5285978408cSSascha Wildner } else
5295978408cSSascha Wildner error = ENOSYS;
5305978408cSSascha Wildner break;
5315978408cSSascha Wildner case 'f':
5325978408cSSascha Wildner if (strcmp(keyword, "flags") == 0) {
5335978408cSSascha Wildner if (value == NULL) {
5345978408cSSascha Wildner error = ENOATTR;
5355978408cSSascha Wildner break;
5365978408cSSascha Wildner }
5375978408cSSascha Wildner flset = flclr = 0;
5385978408cSSascha Wildner #if HAVE_STRUCT_STAT_ST_FLAGS
5395978408cSSascha Wildner if (!strtofflags(&value, &flset, &flclr)) {
5405978408cSSascha Wildner st->st_flags &= ~flclr;
5415978408cSSascha Wildner st->st_flags |= flset;
5425978408cSSascha Wildner } else
5435978408cSSascha Wildner error = errno;
5445978408cSSascha Wildner #endif
5455978408cSSascha Wildner } else
5465978408cSSascha Wildner error = ENOSYS;
5475978408cSSascha Wildner break;
5485978408cSSascha Wildner case 'g':
5495978408cSSascha Wildner if (strcmp(keyword, "gid") == 0) {
5505978408cSSascha Wildner if (value == NULL) {
5515978408cSSascha Wildner error = ENOATTR;
5525978408cSSascha Wildner break;
5535978408cSSascha Wildner }
5545978408cSSascha Wildner error = read_number(value, 10, &num,
5555978408cSSascha Wildner 0, UINT_MAX);
5565978408cSSascha Wildner if (!error)
5575978408cSSascha Wildner st->st_gid = num;
5585978408cSSascha Wildner } else if (strcmp(keyword, "gname") == 0) {
5595978408cSSascha Wildner if (value == NULL) {
5605978408cSSascha Wildner error = ENOATTR;
5615978408cSSascha Wildner break;
5625978408cSSascha Wildner }
5635978408cSSascha Wildner if (gid_from_group(value, &gid) == 0)
5645978408cSSascha Wildner st->st_gid = gid;
5655978408cSSascha Wildner else
5665978408cSSascha Wildner error = EINVAL;
5675978408cSSascha Wildner } else
5685978408cSSascha Wildner error = ENOSYS;
5695978408cSSascha Wildner break;
5705978408cSSascha Wildner case 'l':
5715978408cSSascha Wildner if (strcmp(keyword, "link") == 0) {
5725978408cSSascha Wildner if (value == NULL) {
5735978408cSSascha Wildner error = ENOATTR;
5745978408cSSascha Wildner break;
5755978408cSSascha Wildner }
5765978408cSSascha Wildner node->symlink = emalloc(strlen(value) + 1);
5775978408cSSascha Wildner if (node->symlink == NULL) {
5785978408cSSascha Wildner error = errno;
5795978408cSSascha Wildner break;
5805978408cSSascha Wildner }
5815978408cSSascha Wildner if (strunvis(node->symlink, value) < 0) {
5825978408cSSascha Wildner error = errno;
5835978408cSSascha Wildner break;
5845978408cSSascha Wildner }
5855978408cSSascha Wildner } else
5865978408cSSascha Wildner error = ENOSYS;
5875978408cSSascha Wildner break;
5885978408cSSascha Wildner case 'm':
5895978408cSSascha Wildner if (strcmp(keyword, "mode") == 0) {
5905978408cSSascha Wildner if (value == NULL) {
5915978408cSSascha Wildner error = ENOATTR;
5925978408cSSascha Wildner break;
5935978408cSSascha Wildner }
5945978408cSSascha Wildner if (value[0] >= '0' && value[0] <= '9') {
5955978408cSSascha Wildner error = read_number(value, 8, &num,
5965978408cSSascha Wildner 0, 07777);
5975978408cSSascha Wildner if (!error) {
5985978408cSSascha Wildner st->st_mode &= S_IFMT;
5995978408cSSascha Wildner st->st_mode |= num;
6005978408cSSascha Wildner }
6015978408cSSascha Wildner } else {
6025978408cSSascha Wildner /* Symbolic mode not supported. */
6035978408cSSascha Wildner error = EINVAL;
6045978408cSSascha Wildner break;
6055978408cSSascha Wildner }
6065978408cSSascha Wildner } else
6075978408cSSascha Wildner error = ENOSYS;
6085978408cSSascha Wildner break;
6095978408cSSascha Wildner case 'o':
6105978408cSSascha Wildner if (strcmp(keyword, "optional") == 0) {
6115978408cSSascha Wildner if (value != NULL)
6125978408cSSascha Wildner error = ENXIO;
6135978408cSSascha Wildner node->flags |= FSNODE_F_OPTIONAL;
6145978408cSSascha Wildner } else
6155978408cSSascha Wildner error = ENOSYS;
6165978408cSSascha Wildner break;
6175978408cSSascha Wildner case 's':
6185978408cSSascha Wildner if (strcmp(keyword, "size") == 0) {
6195978408cSSascha Wildner if (value == NULL) {
6205978408cSSascha Wildner error = ENOATTR;
6215978408cSSascha Wildner break;
6225978408cSSascha Wildner }
6235978408cSSascha Wildner error = read_number(value, 10, &num,
6245978408cSSascha Wildner 0, INTMAX_MAX);
6255978408cSSascha Wildner if (!error)
6265978408cSSascha Wildner st->st_size = num;
6275978408cSSascha Wildner } else
6285978408cSSascha Wildner error = ENOSYS;
6295978408cSSascha Wildner break;
6305978408cSSascha Wildner case 't':
631*32357d2aSTomohiro Kusumi if (strcmp(keyword, "tags") == 0) {
632*32357d2aSTomohiro Kusumi if (value == NULL) {
633*32357d2aSTomohiro Kusumi error = ENOATTR;
634*32357d2aSTomohiro Kusumi break;
635*32357d2aSTomohiro Kusumi }
636*32357d2aSTomohiro Kusumi /* Ignore. */
637*32357d2aSTomohiro Kusumi } else if (strcmp(keyword, "time") == 0) {
6385978408cSSascha Wildner if (value == NULL) {
6395978408cSSascha Wildner error = ENOATTR;
6405978408cSSascha Wildner break;
6415978408cSSascha Wildner }
6425978408cSSascha Wildner p = strchr(value, '.');
6435978408cSSascha Wildner if (p != NULL)
6445978408cSSascha Wildner *p++ = '\0';
6455978408cSSascha Wildner error = read_number(value, 10, &num, 0,
6465978408cSSascha Wildner INTMAX_MAX);
6475978408cSSascha Wildner if (error)
6485978408cSSascha Wildner break;
6495978408cSSascha Wildner st->st_atime = num;
6505978408cSSascha Wildner st->st_ctime = num;
6515978408cSSascha Wildner st->st_mtime = num;
65258306fcdSTomohiro Kusumi #if HAVE_STRUCT_STAT_ST_MTIMENSEC
6535978408cSSascha Wildner if (p == NULL)
6545978408cSSascha Wildner break;
6555978408cSSascha Wildner error = read_number(p, 10, &num, 0,
6565978408cSSascha Wildner INTMAX_MAX);
6575978408cSSascha Wildner if (error)
6585978408cSSascha Wildner break;
65958306fcdSTomohiro Kusumi st->st_atimensec = num;
66058306fcdSTomohiro Kusumi st->st_ctimensec = num;
66158306fcdSTomohiro Kusumi st->st_mtimensec = num;
66258306fcdSTomohiro Kusumi #endif
6635978408cSSascha Wildner } else if (strcmp(keyword, "type") == 0) {
6645978408cSSascha Wildner if (value == NULL) {
6655978408cSSascha Wildner error = ENOATTR;
6665978408cSSascha Wildner break;
6675978408cSSascha Wildner }
6685978408cSSascha Wildner if (strcmp(value, "dir") == 0)
6695978408cSSascha Wildner node->type = S_IFDIR;
6705978408cSSascha Wildner else if (strcmp(value, "file") == 0)
6715978408cSSascha Wildner node->type = S_IFREG;
6725978408cSSascha Wildner else if (strcmp(value, "link") == 0)
6735978408cSSascha Wildner node->type = S_IFLNK;
6745978408cSSascha Wildner else
6755978408cSSascha Wildner error = EINVAL;
6765978408cSSascha Wildner } else
6775978408cSSascha Wildner error = ENOSYS;
6785978408cSSascha Wildner break;
6795978408cSSascha Wildner case 'u':
6805978408cSSascha Wildner if (strcmp(keyword, "uid") == 0) {
6815978408cSSascha Wildner if (value == NULL) {
6825978408cSSascha Wildner error = ENOATTR;
6835978408cSSascha Wildner break;
6845978408cSSascha Wildner }
6855978408cSSascha Wildner error = read_number(value, 10, &num,
6865978408cSSascha Wildner 0, UINT_MAX);
6875978408cSSascha Wildner if (!error)
6885978408cSSascha Wildner st->st_uid = num;
6895978408cSSascha Wildner } else if (strcmp(keyword, "uname") == 0) {
6905978408cSSascha Wildner if (value == NULL) {
6915978408cSSascha Wildner error = ENOATTR;
6925978408cSSascha Wildner break;
6935978408cSSascha Wildner }
6945978408cSSascha Wildner if (uid_from_user(value, &uid) == 0)
6955978408cSSascha Wildner st->st_uid = uid;
6965978408cSSascha Wildner else
6975978408cSSascha Wildner error = EINVAL;
6985978408cSSascha Wildner } else
6995978408cSSascha Wildner error = ENOSYS;
7005978408cSSascha Wildner break;
7015978408cSSascha Wildner default:
7025978408cSSascha Wildner error = ENOSYS;
7035978408cSSascha Wildner break;
7045978408cSSascha Wildner }
7055978408cSSascha Wildner
7065978408cSSascha Wildner switch (error) {
7075978408cSSascha Wildner case EINVAL:
7085978408cSSascha Wildner mtree_error("%s: invalid value '%s'", keyword, value);
7095978408cSSascha Wildner break;
7105978408cSSascha Wildner case ENOATTR:
7115978408cSSascha Wildner mtree_error("%s: keyword needs a value", keyword);
7125978408cSSascha Wildner break;
7135978408cSSascha Wildner case ENOSYS:
7145978408cSSascha Wildner mtree_warning("%s: unsupported keyword", keyword);
7155978408cSSascha Wildner break;
7165978408cSSascha Wildner case ENXIO:
7175978408cSSascha Wildner mtree_error("%s: keyword does not take a value",
7185978408cSSascha Wildner keyword);
7195978408cSSascha Wildner break;
7205978408cSSascha Wildner }
7215978408cSSascha Wildner } while (1);
7225978408cSSascha Wildner
7235978408cSSascha Wildner if (error)
7245978408cSSascha Wildner return (error);
7255978408cSSascha Wildner
7265978408cSSascha Wildner st->st_mode = (st->st_mode & ~S_IFMT) | node->type;
7275978408cSSascha Wildner
7285978408cSSascha Wildner /* Nothing more to do for the global defaults. */
7295978408cSSascha Wildner if (node->name == NULL)
7305978408cSSascha Wildner return (0);
7315978408cSSascha Wildner
7325978408cSSascha Wildner /*
7335978408cSSascha Wildner * Be intelligent about the file type.
7345978408cSSascha Wildner */
7355978408cSSascha Wildner if (node->contents != NULL) {
7365978408cSSascha Wildner if (node->symlink != NULL) {
7375978408cSSascha Wildner mtree_error("%s: both link and contents keywords "
7385978408cSSascha Wildner "defined", node->name);
7395978408cSSascha Wildner return (0);
7405978408cSSascha Wildner }
7415978408cSSascha Wildner type = S_IFREG;
7425978408cSSascha Wildner } else if (node->type != 0) {
7435978408cSSascha Wildner type = node->type;
7445978408cSSascha Wildner if (type == S_IFREG) {
7455978408cSSascha Wildner /* the named path is the default contents */
7465978408cSSascha Wildner node->contents = mtree_file_path(node);
7475978408cSSascha Wildner }
7485978408cSSascha Wildner } else
7495978408cSSascha Wildner type = (node->symlink != NULL) ? S_IFLNK : S_IFDIR;
7505978408cSSascha Wildner
7515978408cSSascha Wildner if (node->type == 0)
7525978408cSSascha Wildner node->type = type;
7535978408cSSascha Wildner
7545978408cSSascha Wildner if (node->type != type) {
7555978408cSSascha Wildner mtree_error("%s: file type and defined keywords to not match",
7565978408cSSascha Wildner node->name);
7575978408cSSascha Wildner return (0);
7585978408cSSascha Wildner }
7595978408cSSascha Wildner
7605978408cSSascha Wildner st->st_mode = (st->st_mode & ~S_IFMT) | node->type;
7615978408cSSascha Wildner
7625978408cSSascha Wildner if (node->contents == NULL)
7635978408cSSascha Wildner return (0);
7645978408cSSascha Wildner
7655978408cSSascha Wildner name = mtree_resolve(node->contents, &istemp);
7665978408cSSascha Wildner if (name == NULL)
7675978408cSSascha Wildner return (errno);
7685978408cSSascha Wildner
7695978408cSSascha Wildner if (stat(name, &sb) != 0) {
7705978408cSSascha Wildner mtree_error("%s: contents file '%s' not found", node->name,
7715978408cSSascha Wildner name);
7725978408cSSascha Wildner free(name);
7735978408cSSascha Wildner return (0);
7745978408cSSascha Wildner }
7755978408cSSascha Wildner
7765978408cSSascha Wildner /*
7775978408cSSascha Wildner * Check for hardlinks. If the contents key is used, then the check
7785978408cSSascha Wildner * will only trigger if the contents file is a link even if it is used
7795978408cSSascha Wildner * by more than one file
7805978408cSSascha Wildner */
7815978408cSSascha Wildner if (sb.st_nlink > 1) {
7825978408cSSascha Wildner fsinode *curino;
7835978408cSSascha Wildner
7845978408cSSascha Wildner st->st_ino = sb.st_ino;
7855978408cSSascha Wildner st->st_dev = sb.st_dev;
7865978408cSSascha Wildner curino = link_check(node->inode);
7875978408cSSascha Wildner if (curino != NULL) {
7885978408cSSascha Wildner free(node->inode);
7895978408cSSascha Wildner node->inode = curino;
7905978408cSSascha Wildner node->inode->nlink++;
7912a7b2908STomohiro Kusumi /* Reset st since node->inode has been updated. */
7922a7b2908STomohiro Kusumi st = &node->inode->st;
7935978408cSSascha Wildner }
7945978408cSSascha Wildner }
7955978408cSSascha Wildner
7965978408cSSascha Wildner free(node->contents);
7975978408cSSascha Wildner node->contents = name;
7985978408cSSascha Wildner st->st_size = sb.st_size;
7995978408cSSascha Wildner return (0);
8005978408cSSascha Wildner }
8015978408cSSascha Wildner
8025978408cSSascha Wildner static int
read_mtree_command(FILE * fp)8035978408cSSascha Wildner read_mtree_command(FILE *fp)
8045978408cSSascha Wildner {
8055978408cSSascha Wildner char cmd[10];
8065978408cSSascha Wildner int error;
8075978408cSSascha Wildner
8085978408cSSascha Wildner error = read_word(fp, cmd, sizeof(cmd));
8095978408cSSascha Wildner if (error)
8105978408cSSascha Wildner goto out;
8115978408cSSascha Wildner
8125978408cSSascha Wildner error = read_mtree_keywords(fp, &mtree_global);
8135978408cSSascha Wildner
8145978408cSSascha Wildner out:
8155978408cSSascha Wildner skip_to(fp, "\n");
8165978408cSSascha Wildner (void)getc(fp);
8175978408cSSascha Wildner return (error);
8185978408cSSascha Wildner }
8195978408cSSascha Wildner
8205978408cSSascha Wildner static int
read_mtree_spec1(FILE * fp,bool def,const char * name)8215978408cSSascha Wildner read_mtree_spec1(FILE *fp, bool def, const char *name)
8225978408cSSascha Wildner {
8235978408cSSascha Wildner fsnode *last, *node, *parent;
8245978408cSSascha Wildner u_int type;
8255978408cSSascha Wildner int error;
8265978408cSSascha Wildner
8275978408cSSascha Wildner assert(name[0] != '\0');
8285978408cSSascha Wildner
8295978408cSSascha Wildner /*
8305978408cSSascha Wildner * Treat '..' specially, because it only changes our current
8315978408cSSascha Wildner * directory. We don't create a node for it. We simply ignore
8325978408cSSascha Wildner * any keywords that may appear on the line as well.
8335978408cSSascha Wildner * Going up a directory is a little non-obvious. A directory
8345978408cSSascha Wildner * node has a corresponding '.' child. The parent of '.' is
8355978408cSSascha Wildner * not the '.' node of the parent directory, but the directory
8365978408cSSascha Wildner * node within the parent to which the child relates. However,
8375978408cSSascha Wildner * going up a directory means we need to find the '.' node to
8385978408cSSascha Wildner * which the directoy node is linked. This we can do via the
8395978408cSSascha Wildner * first * pointer, because '.' is always the first entry in a
8405978408cSSascha Wildner * directory.
8415978408cSSascha Wildner */
8425978408cSSascha Wildner if (IS_DOTDOT(name)) {
8435978408cSSascha Wildner /* This deals with NULL pointers as well. */
8445978408cSSascha Wildner if (mtree_current == mtree_root) {
8455978408cSSascha Wildner mtree_warning("ignoring .. in root directory");
8465978408cSSascha Wildner return (0);
8475978408cSSascha Wildner }
8485978408cSSascha Wildner
8495978408cSSascha Wildner node = mtree_current;
8505978408cSSascha Wildner
8515978408cSSascha Wildner assert(node != NULL);
8525978408cSSascha Wildner assert(IS_DOT(node->name));
8535978408cSSascha Wildner assert(node->first == node);
8545978408cSSascha Wildner
8555978408cSSascha Wildner /* Get the corresponding directory node in the parent. */
8565978408cSSascha Wildner node = mtree_current->parent;
8575978408cSSascha Wildner
8585978408cSSascha Wildner assert(node != NULL);
8595978408cSSascha Wildner assert(!IS_DOT(node->name));
8605978408cSSascha Wildner
8615978408cSSascha Wildner node = node->first;
8625978408cSSascha Wildner
8635978408cSSascha Wildner assert(node != NULL);
8645978408cSSascha Wildner assert(IS_DOT(node->name));
8655978408cSSascha Wildner assert(node->first == node);
8665978408cSSascha Wildner
8675978408cSSascha Wildner mtree_current = node;
8685978408cSSascha Wildner return (0);
8695978408cSSascha Wildner }
8705978408cSSascha Wildner
8715978408cSSascha Wildner /*
8725978408cSSascha Wildner * If we don't have a current directory and the first specification
8735978408cSSascha Wildner * (either implicit or defined) is not '.', then we need to create
8745978408cSSascha Wildner * a '.' node first (using a recursive call).
8755978408cSSascha Wildner */
8765978408cSSascha Wildner if (!IS_DOT(name) && mtree_current == NULL) {
8775978408cSSascha Wildner error = read_mtree_spec1(fp, false, ".");
8785978408cSSascha Wildner if (error)
8795978408cSSascha Wildner return (error);
8805978408cSSascha Wildner }
8815978408cSSascha Wildner
8825978408cSSascha Wildner /*
8835978408cSSascha Wildner * Lookup the name in the current directory (if we have a current
8845978408cSSascha Wildner * directory) to make sure we do not create multiple nodes for the
8855978408cSSascha Wildner * same component. For non-definitions, if we find a node with the
8865978408cSSascha Wildner * same name, simply change the current directory. For definitions
8875978408cSSascha Wildner * more happens.
8885978408cSSascha Wildner */
8895978408cSSascha Wildner last = NULL;
8905978408cSSascha Wildner node = mtree_current;
8915978408cSSascha Wildner while (node != NULL) {
8925978408cSSascha Wildner assert(node->first == mtree_current);
8935978408cSSascha Wildner
8945978408cSSascha Wildner if (strcmp(name, node->name) == 0) {
8955978408cSSascha Wildner if (def == true) {
8965978408cSSascha Wildner if (!dupsok)
8975978408cSSascha Wildner mtree_error(
8985978408cSSascha Wildner "duplicate definition of %s",
8995978408cSSascha Wildner name);
9005978408cSSascha Wildner else
9015978408cSSascha Wildner mtree_warning(
9025978408cSSascha Wildner "duplicate definition of %s",
9035978408cSSascha Wildner name);
9045978408cSSascha Wildner return (0);
9055978408cSSascha Wildner }
9065978408cSSascha Wildner
9075978408cSSascha Wildner if (node->type != S_IFDIR) {
9085978408cSSascha Wildner mtree_error("%s is not a directory", name);
9095978408cSSascha Wildner return (0);
9105978408cSSascha Wildner }
9115978408cSSascha Wildner
9125978408cSSascha Wildner assert(!IS_DOT(name));
9135978408cSSascha Wildner
9145978408cSSascha Wildner node = node->child;
9155978408cSSascha Wildner
9165978408cSSascha Wildner assert(node != NULL);
9175978408cSSascha Wildner assert(IS_DOT(node->name));
9185978408cSSascha Wildner
9195978408cSSascha Wildner mtree_current = node;
9205978408cSSascha Wildner return (0);
9215978408cSSascha Wildner }
9225978408cSSascha Wildner
9235978408cSSascha Wildner last = node;
9245978408cSSascha Wildner node = last->next;
9255978408cSSascha Wildner }
9265978408cSSascha Wildner
9275978408cSSascha Wildner parent = (mtree_current != NULL) ? mtree_current->parent : NULL;
9285978408cSSascha Wildner type = (def == false || IS_DOT(name)) ? S_IFDIR : 0;
9295978408cSSascha Wildner node = create_node(name, type, parent, &mtree_global);
9305978408cSSascha Wildner if (node == NULL)
9315978408cSSascha Wildner return (ENOMEM);
9325978408cSSascha Wildner
9335978408cSSascha Wildner if (def == true) {
9345978408cSSascha Wildner error = read_mtree_keywords(fp, node);
9355978408cSSascha Wildner if (error) {
9365978408cSSascha Wildner destroy_node(node);
9375978408cSSascha Wildner return (error);
9385978408cSSascha Wildner }
9395978408cSSascha Wildner }
9405978408cSSascha Wildner
9415978408cSSascha Wildner node->first = (mtree_current != NULL) ? mtree_current : node;
9425978408cSSascha Wildner
9435978408cSSascha Wildner if (last != NULL)
9445978408cSSascha Wildner last->next = node;
9455978408cSSascha Wildner
9465978408cSSascha Wildner if (node->type != S_IFDIR)
9475978408cSSascha Wildner return (0);
9485978408cSSascha Wildner
9495978408cSSascha Wildner if (!IS_DOT(node->name)) {
9505978408cSSascha Wildner parent = node;
9515978408cSSascha Wildner node = create_node(".", S_IFDIR, parent, parent);
9525978408cSSascha Wildner if (node == NULL) {
9535978408cSSascha Wildner last->next = NULL;
9545978408cSSascha Wildner destroy_node(parent);
9555978408cSSascha Wildner return (ENOMEM);
9565978408cSSascha Wildner }
9575978408cSSascha Wildner parent->child = node;
9585978408cSSascha Wildner node->first = node;
9595978408cSSascha Wildner }
9605978408cSSascha Wildner
9615978408cSSascha Wildner assert(node != NULL);
9625978408cSSascha Wildner assert(IS_DOT(node->name));
9635978408cSSascha Wildner assert(node->first == node);
9645978408cSSascha Wildner
9655978408cSSascha Wildner mtree_current = node;
9665978408cSSascha Wildner if (mtree_root == NULL)
9675978408cSSascha Wildner mtree_root = node;
9685978408cSSascha Wildner
9695978408cSSascha Wildner return (0);
9705978408cSSascha Wildner }
9715978408cSSascha Wildner
9725978408cSSascha Wildner static int
read_mtree_spec(FILE * fp)9735978408cSSascha Wildner read_mtree_spec(FILE *fp)
9745978408cSSascha Wildner {
9755978408cSSascha Wildner char pathspec[PATH_MAX], pathtmp[4*PATH_MAX + 1];
9765978408cSSascha Wildner char *cp;
9775978408cSSascha Wildner int error;
9785978408cSSascha Wildner
9795978408cSSascha Wildner error = read_word(fp, pathtmp, sizeof(pathtmp));
9805978408cSSascha Wildner if (error)
9815978408cSSascha Wildner goto out;
9825978408cSSascha Wildner if (strnunvis(pathspec, PATH_MAX, pathtmp) == -1) {
9835978408cSSascha Wildner error = errno;
9845978408cSSascha Wildner goto out;
9855978408cSSascha Wildner }
9865978408cSSascha Wildner error = 0;
9875978408cSSascha Wildner
9885978408cSSascha Wildner cp = strchr(pathspec, '/');
9895978408cSSascha Wildner if (cp != NULL) {
9905978408cSSascha Wildner /* Absolute pathname */
9915978408cSSascha Wildner mtree_current = mtree_root;
9925978408cSSascha Wildner
9935978408cSSascha Wildner do {
9945978408cSSascha Wildner *cp++ = '\0';
9955978408cSSascha Wildner
9965978408cSSascha Wildner /* Disallow '..' as a component. */
9975978408cSSascha Wildner if (IS_DOTDOT(pathspec)) {
9985978408cSSascha Wildner mtree_error("absolute path cannot contain "
9995978408cSSascha Wildner ".. component");
10005978408cSSascha Wildner goto out;
10015978408cSSascha Wildner }
10025978408cSSascha Wildner
10035978408cSSascha Wildner /* Ignore multiple adjacent slashes and '.'. */
10045978408cSSascha Wildner if (pathspec[0] != '\0' && !IS_DOT(pathspec))
10055978408cSSascha Wildner error = read_mtree_spec1(fp, false, pathspec);
10065978408cSSascha Wildner memmove(pathspec, cp, strlen(cp) + 1);
10075978408cSSascha Wildner cp = strchr(pathspec, '/');
10085978408cSSascha Wildner } while (!error && cp != NULL);
10095978408cSSascha Wildner
10105978408cSSascha Wildner /* Disallow '.' and '..' as the last component. */
10115978408cSSascha Wildner if (!error && (IS_DOT(pathspec) || IS_DOTDOT(pathspec))) {
10125978408cSSascha Wildner mtree_error("absolute path cannot contain . or .. "
10135978408cSSascha Wildner "components");
10145978408cSSascha Wildner goto out;
10155978408cSSascha Wildner }
10165978408cSSascha Wildner }
10175978408cSSascha Wildner
10185978408cSSascha Wildner /* Ignore absolute specfications that end with a slash. */
10195978408cSSascha Wildner if (!error && pathspec[0] != '\0')
10205978408cSSascha Wildner error = read_mtree_spec1(fp, true, pathspec);
10215978408cSSascha Wildner
10225978408cSSascha Wildner out:
10235978408cSSascha Wildner skip_to(fp, "\n");
10245978408cSSascha Wildner (void)getc(fp);
10255978408cSSascha Wildner return (error);
10265978408cSSascha Wildner }
10275978408cSSascha Wildner
10285978408cSSascha Wildner fsnode *
read_mtree(const char * fname,fsnode * node)10295978408cSSascha Wildner read_mtree(const char *fname, fsnode *node)
10305978408cSSascha Wildner {
10315978408cSSascha Wildner struct mtree_fileinfo *fi;
10325978408cSSascha Wildner FILE *fp;
10335978408cSSascha Wildner int c, error;
10345978408cSSascha Wildner
10355978408cSSascha Wildner /* We do not yet support nesting... */
10365978408cSSascha Wildner assert(node == NULL);
10375978408cSSascha Wildner
10385978408cSSascha Wildner if (strcmp(fname, "-") == 0)
10395978408cSSascha Wildner fp = stdin;
10405978408cSSascha Wildner else {
10415978408cSSascha Wildner fp = fopen(fname, "r");
10425978408cSSascha Wildner if (fp == NULL)
10435978408cSSascha Wildner err(1, "Can't open `%s'", fname);
10445978408cSSascha Wildner }
10455978408cSSascha Wildner
10465978408cSSascha Wildner error = mtree_file_push(fname, fp);
10475978408cSSascha Wildner if (error)
10485978408cSSascha Wildner goto out;
10495978408cSSascha Wildner
10505978408cSSascha Wildner memset(&mtree_global, 0, sizeof(mtree_global));
10515978408cSSascha Wildner memset(&mtree_global_inode, 0, sizeof(mtree_global_inode));
10525978408cSSascha Wildner mtree_global.inode = &mtree_global_inode;
10535978408cSSascha Wildner mtree_global_inode.nlink = 1;
10545978408cSSascha Wildner mtree_global_inode.st.st_nlink = 1;
10555978408cSSascha Wildner mtree_global_inode.st.st_atime = mtree_global_inode.st.st_ctime =
10565978408cSSascha Wildner mtree_global_inode.st.st_mtime = time(NULL);
10575978408cSSascha Wildner errors = warnings = 0;
10585978408cSSascha Wildner
10595978408cSSascha Wildner setgroupent(1);
10605978408cSSascha Wildner setpassent(1);
10615978408cSSascha Wildner
10625978408cSSascha Wildner mtree_root = node;
10635978408cSSascha Wildner mtree_current = node;
10645978408cSSascha Wildner do {
10655978408cSSascha Wildner /* Start of a new line... */
10665978408cSSascha Wildner fi = SLIST_FIRST(&mtree_fileinfo);
10675978408cSSascha Wildner fi->line++;
10685978408cSSascha Wildner
10695978408cSSascha Wildner error = skip_over(fp, " \t");
10705978408cSSascha Wildner if (error)
10715978408cSSascha Wildner break;
10725978408cSSascha Wildner
10735978408cSSascha Wildner c = getc(fp);
10745978408cSSascha Wildner if (c == EOF) {
10755978408cSSascha Wildner error = ferror(fp) ? errno : -1;
10765978408cSSascha Wildner break;
10775978408cSSascha Wildner }
10785978408cSSascha Wildner
10795978408cSSascha Wildner switch (c) {
10805978408cSSascha Wildner case '\n': /* empty line */
10815978408cSSascha Wildner error = 0;
10825978408cSSascha Wildner break;
10835978408cSSascha Wildner case '#': /* comment -- skip to end of line. */
10845978408cSSascha Wildner error = skip_to(fp, "\n");
10855978408cSSascha Wildner if (!error)
10865978408cSSascha Wildner (void)getc(fp);
10875978408cSSascha Wildner break;
10885978408cSSascha Wildner case '/': /* special commands */
10895978408cSSascha Wildner error = read_mtree_command(fp);
10905978408cSSascha Wildner break;
10915978408cSSascha Wildner default: /* specification */
10925978408cSSascha Wildner ungetc(c, fp);
10935978408cSSascha Wildner error = read_mtree_spec(fp);
10945978408cSSascha Wildner break;
10955978408cSSascha Wildner }
10965978408cSSascha Wildner } while (!error);
10975978408cSSascha Wildner
10985978408cSSascha Wildner endpwent();
10995978408cSSascha Wildner endgrent();
11005978408cSSascha Wildner
11015978408cSSascha Wildner if (error <= 0 && (errors || warnings)) {
11025978408cSSascha Wildner warnx("%u error(s) and %u warning(s) in mtree manifest",
11035978408cSSascha Wildner errors, warnings);
11045978408cSSascha Wildner if (errors)
11055978408cSSascha Wildner exit(1);
11065978408cSSascha Wildner }
11075978408cSSascha Wildner
11085978408cSSascha Wildner out:
11095978408cSSascha Wildner if (error > 0)
11105978408cSSascha Wildner errc(1, error, "Error reading mtree file");
11115978408cSSascha Wildner
11125978408cSSascha Wildner if (fp != stdin)
11135978408cSSascha Wildner fclose(fp);
11145978408cSSascha Wildner
11155978408cSSascha Wildner if (mtree_root != NULL)
11165978408cSSascha Wildner return (mtree_root);
11175978408cSSascha Wildner
11185978408cSSascha Wildner /* Handle empty specifications. */
11195978408cSSascha Wildner node = create_node(".", S_IFDIR, NULL, &mtree_global);
11205978408cSSascha Wildner node->first = node;
11215978408cSSascha Wildner return (node);
11225978408cSSascha Wildner }
1123