file_cmds-321.40.3.tar.gz
[apple/file_cmds.git] / mtree / spec.c
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
32 static 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 "metrics.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 *
60 mtree_readspec(FILE *fi)
61 {
62 NODE *centry, *last;
63 char *p;
64 NODE ginfo, *root;
65 int c_cur, c_next;
66 char buf[2048];
67
68 centry = last = root = NULL;
69 bzero(&ginfo, sizeof(ginfo));
70 c_cur = c_next = 0;
71 for (lineno = 1; fgets(buf, sizeof(buf), fi);
72 ++lineno, c_cur = c_next, c_next = 0) {
73 /* Skip empty lines. */
74 if (buf[0] == '\n')
75 continue;
76
77 /* Find end of line. */
78 if ((p = index(buf, '\n')) == NULL) {
79 RECORD_FAILURE(21, ERANGE);
80 errx(1, "line %d too long", lineno);
81 }
82
83 /* See if next line is continuation line. */
84 if (p[-1] == '\\') {
85 --p;
86 c_next = 1;
87 }
88
89 /* Null-terminate the line. */
90 *p = '\0';
91
92 /* Skip leading whitespace. */
93 for (p = buf; *p && isspace(*p); ++p);
94
95 /* If nothing but whitespace or comment char, continue. */
96 if (!*p || *p == '#')
97 continue;
98
99 #ifdef DEBUG
100 (void)fprintf(stderr, "line %d: {%s}\n", lineno, p);
101 #endif
102 if (c_cur) {
103 set(p, centry);
104 continue;
105 }
106
107 /* Grab file name, "$", "set", or "unset". */
108 if ((p = strtok(p, "\n\t ")) == NULL) {
109 RECORD_FAILURE(22, EINVAL);
110 errx(1, "line %d: missing field", lineno);
111 }
112
113 if (p[0] == '/')
114 switch(p[1]) {
115 case 's':
116 if (strcmp(p + 1, "set"))
117 break;
118 set(NULL, &ginfo);
119 continue;
120 case 'u':
121 if (strcmp(p + 1, "unset"))
122 break;
123 unset(NULL, &ginfo);
124 continue;
125 }
126
127 if (index(p, '/')) {
128 RECORD_FAILURE(23, EINVAL);
129 errx(1, "line %d: slash character in file name",
130 lineno);
131 }
132
133 if (!strcmp(p, "..")) {
134 /* Don't go up, if haven't gone down. */
135 if (!root)
136 goto noparent;
137 if (last->type != F_DIR || last->flags & F_DONE) {
138 if (last == root)
139 goto noparent;
140 last = last->parent;
141 }
142 last->flags |= F_DONE;
143 continue;
144
145 noparent: RECORD_FAILURE(24, EINVAL);
146 errx(1, "line %d: no parent node", lineno);
147 }
148
149 if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL) {
150 RECORD_FAILURE(25, ENOMEM);
151 errx(1, "calloc");
152 }
153 *centry = ginfo;
154 #define MAGIC "?*["
155 if (strpbrk(p, MAGIC))
156 centry->flags |= F_MAGIC;
157 if (strunvis(centry->name, p) == -1) {
158 RECORD_FAILURE(26, EILSEQ);
159 errx(1, "filename %s is ill-encoded", p);
160 }
161 set(NULL, centry);
162
163 if (!root) {
164 last = root = centry;
165 root->parent = root;
166 } else if (last->type == F_DIR && !(last->flags & F_DONE)) {
167 centry->parent = last;
168 last = last->child = centry;
169 } else {
170 centry->parent = last->parent;
171 centry->prev = last;
172 last = last->next = centry;
173 }
174 }
175 return (root);
176 }
177
178 static void
179 set(char *t, NODE *ip)
180 {
181 int error = 0;
182 int type;
183 char *kw, *val = NULL;
184 struct group *gr;
185 struct passwd *pw;
186 mode_t *m;
187 int value;
188 char *ep;
189
190 for (; (kw = strtok(t, "= \t\n")); t = NULL) {
191 ip->flags |= type = parsekey(kw, &value);
192 if ((value == 0) || (val = strtok(NULL, " \t\n")) == NULL) {
193 RECORD_FAILURE(27, EINVAL);
194 errx(1, "line %d: missing value", lineno);
195 }
196 switch(type) {
197 case F_CKSUM:
198 ip->cksum = strtoul(val, &ep, 10);
199 if (*ep) {
200 RECORD_FAILURE(28, EINVAL);
201 errx(1, "line %d: invalid checksum %s",
202 lineno, val);
203 }
204 break;
205 case F_MD5:
206 ip->md5digest = strdup(val);
207 if (!ip->md5digest) {
208 RECORD_FAILURE(29, ENOMEM);
209 errx(1, "strdup");
210 }
211 break;
212 case F_SHA1:
213 ip->sha1digest = strdup(val);
214 if (!ip->sha1digest) {
215 RECORD_FAILURE(30, ENOMEM);
216 errx(1, "strdup");
217 }
218 break;
219 case F_SHA256:
220 ip->sha256digest = strdup(val);
221 if (!ip->sha256digest) {
222 RECORD_FAILURE(31, ENOMEM);
223 errx(1, "strdup");
224 }
225 break;
226 case F_RMD160:
227 ip->rmd160digest = strdup(val);
228 if (!ip->rmd160digest) {
229 RECORD_FAILURE(32, ENOMEM);
230 errx(1, "strdup");
231 }
232 break;
233 case F_FLAGS:
234 if (strcmp("none", val) == 0) {
235 ip->st_flags = 0;
236 } else if (strtofflags(&val, &ip->st_flags, NULL) != 0) {
237 RECORD_FAILURE(33, EINVAL);
238 errx(1, "line %d: invalid flag %s",lineno, val);
239 }
240 break;
241 case F_GID:
242 ip->st_gid = (gid_t)strtoul(val, &ep, 10);
243 if (*ep) {
244 RECORD_FAILURE(34, EINVAL);
245 errx(1, "line %d: invalid gid %s", lineno, val);
246 }
247 break;
248 case F_GNAME:
249 if ((gr = getgrnam(val)) == NULL) {
250 RECORD_FAILURE(35, EINVAL);
251 errx(1, "line %d: unknown group %s", lineno, val);
252 }
253 ip->st_gid = gr->gr_gid;
254 break;
255 case F_IGN:
256 /* just set flag bit */
257 break;
258 case F_MODE:
259 if ((m = setmode(val)) == NULL) {
260 RECORD_FAILURE(36, EINVAL);
261 errx(1, "line %d: invalid file mode %s",
262 lineno, val);
263 }
264 ip->st_mode = getmode(m, 0);
265 free(m);
266 break;
267 case F_NLINK:
268 ip->st_nlink = strtoul(val, &ep, 10);
269 if (*ep) {
270 RECORD_FAILURE(37, EINVAL);
271 errx(1, "line %d: invalid link count %s",
272 lineno, val);
273 }
274 break;
275 case F_SIZE:
276 ip->st_size = strtoq(val, &ep, 10);
277 if (*ep) {
278 RECORD_FAILURE(38, EINVAL);
279 errx(1, "line %d: invalid size %s",
280 lineno, val);
281 }
282 break;
283 case F_SLINK:
284 ip->slink = malloc(strlen(val) + 1);
285 if (ip->slink == NULL) {
286 RECORD_FAILURE(39, ENOMEM);
287 errx(1, "malloc");
288 }
289 if (strunvis(ip->slink, val) == -1) {
290 RECORD_FAILURE(40, EILSEQ);
291 errx(1, "symlink %s is ill-encoded", val);
292 }
293 break;
294 case F_TIME:
295 ip->st_mtimespec.tv_sec = strtoul(val, &ep, 10);
296 if (*ep != '.') {
297 RECORD_FAILURE(41, EINVAL);
298 errx(1, "line %d: invalid time %s",
299 lineno, val);
300 }
301 val = ep + 1;
302 ip->st_mtimespec.tv_nsec = strtoul(val, &ep, 10);
303 if (*ep) {
304 RECORD_FAILURE(42, EINVAL);
305 errx(1, "line %d: invalid time %s",
306 lineno, val);
307 }
308 break;
309 case F_TYPE:
310 switch(*val) {
311 case 'b':
312 if (!strcmp(val, "block"))
313 ip->type = F_BLOCK;
314 break;
315 case 'c':
316 if (!strcmp(val, "char"))
317 ip->type = F_CHAR;
318 break;
319 case 'd':
320 if (!strcmp(val, "dir"))
321 ip->type = F_DIR;
322 break;
323 case 'f':
324 if (!strcmp(val, "file"))
325 ip->type = F_FILE;
326 if (!strcmp(val, "fifo"))
327 ip->type = F_FIFO;
328 break;
329 case 'l':
330 if (!strcmp(val, "link"))
331 ip->type = F_LINK;
332 break;
333 case 's':
334 if (!strcmp(val, "socket"))
335 ip->type = F_SOCK;
336 break;
337 default:
338 RECORD_FAILURE(43, EINVAL);
339 errx(1, "line %d: unknown file type %s",
340 lineno, val);
341 }
342 break;
343 case F_UID:
344 ip->st_uid = (uid_t)strtoul(val, &ep, 10);
345 if (*ep) {
346 RECORD_FAILURE(44, EINVAL);
347 errx(1, "line %d: invalid uid %s", lineno, val);
348 }
349 break;
350 case F_UNAME:
351 if ((pw = getpwnam(val)) == NULL) {
352 RECORD_FAILURE(45, EINVAL);
353 errx(1, "line %d: unknown user %s", lineno, val);
354 }
355 ip->st_uid = pw->pw_uid;
356 break;
357 case F_BTIME:
358 ip->st_birthtimespec.tv_sec = strtoul(val, &ep, 10);
359 if (*ep != '.') {
360 RECORD_FAILURE(46, EINVAL);
361 errx(1, "line %d: invalid time %s",
362 lineno, val);
363 }
364 val = ep + 1;
365 ip->st_birthtimespec.tv_nsec = strtoul(val, &ep, 10);
366 if (*ep) {
367 RECORD_FAILURE(47, EINVAL);
368 errx(1, "line %d: invalid time %s",
369 lineno, val);
370 }
371 break;
372 case F_ATIME:
373 ip->st_atimespec.tv_sec = strtoul(val, &ep, 10);
374 if (*ep != '.') {
375 RECORD_FAILURE(48, EINVAL);
376 errx(1, "line %d: invalid time %s",
377 lineno, val);
378 }
379 val = ep + 1;
380 ip->st_atimespec.tv_nsec = strtoul(val, &ep, 10);
381 if (*ep) {
382 RECORD_FAILURE(49, EINVAL);
383 errx(1, "line %d: invalid time %s",
384 lineno, val);
385 }
386 break;
387 case F_CTIME:
388 ip->st_ctimespec.tv_sec = strtoul(val, &ep, 10);
389 if (*ep != '.') {
390 RECORD_FAILURE(50, EINVAL);
391 errx(1, "line %d: invalid time %s",
392 lineno, val);
393 }
394 val = ep + 1;
395 ip->st_ctimespec.tv_nsec = strtoul(val, &ep, 10);
396 if (*ep) {
397 RECORD_FAILURE(51, EINVAL);
398 errx(1, "line %d: invalid time %s",
399 lineno, val);
400 }
401 break;
402 case F_PTIME:
403 ip->st_ptimespec.tv_sec = strtoul(val, &ep, 10);
404 if (*ep != '.') {
405 RECORD_FAILURE(52, EINVAL);
406 errx(1, "line %d: invalid time %s",
407 lineno, val);
408 }
409 val = ep + 1;
410 ip->st_ptimespec.tv_nsec = strtoul(val, &ep, 10);
411 if (*ep) {
412 RECORD_FAILURE(53, EINVAL);
413 errx(1, "line %d: invalid time %s",
414 lineno, val);
415 }
416 break;
417 case F_XATTRS:
418 ep = strtok(val,".");
419 ip->xattrsdigest = strdup(ep);
420 if (!ip->xattrsdigest) {
421 error = errno;
422 RECORD_FAILURE(54, error);
423 errc(1, error, "strdup");
424 }
425 val = strtok(NULL,".");
426 if (val) {
427 ip->xdstream_priv_id = strtoull(val, &ep, 10);
428 if (*ep) {
429 RECORD_FAILURE(55, EINVAL);
430 errx(1, "line %d: invalid private id %s",
431 lineno, val);
432 }
433 } else {
434 ip->xdstream_priv_id = 0;
435 }
436 break;
437 case F_INODE:
438 ip->st_ino = (ino_t)strtoull(val, &ep, 10);
439 if (*ep) {
440 RECORD_FAILURE(56, EINVAL);
441 errx(1, "line %d: invalid inode %s",
442 lineno, val);
443 }
444 break;
445 case F_ACL:
446 ip->acldigest = strdup(val);
447 if (!ip->acldigest) {
448 error = errno;
449 RECORD_FAILURE(57, error);
450 errc(1, error, "strdup");
451 }
452 break;
453 case F_SIBLINGID:
454 ip->sibling_id = (quad_t)strtoull(val, &ep, 10);
455 if (*ep) {
456 RECORD_FAILURE(58, EINVAL);
457 errx(1, "line %d: invalid sibling id %s", lineno, val);
458 }
459 }
460 }
461 }
462
463 static void
464 unset(char *t, NODE *ip)
465 {
466 char *p;
467
468 while ((p = strtok(t, "\n\t ")))
469 ip->flags &= ~parsekey(p, NULL);
470 }