xref: /dflybsd-src/usr.sbin/makefs/mtree.c (revision 32357d2acfd467795886f13d62427207578d24db)
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