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