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