1/*-
2 * Copyright (c) 1989, 1993
3 *	The Regents of the University of California.  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 * 3. Neither the name of the University nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#if 0
31#ifndef lint
32static char sccsid[] = "@(#)spec.c	8.1 (Berkeley) 6/6/93";
33#endif /* not lint */
34#endif
35#include <sys/cdefs.h>
36__FBSDID("$FreeBSD: src/usr.sbin/mtree/spec.c,v 1.22 2005/03/29 11:44:17 tobez Exp $");
37
38#include <sys/types.h>
39#include <sys/stat.h>
40#include <ctype.h>
41#include <err.h>
42#include <errno.h>
43#include <fts.h>
44#include <grp.h>
45#include <pwd.h>
46#include <stdio.h>
47#include <stdint.h>
48#include <unistd.h>
49#include <vis.h>
50#include "mtree.h"
51#include "extern.h"
52
53int lineno;				/* Current spec line number. */
54
55static void	 set(char *, NODE *);
56static void	 unset(char *, NODE *);
57
58NODE *
59mtree_readspec(FILE *fi)
60{
61	NODE *centry, *last;
62	char *p;
63	NODE ginfo, *root;
64	int c_cur, c_next;
65	char buf[2048];
66
67	centry = last = root = NULL;
68	bzero(&ginfo, sizeof(ginfo));
69	c_cur = c_next = 0;
70	for (lineno = 1; fgets(buf, sizeof(buf), fi);
71	    ++lineno, c_cur = c_next, c_next = 0) {
72		/* Skip empty lines. */
73		if (buf[0] == '\n')
74			continue;
75
76		/* Find end of line. */
77		if ((p = index(buf, '\n')) == NULL)
78			errx(1, "line %d too long", lineno);
79
80		/* See if next line is continuation line. */
81		if (p[-1] == '\\') {
82			--p;
83			c_next = 1;
84		}
85
86		/* Null-terminate the line. */
87		*p = '\0';
88
89		/* Skip leading whitespace. */
90		for (p = buf; *p && isspace(*p); ++p);
91
92		/* If nothing but whitespace or comment char, continue. */
93		if (!*p || *p == '#')
94			continue;
95
96#ifdef DEBUG
97		(void)fprintf(stderr, "line %d: {%s}\n", lineno, p);
98#endif
99		if (c_cur) {
100			set(p, centry);
101			continue;
102		}
103
104		/* Grab file name, "$", "set", or "unset". */
105		if ((p = strtok(p, "\n\t ")) == NULL)
106			errx(1, "line %d: missing field", lineno);
107
108		if (p[0] == '/')
109			switch(p[1]) {
110			case 's':
111				if (strcmp(p + 1, "set"))
112					break;
113				set(NULL, &ginfo);
114				continue;
115			case 'u':
116				if (strcmp(p + 1, "unset"))
117					break;
118				unset(NULL, &ginfo);
119				continue;
120			}
121
122		if (index(p, '/'))
123			errx(1, "line %d: slash character in file name",
124			lineno);
125
126		if (!strcmp(p, "..")) {
127			/* Don't go up, if haven't gone down. */
128			if (!root)
129				goto noparent;
130			if (last->type != F_DIR || last->flags & F_DONE) {
131				if (last == root)
132					goto noparent;
133				last = last->parent;
134			}
135			last->flags |= F_DONE;
136			continue;
137
138noparent:		errx(1, "line %d: no parent node", lineno);
139		}
140
141		if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL)
142			errx(1, "calloc");
143		*centry = ginfo;
144#define	MAGIC	"?*["
145		if (strpbrk(p, MAGIC))
146			centry->flags |= F_MAGIC;
147		if (strunvis(centry->name, p) == -1)
148			errx(1, "filename %s is ill-encoded", p);
149		set(NULL, centry);
150
151		if (!root) {
152			last = root = centry;
153			root->parent = root;
154		} else if (last->type == F_DIR && !(last->flags & F_DONE)) {
155			centry->parent = last;
156			last = last->child = centry;
157		} else {
158			centry->parent = last->parent;
159			centry->prev = last;
160			last = last->next = centry;
161		}
162	}
163	return (root);
164}
165
166static void
167set(char *t, NODE *ip)
168{
169	int type;
170	char *kw, *val = NULL;
171	struct group *gr;
172	struct passwd *pw;
173	mode_t *m;
174	int value;
175	char *ep;
176
177	for (; (kw = strtok(t, "= \t\n")); t = NULL) {
178		ip->flags |= type = parsekey(kw, &value);
179		if ((value == 0) || (val = strtok(NULL, " \t\n")) == NULL)
180			errx(1, "line %d: missing value", lineno);
181		switch(type) {
182		case F_CKSUM:
183			ip->cksum = strtoul(val, &ep, 10);
184			if (*ep)
185				errx(1, "line %d: invalid checksum %s",
186				lineno, val);
187			break;
188		case F_MD5:
189			ip->md5digest = strdup(val);
190			if(!ip->md5digest)
191				errx(1, "strdup");
192			break;
193		case F_SHA1:
194			ip->sha1digest = strdup(val);
195			if(!ip->sha1digest)
196				errx(1, "strdup");
197			break;
198		case F_SHA256:
199			ip->sha256digest = strdup(val);
200			if(!ip->sha256digest)
201				errx(1, "strdup");
202			break;
203		case F_RMD160:
204			ip->rmd160digest = strdup(val);
205			if(!ip->rmd160digest)
206				errx(1, "strdup");
207			break;
208		case F_FLAGS:
209			if (strcmp("none", val) == 0)
210				ip->st_flags = 0;
211			else if (strtofflags(&val, &ip->st_flags, NULL) != 0)
212				errx(1, "line %d: invalid flag %s",lineno, val);
213 			break;
214		case F_GID:
215			ip->st_gid = (gid_t)strtoul(val, &ep, 10);
216			if (*ep)
217				errx(1, "line %d: invalid gid %s", lineno, val);
218			break;
219		case F_GNAME:
220			if ((gr = getgrnam(val)) == NULL)
221			    errx(1, "line %d: unknown group %s", lineno, val);
222			ip->st_gid = gr->gr_gid;
223			break;
224		case F_IGN:
225			/* just set flag bit */
226			break;
227		case F_MODE:
228			if ((m = setmode(val)) == NULL)
229				errx(1, "line %d: invalid file mode %s",
230				lineno, val);
231			ip->st_mode = getmode(m, 0);
232			free(m);
233			break;
234		case F_NLINK:
235			ip->st_nlink = strtoul(val, &ep, 10);
236			if (*ep)
237				errx(1, "line %d: invalid link count %s",
238				lineno,  val);
239			break;
240		case F_SIZE:
241			ip->st_size = strtoq(val, &ep, 10);
242			if (*ep)
243				errx(1, "line %d: invalid size %s",
244				lineno, val);
245			break;
246		case F_SLINK:
247			ip->slink = malloc(strlen(val) + 1);
248			if (ip->slink == NULL)
249				errx(1, "malloc");
250			if (strunvis(ip->slink, val) == -1)
251				errx(1, "symlink %s is ill-encoded", val);
252			break;
253		case F_TIME:
254			ip->st_mtimespec.tv_sec = strtoul(val, &ep, 10);
255			if (*ep != '.')
256				errx(1, "line %d: invalid time %s",
257				lineno, val);
258			val = ep + 1;
259			ip->st_mtimespec.tv_nsec = strtoul(val, &ep, 10);
260			if (*ep)
261				errx(1, "line %d: invalid time %s",
262				lineno, val);
263			break;
264		case F_TYPE:
265			switch(*val) {
266			case 'b':
267				if (!strcmp(val, "block"))
268					ip->type = F_BLOCK;
269				break;
270			case 'c':
271				if (!strcmp(val, "char"))
272					ip->type = F_CHAR;
273				break;
274			case 'd':
275				if (!strcmp(val, "dir"))
276					ip->type = F_DIR;
277				break;
278			case 'f':
279				if (!strcmp(val, "file"))
280					ip->type = F_FILE;
281				if (!strcmp(val, "fifo"))
282					ip->type = F_FIFO;
283				break;
284			case 'l':
285				if (!strcmp(val, "link"))
286					ip->type = F_LINK;
287				break;
288			case 's':
289				if (!strcmp(val, "socket"))
290					ip->type = F_SOCK;
291				break;
292			default:
293				errx(1, "line %d: unknown file type %s",
294				lineno, val);
295			}
296			break;
297		case F_UID:
298			ip->st_uid = (uid_t)strtoul(val, &ep, 10);
299			if (*ep)
300				errx(1, "line %d: invalid uid %s", lineno, val);
301			break;
302		case F_UNAME:
303			if ((pw = getpwnam(val)) == NULL)
304			    errx(1, "line %d: unknown user %s", lineno, val);
305			ip->st_uid = pw->pw_uid;
306			break;
307		}
308	}
309}
310
311static void
312unset(char *t, NODE *ip)
313{
314	char *p;
315
316	while ((p = strtok(t, "\n\t ")))
317		ip->flags &= ~parsekey(p, NULL);
318}
319