spec.c revision 187910
1276789Sdim/*-
2276789Sdim * Copyright (c) 1989, 1993
3353358Sdim *	The Regents of the University of California.  All rights reserved.
4353358Sdim *
5353358Sdim * Redistribution and use in source and binary forms, with or without
6276789Sdim * modification, are permitted provided that the following conditions
7276789Sdim * are met:
8276789Sdim * 1. Redistributions of source code must retain the above copyright
9276789Sdim *    notice, this list of conditions and the following disclaimer.
10276789Sdim * 2. Redistributions in binary form must reproduce the above copyright
11276789Sdim *    notice, this list of conditions and the following disclaimer in the
12276789Sdim *    documentation and/or other materials provided with the distribution.
13276789Sdim * 3. Neither the name of the University nor the names of its contributors
14276789Sdim *    may be used to endorse or promote products derived from this software
15296417Sdim *    without specific prior written permission.
16276789Sdim *
17276789Sdim * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18276789Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19276789Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20276789Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21276789Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22276789Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23276789Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24276789Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25276789Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26276789Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27276789Sdim * SUCH DAMAGE.
28276789Sdim */
29276789Sdim
30276789Sdim#if 0
31276789Sdim#ifndef lint
32276789Sdimstatic char sccsid[] = "@(#)spec.c	8.1 (Berkeley) 6/6/93";
33276789Sdim#endif /* not lint */
34276789Sdim#endif
35276789Sdim#include <sys/cdefs.h>
36276789Sdim__FBSDID("$FreeBSD: head/usr.sbin/mtree/spec.c 187910 2009-01-30 05:49:27Z kientzle $");
37276789Sdim
38276789Sdim#include <sys/types.h>
39276789Sdim#include <sys/stat.h>
40276789Sdim#include <ctype.h>
41276789Sdim#include <err.h>
42276789Sdim#include <errno.h>
43276789Sdim#include <fts.h>
44276789Sdim#include <grp.h>
45276789Sdim#include <pwd.h>
46276789Sdim#include <stdio.h>
47276789Sdim#include <unistd.h>
48276789Sdim#include <vis.h>
49276789Sdim#include "mtree.h"
50276789Sdim#include "extern.h"
51276789Sdim
52276789Sdimint lineno;				/* Current spec line number. */
53276789Sdim
54276789Sdimstatic void	 set(char *, NODE *);
55276789Sdimstatic void	 unset(char *, NODE *);
56276789Sdim
57276789SdimNODE *
58276789Sdimmtree_readspec(FILE *fi)
59276789Sdim{
60276789Sdim	NODE *centry, *last;
61276789Sdim	char *p;
62276789Sdim	NODE ginfo, *root;
63276789Sdim	int c_cur, c_next;
64276789Sdim	char buf[2048];
65276789Sdim
66276789Sdim	centry = last = root = NULL;
67276789Sdim	bzero(&ginfo, sizeof(ginfo));
68276789Sdim	c_cur = c_next = 0;
69276789Sdim	for (lineno = 1; fgets(buf, sizeof(buf), fi);
70276789Sdim	    ++lineno, c_cur = c_next, c_next = 0) {
71276789Sdim		/* Skip empty lines. */
72276789Sdim		if (buf[0] == '\n')
73276789Sdim			continue;
74276789Sdim
75276789Sdim		/* Find end of line. */
76276789Sdim		if ((p = index(buf, '\n')) == NULL)
77276789Sdim			errx(1, "line %d too long", lineno);
78276789Sdim
79276789Sdim		/* See if next line is continuation line. */
80276789Sdim		if (p[-1] == '\\') {
81276789Sdim			--p;
82276789Sdim			c_next = 1;
83276789Sdim		}
84276789Sdim
85276789Sdim		/* Null-terminate the line. */
86276789Sdim		*p = '\0';
87276789Sdim
88276789Sdim		/* Skip leading whitespace. */
89276789Sdim		for (p = buf; *p && isspace(*p); ++p);
90276789Sdim
91276789Sdim		/* If nothing but whitespace or comment char, continue. */
92276789Sdim		if (!*p || *p == '#')
93276789Sdim			continue;
94276789Sdim
95276789Sdim#ifdef DEBUG
96276789Sdim		(void)fprintf(stderr, "line %d: {%s}\n", lineno, p);
97276789Sdim#endif
98276789Sdim		if (c_cur) {
99276789Sdim			set(p, centry);
100276789Sdim			continue;
101276789Sdim		}
102276789Sdim
103276789Sdim		/* Grab file name, "$", "set", or "unset". */
104276789Sdim		if ((p = strtok(p, "\n\t ")) == NULL)
105276789Sdim			errx(1, "line %d: missing field", lineno);
106276789Sdim
107276789Sdim		if (p[0] == '/')
108276789Sdim			switch(p[1]) {
109276789Sdim			case 's':
110276789Sdim				if (strcmp(p + 1, "set"))
111276789Sdim					break;
112276789Sdim				set(NULL, &ginfo);
113276789Sdim				continue;
114276789Sdim			case 'u':
115276789Sdim				if (strcmp(p + 1, "unset"))
116276789Sdim					break;
117276789Sdim				unset(NULL, &ginfo);
118276789Sdim				continue;
119276789Sdim			}
120276789Sdim
121276789Sdim		if (index(p, '/'))
122276789Sdim			errx(1, "line %d: slash character in file name",
123276789Sdim			lineno);
124276789Sdim
125276789Sdim		if (!strcmp(p, "..")) {
126276789Sdim			/* Don't go up, if haven't gone down. */
127276789Sdim			if (!root)
128276789Sdim				goto noparent;
129276789Sdim			if (last->type != F_DIR || last->flags & F_DONE) {
130276789Sdim				if (last == root)
131276789Sdim					goto noparent;
132276789Sdim				last = last->parent;
133276789Sdim			}
134276789Sdim			last->flags |= F_DONE;
135276789Sdim			continue;
136276789Sdim
137276789Sdimnoparent:		errx(1, "line %d: no parent node", lineno);
138276789Sdim		}
139276789Sdim
140276789Sdim		if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL)
141276789Sdim			errx(1, "calloc");
142276789Sdim		*centry = ginfo;
143276789Sdim#define	MAGIC	"?*["
144276789Sdim		if (strpbrk(p, MAGIC))
145276789Sdim			centry->flags |= F_MAGIC;
146276789Sdim		if (strunvis(centry->name, p) == -1)
147276789Sdim			errx(1, "filename %s is ill-encoded", p);
148276789Sdim		set(NULL, centry);
149276789Sdim
150276789Sdim		if (!root) {
151276789Sdim			last = root = centry;
152276789Sdim			root->parent = root;
153276789Sdim		} else if (last->type == F_DIR && !(last->flags & F_DONE)) {
154276789Sdim			centry->parent = last;
155276789Sdim			last = last->child = centry;
156276789Sdim		} else {
157276789Sdim			centry->parent = last->parent;
158276789Sdim			centry->prev = last;
159276789Sdim			last = last->next = centry;
160276789Sdim		}
161276789Sdim	}
162276789Sdim	return (root);
163276789Sdim}
164276789Sdim
165276789Sdimstatic void
166276789Sdimset(char *t, NODE *ip)
167276789Sdim{
168276789Sdim	int type;
169276789Sdim	char *kw, *val = NULL;
170276789Sdim	struct group *gr;
171276789Sdim	struct passwd *pw;
172276789Sdim	mode_t *m;
173276789Sdim	int value;
174276789Sdim	char *ep;
175276789Sdim
176276789Sdim	for (; (kw = strtok(t, "= \t\n")); t = NULL) {
177276789Sdim		ip->flags |= type = parsekey(kw, &value);
178276789Sdim		if (value && (val = strtok(NULL, " \t\n")) == NULL)
179276789Sdim			errx(1, "line %d: missing value", lineno);
180276789Sdim		switch(type) {
181276789Sdim		case F_CKSUM:
182276789Sdim			ip->cksum = strtoul(val, &ep, 10);
183276789Sdim			if (*ep)
184276789Sdim				errx(1, "line %d: invalid checksum %s",
185276789Sdim				lineno, val);
186276789Sdim			break;
187276789Sdim		case F_MD5:
188276789Sdim			ip->md5digest = strdup(val);
189276789Sdim			if(!ip->md5digest)
190276789Sdim				errx(1, "strdup");
191276789Sdim			break;
192276789Sdim		case F_SHA1:
193276789Sdim			ip->sha1digest = strdup(val);
194296417Sdim			if(!ip->sha1digest)
195276789Sdim				errx(1, "strdup");
196276789Sdim			break;
197276789Sdim		case F_SHA256:
198276789Sdim			ip->sha256digest = strdup(val);
199276789Sdim			if(!ip->sha256digest)
200276789Sdim				errx(1, "strdup");
201276789Sdim			break;
202276789Sdim		case F_RMD160:
203276789Sdim			ip->rmd160digest = strdup(val);
204276789Sdim			if(!ip->rmd160digest)
205276789Sdim				errx(1, "strdup");
206276789Sdim			break;
207276789Sdim		case F_FLAGS:
208276789Sdim			if (strcmp("none", val) == 0)
209296417Sdim				ip->st_flags = 0;
210276789Sdim			else if (strtofflags(&val, &ip->st_flags, NULL) != 0)
211276789Sdim				errx(1, "line %d: invalid flag %s",lineno, val);
212276789Sdim 			break;
213276789Sdim		case F_GID:
214276789Sdim			ip->st_gid = strtoul(val, &ep, 10);
215276789Sdim			if (*ep)
216276789Sdim				errx(1, "line %d: invalid gid %s", lineno, val);
217276789Sdim			break;
218276789Sdim		case F_GNAME:
219276789Sdim			if ((gr = getgrnam(val)) == NULL)
220276789Sdim			    errx(1, "line %d: unknown group %s", lineno, val);
221276789Sdim			ip->st_gid = gr->gr_gid;
222276789Sdim			break;
223276789Sdim		case F_IGN:
224276789Sdim			/* just set flag bit */
225276789Sdim			break;
226276789Sdim		case F_MODE:
227276789Sdim			if ((m = setmode(val)) == NULL)
228276789Sdim				errx(1, "line %d: invalid file mode %s",
229296417Sdim				lineno, val);
230296417Sdim			ip->st_mode = getmode(m, 0);
231276789Sdim			free(m);
232276789Sdim			break;
233276789Sdim		case F_NLINK:
234276789Sdim			ip->st_nlink = strtoul(val, &ep, 10);
235276789Sdim			if (*ep)
236276789Sdim				errx(1, "line %d: invalid link count %s",
237276789Sdim				lineno,  val);
238276789Sdim			break;
239276789Sdim		case F_OPT:
240276789Sdim			/* just set flag bit */
241276789Sdim			break;
242276789Sdim		case F_SIZE:
243276789Sdim			ip->st_size = strtoq(val, &ep, 10);
244276789Sdim			if (*ep)
245276789Sdim				errx(1, "line %d: invalid size %s",
246276789Sdim				lineno, val);
247276789Sdim			break;
248276789Sdim		case F_SLINK:
249276789Sdim			ip->slink = malloc(strlen(val) + 1);
250276789Sdim			if (ip->slink == NULL)
251276789Sdim				errx(1, "malloc");
252276789Sdim			if (strunvis(ip->slink, val) == -1)
253276789Sdim				errx(1, "symlink %s is ill-encoded", val);
254296417Sdim			break;
255276789Sdim		case F_TIME:
256276789Sdim			ip->st_mtimespec.tv_sec = strtoul(val, &ep, 10);
257276789Sdim			if (*ep == '.') {
258276789Sdim				val = ep + 1;
259276789Sdim				ip->st_mtimespec.tv_nsec
260276789Sdim				    = strtoul(val, &ep, 10);
261276789Sdim			} else
262276789Sdim				ip->st_mtimespec.tv_nsec = 0;
263276789Sdim			if (*ep)
264276789Sdim				errx(1, "line %d: invalid time %s",
265276789Sdim				    lineno, val);
266276789Sdim			break;
267276789Sdim		case F_TYPE:
268276789Sdim			switch(*val) {
269276789Sdim			case 'b':
270276789Sdim				if (!strcmp(val, "block"))
271276789Sdim					ip->type = F_BLOCK;
272276789Sdim				break;
273276789Sdim			case 'c':
274276789Sdim				if (!strcmp(val, "char"))
275276789Sdim					ip->type = F_CHAR;
276276789Sdim				break;
277276789Sdim			case 'd':
278276789Sdim				if (!strcmp(val, "dir"))
279276789Sdim					ip->type = F_DIR;
280276789Sdim				break;
281276789Sdim			case 'f':
282276789Sdim				if (!strcmp(val, "file"))
283276789Sdim					ip->type = F_FILE;
284276789Sdim				if (!strcmp(val, "fifo"))
285276789Sdim					ip->type = F_FIFO;
286276789Sdim				break;
287276789Sdim			case 'l':
288276789Sdim				if (!strcmp(val, "link"))
289276789Sdim					ip->type = F_LINK;
290276789Sdim				break;
291276789Sdim			case 's':
292276789Sdim				if (!strcmp(val, "socket"))
293276789Sdim					ip->type = F_SOCK;
294276789Sdim				break;
295276789Sdim			default:
296276789Sdim				errx(1, "line %d: unknown file type %s",
297276789Sdim				lineno, val);
298276789Sdim			}
299276789Sdim			break;
300276789Sdim		case F_UID:
301276789Sdim			ip->st_uid = strtoul(val, &ep, 10);
302276789Sdim			if (*ep)
303276789Sdim				errx(1, "line %d: invalid uid %s", lineno, val);
304276789Sdim			break;
305276789Sdim		case F_UNAME:
306276789Sdim			if ((pw = getpwnam(val)) == NULL)
307276789Sdim			    errx(1, "line %d: unknown user %s", lineno, val);
308276789Sdim			ip->st_uid = pw->pw_uid;
309276789Sdim			break;
310276789Sdim		}
311276789Sdim	}
312276789Sdim}
313276789Sdim
314276789Sdimstatic void
315276789Sdimunset(char *t, NODE *ip)
316276789Sdim{
317276789Sdim	char *p;
318276789Sdim
319276789Sdim	while ((p = strtok(t, "\n\t ")))
320276789Sdim		ip->flags &= ~parsekey(p, NULL);
321276789Sdim}
322276789Sdim