1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2011 Marcel Moolenaar
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#if HAVE_NBTOOL_CONFIG_H
29#include "nbtool_config.h"
30#endif
31
32#include <sys/param.h>
33#include <sys/queue.h>
34#include <sys/sbuf.h>
35#include <sys/stat.h>
36#include <sys/types.h>
37#include <assert.h>
38#include <errno.h>
39#include <fcntl.h>
40#include <grp.h>
41#include <inttypes.h>
42#include <pwd.h>
43#include <stdarg.h>
44#include <stdbool.h>
45#include <stddef.h>
46#include <stdio.h>
47#include <stdlib.h>
48#include <string.h>
49#include <strings.h>
50#include <time.h>
51#include <unistd.h>
52#include <util.h>
53#include <vis.h>
54
55#include "makefs.h"
56
57#ifndef ENOATTR
58#define	ENOATTR	ENODATA
59#endif
60
61#define	IS_DOT(nm)	((nm)[0] == '.' && (nm)[1] == '\0')
62#define	IS_DOTDOT(nm)	((nm)[0] == '.' && (nm)[1] == '.' && (nm)[2] == '\0')
63
64struct mtree_fileinfo {
65	SLIST_ENTRY(mtree_fileinfo) next;
66	FILE *fp;
67	const char *name;
68	u_int line;
69};
70
71/* Global state used while parsing. */
72static SLIST_HEAD(, mtree_fileinfo) mtree_fileinfo =
73    SLIST_HEAD_INITIALIZER(mtree_fileinfo);
74static fsnode *mtree_root;
75static fsnode *mtree_current;
76static fsnode mtree_global;
77static fsinode mtree_global_inode;
78static u_int errors, warnings;
79
80static void mtree_error(const char *, ...) __printflike(1, 2);
81static void mtree_warning(const char *, ...) __printflike(1, 2);
82
83static int
84mtree_file_push(const char *name, FILE *fp)
85{
86	struct mtree_fileinfo *fi;
87
88	fi = emalloc(sizeof(*fi));
89	if (strcmp(name, "-") == 0)
90		fi->name = estrdup("(stdin)");
91	else
92		fi->name = estrdup(name);
93	if (fi->name == NULL) {
94		free(fi);
95		return (ENOMEM);
96	}
97
98	fi->fp = fp;
99	fi->line = 0;
100
101	SLIST_INSERT_HEAD(&mtree_fileinfo, fi, next);
102	return (0);
103}
104
105static void
106mtree_print(const char *msgtype, const char *fmt, va_list ap)
107{
108	struct mtree_fileinfo *fi;
109
110	if (msgtype != NULL) {
111		fi = SLIST_FIRST(&mtree_fileinfo);
112		if (fi != NULL)
113			fprintf(stderr, "%s:%u: ", fi->name, fi->line);
114		fprintf(stderr, "%s: ", msgtype);
115	}
116	vfprintf(stderr, fmt, ap);
117}
118
119static void
120mtree_error(const char *fmt, ...)
121{
122	va_list ap;
123
124	va_start(ap, fmt);
125	mtree_print("error", fmt, ap);
126	va_end(ap);
127
128	errors++;
129	fputc('\n', stderr);
130}
131
132static void
133mtree_warning(const char *fmt, ...)
134{
135	va_list ap;
136
137	va_start(ap, fmt);
138	mtree_print("warning", fmt, ap);
139	va_end(ap);
140
141	warnings++;
142	fputc('\n', stderr);
143}
144
145#ifndef MAKEFS_MAX_TREE_DEPTH
146# define MAKEFS_MAX_TREE_DEPTH (MAXPATHLEN/2)
147#endif
148
149/* construct path to node->name */
150static char *
151mtree_file_path(fsnode *node)
152{
153	fsnode *pnode;
154	struct sbuf *sb;
155	char *res, *rp[MAKEFS_MAX_TREE_DEPTH];
156	int depth;
157
158	depth = 0;
159	rp[depth] = node->name;
160	for (pnode = node->parent; pnode && depth < MAKEFS_MAX_TREE_DEPTH - 1;
161	     pnode = pnode->parent) {
162		if (strcmp(pnode->name, ".") == 0)
163			break;
164		rp[++depth] = pnode->name;
165	}
166
167	sb = sbuf_new_auto();
168	if (sb == NULL) {
169		errno = ENOMEM;
170		return (NULL);
171	}
172	while (depth > 0) {
173		sbuf_cat(sb, rp[depth--]);
174		sbuf_putc(sb, '/');
175	}
176	sbuf_cat(sb, rp[depth]);
177	sbuf_finish(sb);
178	res = estrdup(sbuf_data(sb));
179	sbuf_delete(sb);
180	if (res == NULL)
181		errno = ENOMEM;
182	return res;
183
184}
185
186/* mtree_resolve() sets errno to indicate why NULL was returned. */
187static char *
188mtree_resolve(const char *spec, int *istemp)
189{
190	struct sbuf *sb;
191	char *res, *var = NULL;
192	const char *base, *p, *v;
193	size_t len;
194	int c, error, quoted, subst;
195
196	len = strlen(spec);
197	if (len == 0) {
198		errno = EINVAL;
199		return (NULL);
200	}
201
202	c = (len > 1) ? (spec[0] == spec[len - 1]) ? spec[0] : 0 : 0;
203	*istemp = (c == '`') ? 1 : 0;
204	subst = (c == '`' || c == '"') ? 1 : 0;
205	quoted = (subst || c == '\'') ? 1 : 0;
206
207	if (!subst) {
208		res = estrdup(spec + quoted);
209		if (quoted)
210			res[len - 2] = '\0';
211		return (res);
212	}
213
214	sb = sbuf_new_auto();
215	if (sb == NULL) {
216		errno = ENOMEM;
217		return (NULL);
218	}
219
220	base = spec + 1;
221	len -= 2;
222	error = 0;
223	while (len > 0) {
224		p = strchr(base, '$');
225		if (p == NULL) {
226			sbuf_bcat(sb, base, len);
227			base += len;
228			len = 0;
229			continue;
230		}
231		/* The following is safe. spec always starts with a quote. */
232		if (p[-1] == '\\')
233			p--;
234		if (base != p) {
235			sbuf_bcat(sb, base, p - base);
236			len -= p - base;
237			base = p;
238		}
239		if (*p == '\\') {
240			sbuf_putc(sb, '$');
241			base += 2;
242			len -= 2;
243			continue;
244		}
245		/* Skip the '$'. */
246		base++;
247		len--;
248		/* Handle ${X} vs $X. */
249		v = base;
250		if (*base == '{') {
251			p = strchr(v, '}');
252			if (p == NULL)
253				p = v;
254		} else
255			p = v;
256		len -= (p + 1) - base;
257		base = p + 1;
258
259		if (v == p) {
260			sbuf_putc(sb, *v);
261			continue;
262		}
263
264		error = ENOMEM;
265		var = ecalloc(p - v, 1);
266		memcpy(var, v + 1, p - v - 1);
267		if (strcmp(var, ".CURDIR") == 0) {
268			res = getcwd(NULL, 0);
269			if (res == NULL)
270				break;
271		} else if (strcmp(var, ".PROG") == 0) {
272			res = estrdup(getprogname());
273		} else {
274			v = getenv(var);
275			if (v != NULL) {
276				res = estrdup(v);
277			} else
278				res = NULL;
279		}
280		error = 0;
281
282		if (res != NULL) {
283			sbuf_cat(sb, res);
284			free(res);
285		}
286		free(var);
287		var = NULL;
288	}
289
290	free(var);
291	sbuf_finish(sb);
292	res = (error == 0) ? strdup(sbuf_data(sb)) : NULL;
293	sbuf_delete(sb);
294	if (res == NULL)
295		errno = ENOMEM;
296	return (res);
297}
298
299static int
300skip_over(FILE *fp, const char *cs)
301{
302	int c;
303
304	c = getc(fp);
305	while (c != EOF && strchr(cs, c) != NULL)
306		c = getc(fp);
307	if (c != EOF) {
308		ungetc(c, fp);
309		return (0);
310	}
311	return (ferror(fp) ? errno : -1);
312}
313
314static int
315skip_to(FILE *fp, const char *cs)
316{
317	int c;
318
319	c = getc(fp);
320	while (c != EOF && strchr(cs, c) == NULL)
321		c = getc(fp);
322	if (c != EOF) {
323		ungetc(c, fp);
324		return (0);
325	}
326	return (ferror(fp) ? errno : -1);
327}
328
329static int
330read_word(FILE *fp, char *buf, size_t bufsz)
331{
332	struct mtree_fileinfo *fi;
333	size_t idx, qidx;
334	int c, done, error, esc, qlvl;
335
336	if (bufsz == 0)
337		return (EINVAL);
338
339	done = 0;
340	esc = 0;
341	idx = 0;
342	qidx = -1;
343	qlvl = 0;
344	do {
345		c = getc(fp);
346		switch (c) {
347		case EOF:
348			buf[idx] = '\0';
349			error = ferror(fp) ? errno : -1;
350			if (error == -1)
351				mtree_error("unexpected end of file");
352			return (error);
353		case '#':		/* comment -- skip to end of line. */
354			if (!esc) {
355				error = skip_to(fp, "\n");
356				if (!error)
357					continue;
358			}
359			break;
360		case '\\':
361			esc++;
362			break;
363		case '`':
364		case '\'':
365		case '"':
366			if (esc)
367				break;
368			if (qlvl == 0) {
369				qlvl++;
370				qidx = idx;
371			} else if (c == buf[qidx]) {
372				qlvl--;
373				if (qlvl > 0) {
374					do {
375						qidx--;
376					} while (buf[qidx] != '`' &&
377					    buf[qidx] != '\'' &&
378					    buf[qidx] != '"');
379				} else
380					qidx = -1;
381			} else {
382				qlvl++;
383				qidx = idx;
384			}
385			break;
386		case ' ':
387		case '\t':
388		case '\n':
389			if (!esc && qlvl == 0) {
390				ungetc(c, fp);
391				c = '\0';
392				done = 1;
393				break;
394			}
395			if (c == '\n') {
396				/*
397				 * We going to eat the newline ourselves.
398				 */
399				if (qlvl > 0)
400					mtree_warning("quoted word straddles "
401					    "onto next line.");
402				fi = SLIST_FIRST(&mtree_fileinfo);
403				fi->line++;
404			}
405			break;
406		default:
407			if (esc)
408				buf[idx++] = '\\';
409			break;
410		}
411		buf[idx++] = c;
412		esc = 0;
413	} while (idx < bufsz && !done);
414
415	if (idx >= bufsz) {
416		mtree_error("word too long to fit buffer (max %zu characters)",
417		    bufsz);
418		skip_to(fp, " \t\n");
419	}
420	return (0);
421}
422
423static fsnode *
424create_node(const char *name, u_int type, fsnode *parent, fsnode *global)
425{
426	fsnode *n;
427
428	n = ecalloc(1, sizeof(*n));
429	n->name = estrdup(name);
430	n->type = (type == 0) ? global->type : type;
431	n->parent = parent;
432
433	n->inode = ecalloc(1, sizeof(*n->inode));
434
435	/* Assign global options/defaults. */
436	memcpy(n->inode, global->inode, sizeof(*n->inode));
437	n->inode->st.st_mode = (n->inode->st.st_mode & ~S_IFMT) | n->type;
438
439	if (n->type == S_IFLNK)
440		n->symlink = global->symlink;
441	else if (n->type == S_IFREG)
442		n->contents = global->contents;
443
444	return (n);
445}
446
447static void
448destroy_node(fsnode *n)
449{
450
451	assert(n != NULL);
452	assert(n->name != NULL);
453	assert(n->inode != NULL);
454
455	free(n->inode);
456	free(n->name);
457	free(n);
458}
459
460static int
461read_number(const char *tok, u_int base, intmax_t *res, intmax_t min,
462    intmax_t max)
463{
464	char *end;
465	intmax_t val;
466
467	val = strtoimax(tok, &end, base);
468	if (end == tok || end[0] != '\0')
469		return (EINVAL);
470	if (val < min || val > max)
471		return (EDOM);
472	*res = val;
473	return (0);
474}
475
476static int
477read_mtree_keywords(FILE *fp, fsnode *node)
478{
479	char keyword[PATH_MAX];
480	char *name, *p, *value;
481	gid_t gid;
482	uid_t uid;
483	struct stat *st, sb;
484	intmax_t num;
485	u_long flset, flclr;
486	int error, istemp;
487	uint32_t type;
488
489	st = &node->inode->st;
490	do {
491		error = skip_over(fp, " \t");
492		if (error)
493			break;
494
495		error = read_word(fp, keyword, sizeof(keyword));
496		if (error)
497			break;
498
499		if (keyword[0] == '\0')
500			break;
501
502		value = strchr(keyword, '=');
503		if (value != NULL)
504			*value++ = '\0';
505
506		/*
507		 * We use EINVAL, ENOATTR, ENOSYS and ENXIO to signal
508		 * certain conditions:
509		 *   EINVAL -	Value provided for a keyword that does
510		 *		not take a value. The value is ignored.
511		 *   ENOATTR -	Value missing for a keyword that needs
512		 *		a value. The keyword is ignored.
513		 *   ENOSYS -	Unsupported keyword encountered. The
514		 *		keyword is ignored.
515		 *   ENXIO -	Value provided for a keyword that does
516		 *		not take a value. The value is ignored.
517		 */
518		switch (keyword[0]) {
519		case 'c':
520			if (strcmp(keyword, "contents") == 0) {
521				if (value == NULL) {
522					error = ENOATTR;
523					break;
524				}
525				node->contents = estrdup(value);
526			} else
527				error = ENOSYS;
528			break;
529		case 'f':
530			if (strcmp(keyword, "flags") == 0) {
531				if (value == NULL) {
532					error = ENOATTR;
533					break;
534				}
535				flset = flclr = 0;
536#if HAVE_STRUCT_STAT_ST_FLAGS
537				if (!strtofflags(&value, &flset, &flclr)) {
538					st->st_flags &= ~flclr;
539					st->st_flags |= flset;
540				} else
541					error = errno;
542#endif
543			} else
544				error = ENOSYS;
545			break;
546		case 'g':
547			if (strcmp(keyword, "gid") == 0) {
548				if (value == NULL) {
549					error = ENOATTR;
550					break;
551				}
552				error = read_number(value, 10, &num,
553				    0, UINT_MAX);
554				if (!error)
555					st->st_gid = num;
556			} else if (strcmp(keyword, "gname") == 0) {
557				if (value == NULL) {
558					error = ENOATTR;
559					break;
560				}
561				if (gid_from_group(value, &gid) == 0)
562					st->st_gid = gid;
563				else
564					error = EINVAL;
565			} else
566				error = ENOSYS;
567			break;
568		case 'l':
569			if (strcmp(keyword, "link") == 0) {
570				if (value == NULL) {
571					error = ENOATTR;
572					break;
573				}
574				node->symlink = emalloc(strlen(value) + 1);
575				if (node->symlink == NULL) {
576					error = errno;
577					break;
578				}
579				if (strunvis(node->symlink, value) < 0) {
580					error = errno;
581					break;
582				}
583			} else
584				error = ENOSYS;
585			break;
586		case 'm':
587			if (strcmp(keyword, "mode") == 0) {
588				if (value == NULL) {
589					error = ENOATTR;
590					break;
591				}
592				if (value[0] >= '0' && value[0] <= '9') {
593					error = read_number(value, 8, &num,
594					    0, 07777);
595					if (!error) {
596						st->st_mode &= S_IFMT;
597						st->st_mode |= num;
598					}
599				} else {
600					/* Symbolic mode not supported. */
601					error = EINVAL;
602					break;
603				}
604			} else
605				error = ENOSYS;
606			break;
607		case 'o':
608			if (strcmp(keyword, "optional") == 0) {
609				if (value != NULL)
610					error = ENXIO;
611				node->flags |= FSNODE_F_OPTIONAL;
612			} else
613				error = ENOSYS;
614			break;
615		case 's':
616			if (strcmp(keyword, "size") == 0) {
617				if (value == NULL) {
618					error = ENOATTR;
619					break;
620				}
621				error = read_number(value, 10, &num,
622				    0, INTMAX_MAX);
623				if (!error)
624					st->st_size = num;
625			} else
626				error = ENOSYS;
627			break;
628		case 't':
629			if (strcmp(keyword, "tags") == 0) {
630				if (value == NULL) {
631					error = ENOATTR;
632					break;
633				}
634				/* Ignore. */
635			} else if (strcmp(keyword, "time") == 0) {
636				if (value == NULL) {
637					error = ENOATTR;
638					break;
639				}
640				p = strchr(value, '.');
641				if (p != NULL)
642					*p++ = '\0';
643				error = read_number(value, 10, &num, 0,
644				    INTMAX_MAX);
645				if (error)
646					break;
647				st->st_atime = num;
648				st->st_ctime = num;
649				st->st_mtime = num;
650#if HAVE_STRUCT_STAT_ST_MTIMENSEC
651				if (p == NULL)
652					break;
653				error = read_number(p, 10, &num, 0,
654				    INTMAX_MAX);
655				if (error)
656					break;
657				st->st_atimensec = num;
658				st->st_ctimensec = num;
659				st->st_mtimensec = num;
660#endif
661			} else if (strcmp(keyword, "type") == 0) {
662				if (value == NULL) {
663					error = ENOATTR;
664					break;
665				}
666				if (strcmp(value, "dir") == 0)
667					node->type = S_IFDIR;
668				else if (strcmp(value, "file") == 0)
669					node->type = S_IFREG;
670				else if (strcmp(value, "link") == 0)
671					node->type = S_IFLNK;
672				else
673					error = EINVAL;
674			} else
675				error = ENOSYS;
676			break;
677		case 'u':
678			if (strcmp(keyword, "uid") == 0) {
679				if (value == NULL) {
680					error = ENOATTR;
681					break;
682				}
683				error = read_number(value, 10, &num,
684				    0, UINT_MAX);
685				if (!error)
686					st->st_uid = num;
687			} else if (strcmp(keyword, "uname") == 0) {
688				if (value == NULL) {
689					error = ENOATTR;
690					break;
691				}
692				if (uid_from_user(value, &uid) == 0)
693					st->st_uid = uid;
694				else
695					error = EINVAL;
696			} else
697				error = ENOSYS;
698			break;
699		default:
700			error = ENOSYS;
701			break;
702		}
703
704		switch (error) {
705		case EINVAL:
706			mtree_error("%s: invalid value '%s'", keyword, value);
707			break;
708		case ENOATTR:
709			mtree_error("%s: keyword needs a value", keyword);
710			break;
711		case ENOSYS:
712			mtree_warning("%s: unsupported keyword", keyword);
713			break;
714		case ENXIO:
715			mtree_error("%s: keyword does not take a value",
716			    keyword);
717			break;
718		}
719	} while (1);
720
721	if (error)
722		return (error);
723
724	st->st_mode = (st->st_mode & ~S_IFMT) | node->type;
725
726	/* Nothing more to do for the global defaults. */
727	if (node->name == NULL)
728		return (0);
729
730	/*
731	 * Be intelligent about the file type.
732	 */
733	if (node->contents != NULL) {
734		if (node->symlink != NULL) {
735			mtree_error("%s: both link and contents keywords "
736			    "defined", node->name);
737			return (0);
738		}
739		type = S_IFREG;
740	} else if (node->type != 0) {
741		type = node->type;
742		if (type == S_IFLNK && node->symlink == NULL) {
743			mtree_error("%s: link type requires link keyword", node->name);
744			return (0);
745		} else if (type == S_IFREG) {
746			/* the named path is the default contents */
747			node->contents = mtree_file_path(node);
748		}
749	} else
750		type = (node->symlink != NULL) ? S_IFLNK : S_IFDIR;
751
752	if (node->type == 0)
753		node->type = type;
754
755	if (node->type != type) {
756		mtree_error("%s: file type and defined keywords to not match",
757		    node->name);
758		return (0);
759	}
760
761	st->st_mode = (st->st_mode & ~S_IFMT) | node->type;
762
763	if (node->contents == NULL)
764		return (0);
765
766	name = mtree_resolve(node->contents, &istemp);
767	if (name == NULL)
768		return (errno);
769
770	if (stat(name, &sb) != 0) {
771		mtree_error("%s: contents file '%s' not found", node->name,
772		    name);
773		free(name);
774		return (0);
775	}
776
777	/*
778         * Check for hardlinks. If the contents key is used, then the check
779         * will only trigger if the contents file is a link even if it is used
780         * by more than one file
781	 */
782	if (sb.st_nlink > 1) {
783		fsinode *curino;
784
785		st->st_ino = sb.st_ino;
786		st->st_dev = sb.st_dev;
787		curino = link_check(node->inode);
788		if (curino != NULL) {
789			free(node->inode);
790			node->inode = curino;
791			node->inode->nlink++;
792			/* Reset st since node->inode has been updated. */
793			st = &node->inode->st;
794		}
795	}
796
797	free(node->contents);
798	node->contents = name;
799	st->st_size = sb.st_size;
800	return (0);
801}
802
803static int
804read_mtree_command(FILE *fp)
805{
806	char cmd[10];
807	int error;
808
809	error = read_word(fp, cmd, sizeof(cmd));
810	if (error)
811		goto out;
812
813	error = read_mtree_keywords(fp, &mtree_global);
814
815 out:
816	skip_to(fp, "\n");
817	(void)getc(fp);
818	return (error);
819}
820
821static int
822read_mtree_spec1(FILE *fp, bool def, const char *name)
823{
824	fsnode *last, *node, *parent;
825	u_int type;
826	int error;
827
828	assert(name[0] != '\0');
829
830	/*
831	 * Treat '..' specially, because it only changes our current
832	 * directory. We don't create a node for it. We simply ignore
833	 * any keywords that may appear on the line as well.
834	 * Going up a directory is a little non-obvious. A directory
835	 * node has a corresponding '.' child. The parent of '.' is
836	 * not the '.' node of the parent directory, but the directory
837	 * node within the parent to which the child relates. However,
838	 * going up a directory means we need to find the '.' node to
839	 * which the directory node is linked.  This we can do via the
840	 * first * pointer, because '.' is always the first entry in a
841	 * directory.
842	 */
843	if (IS_DOTDOT(name)) {
844		/* This deals with NULL pointers as well. */
845		if (mtree_current == mtree_root) {
846			mtree_warning("ignoring .. in root directory");
847			return (0);
848		}
849
850		node = mtree_current;
851
852		assert(node != NULL);
853		assert(IS_DOT(node->name));
854		assert(node->first == node);
855
856		/* Get the corresponding directory node in the parent. */
857		node = mtree_current->parent;
858
859		assert(node != NULL);
860		assert(!IS_DOT(node->name));
861
862		node = node->first;
863
864		assert(node != NULL);
865		assert(IS_DOT(node->name));
866		assert(node->first == node);
867
868		mtree_current = node;
869		return (0);
870	}
871
872	/*
873	 * If we don't have a current directory and the first specification
874	 * (either implicit or defined) is not '.', then we need to create
875	 * a '.' node first (using a recursive call).
876	 */
877	if (!IS_DOT(name) && mtree_current == NULL) {
878		error = read_mtree_spec1(fp, false, ".");
879		if (error)
880			return (error);
881	}
882
883	/*
884	 * Lookup the name in the current directory (if we have a current
885	 * directory) to make sure we do not create multiple nodes for the
886	 * same component. For non-definitions, if we find a node with the
887	 * same name, simply change the current directory. For definitions
888	 * more happens.
889	 */
890	last = NULL;
891	node = mtree_current;
892	while (node != NULL) {
893		assert(node->first == mtree_current);
894
895		if (strcmp(name, node->name) == 0) {
896			if (def == true) {
897				if (dupsok == 0)
898					mtree_error(
899					    "duplicate definition of %s",
900					    name);
901				else if (dupsok == 1)
902					mtree_warning(
903					    "duplicate definition of %s",
904					    name);
905				return (0);
906			}
907
908			if (node->type != S_IFDIR) {
909				mtree_error("%s is not a directory", name);
910				return (0);
911			}
912
913			assert(!IS_DOT(name));
914
915			node = node->child;
916
917			assert(node != NULL);
918			assert(IS_DOT(node->name));
919
920			mtree_current = node;
921			return (0);
922		}
923
924		last = node;
925		node = last->next;
926	}
927
928	parent = (mtree_current != NULL) ? mtree_current->parent : NULL;
929	type = (def == false || IS_DOT(name)) ? S_IFDIR : 0;
930	node = create_node(name, type, parent, &mtree_global);
931	if (node == NULL)
932		return (ENOMEM);
933
934	if (def == true) {
935		error = read_mtree_keywords(fp, node);
936		if (error) {
937			destroy_node(node);
938			return (error);
939		}
940	}
941
942	node->first = (mtree_current != NULL) ? mtree_current : node;
943
944	if (last != NULL)
945		last->next = node;
946
947	if (node->type != S_IFDIR)
948		return (0);
949
950	if (!IS_DOT(node->name)) {
951		parent = node;
952		node = create_node(".", S_IFDIR, parent, parent);
953		if (node == NULL) {
954			last->next = NULL;
955			destroy_node(parent);
956			return (ENOMEM);
957		}
958		parent->child = node;
959		node->first = node;
960	}
961
962	assert(node != NULL);
963	assert(IS_DOT(node->name));
964	assert(node->first == node);
965
966	mtree_current = node;
967	if (mtree_root == NULL)
968		mtree_root = node;
969
970	return (0);
971}
972
973static int
974read_mtree_spec(FILE *fp)
975{
976	char pathspec[PATH_MAX], pathtmp[4*PATH_MAX + 1];
977	char *cp;
978	int error;
979
980	error = read_word(fp, pathtmp, sizeof(pathtmp));
981	if (error)
982		goto out;
983	if (strnunvis(pathspec, PATH_MAX, pathtmp) == -1) {
984		error = errno;
985		goto out;
986	}
987	error = 0;
988
989	cp = strchr(pathspec, '/');
990	if (cp != NULL) {
991		/* Absolute pathname */
992		mtree_current = mtree_root;
993
994		do {
995			*cp++ = '\0';
996
997			/* Disallow '..' as a component. */
998			if (IS_DOTDOT(pathspec)) {
999				mtree_error("absolute path cannot contain "
1000				    ".. component");
1001				goto out;
1002			}
1003
1004			/* Ignore multiple adjacent slashes and '.'. */
1005			if (pathspec[0] != '\0' && !IS_DOT(pathspec))
1006				error = read_mtree_spec1(fp, false, pathspec);
1007			memmove(pathspec, cp, strlen(cp) + 1);
1008			cp = strchr(pathspec, '/');
1009		} while (!error && cp != NULL);
1010
1011		/* Disallow '.' and '..' as the last component. */
1012		if (!error && (IS_DOT(pathspec) || IS_DOTDOT(pathspec))) {
1013			mtree_error("absolute path cannot contain . or .. "
1014			    "components");
1015			goto out;
1016		}
1017	}
1018
1019	/* Ignore absolute specifications that end with a slash. */
1020	if (!error && pathspec[0] != '\0')
1021		error = read_mtree_spec1(fp, true, pathspec);
1022
1023 out:
1024	skip_to(fp, "\n");
1025	(void)getc(fp);
1026	return (error);
1027}
1028
1029fsnode *
1030read_mtree(const char *fname, fsnode *node)
1031{
1032	struct mtree_fileinfo *fi;
1033	FILE *fp;
1034	int c, error;
1035
1036	/* We do not yet support nesting... */
1037	assert(node == NULL);
1038
1039	if (strcmp(fname, "-") == 0)
1040		fp = stdin;
1041	else {
1042		fp = fopen(fname, "r");
1043		if (fp == NULL)
1044			err(1, "Can't open `%s'", fname);
1045	}
1046
1047	error = mtree_file_push(fname, fp);
1048	if (error)
1049		goto out;
1050
1051	memset(&mtree_global, 0, sizeof(mtree_global));
1052	memset(&mtree_global_inode, 0, sizeof(mtree_global_inode));
1053	mtree_global.inode = &mtree_global_inode;
1054	mtree_global_inode.nlink = 1;
1055	mtree_global_inode.st.st_nlink = 1;
1056	mtree_global_inode.st.st_atime = mtree_global_inode.st.st_ctime =
1057	    mtree_global_inode.st.st_mtime = time(NULL);
1058	errors = warnings = 0;
1059
1060	setgroupent(1);
1061	setpassent(1);
1062
1063	mtree_root = node;
1064	mtree_current = node;
1065	do {
1066		/* Start of a new line... */
1067		fi = SLIST_FIRST(&mtree_fileinfo);
1068		fi->line++;
1069
1070		error = skip_over(fp, " \t");
1071		if (error)
1072			break;
1073
1074		c = getc(fp);
1075		if (c == EOF) {
1076			error = ferror(fp) ? errno : -1;
1077			break;
1078		}
1079
1080		switch (c) {
1081		case '\n':		/* empty line */
1082			error = 0;
1083			break;
1084		case '#':		/* comment -- skip to end of line. */
1085			error = skip_to(fp, "\n");
1086			if (!error)
1087				(void)getc(fp);
1088			break;
1089		case '/':		/* special commands */
1090			error = read_mtree_command(fp);
1091			break;
1092		default:		/* specification */
1093			ungetc(c, fp);
1094			error = read_mtree_spec(fp);
1095			break;
1096		}
1097	} while (!error);
1098
1099	endpwent();
1100	endgrent();
1101
1102	if (error <= 0 && (errors || warnings)) {
1103		warnx("%u error(s) and %u warning(s) in mtree manifest",
1104		    errors, warnings);
1105		if (errors)
1106			exit(1);
1107	}
1108
1109 out:
1110	if (error > 0)
1111		errc(1, error, "Error reading mtree file");
1112
1113	if (fp != stdin)
1114		fclose(fp);
1115
1116	if (mtree_root != NULL)
1117		return (mtree_root);
1118
1119	/* Handle empty specifications. */
1120	node = create_node(".", S_IFDIR, NULL, &mtree_global);
1121	node->first = node;
1122	return (node);
1123}
1124