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