xref: /dragonfly/usr.sbin/mtree/spec.c (revision 27a796753626c4fca2de1d161fddbac6d6930ecc)
1 /*        @(#)spec.c          8.2 (Berkeley) 4/28/95        */
2 /*        $NetBSD: spec.c,v 1.90 2017/12/14 18:34:41 christos 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 /*-
34  * Copyright (c) 2001-2004 The NetBSD Foundation, Inc.
35  * All rights reserved.
36  *
37  * This code is derived from software contributed to The NetBSD Foundation
38  * by Luke Mewburn of Wasabi Systems.
39  *
40  * Redistribution and use in source and binary forms, with or without
41  * modification, are permitted provided that the following conditions
42  * are met:
43  * 1. Redistributions of source code must retain the above copyright
44  *    notice, this list of conditions and the following disclaimer.
45  * 2. Redistributions in binary form must reproduce the above copyright
46  *    notice, this list of conditions and the following disclaimer in the
47  *    documentation and/or other materials provided with the distribution.
48  *
49  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
50  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
51  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
52  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
53  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
54  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
55  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
56  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
57  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
58  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
59  * POSSIBILITY OF SUCH DAMAGE.
60  */
61 
62 #include <sys/param.h>
63 #include <sys/stat.h>
64 
65 #include <assert.h>
66 #include <ctype.h>
67 #include <errno.h>
68 #include <grp.h>
69 #include <pwd.h>
70 #include <stdarg.h>
71 #include <stdio.h>
72 #include <stdint.h>
73 #include <stdlib.h>
74 #include <string.h>
75 #include <unistd.h>
76 #include <vis.h>
77 #include <util.h>
78 
79 #include "extern.h"
80 #include "pack_dev.h"
81 
82 size_t    mtree_lineno;                           /* Current spec line number */
83 int       mtree_Mflag;                            /* Merge duplicate entries */
84 int       mtree_Wflag;                            /* Don't "whack" permissions */
85 int       mtree_Sflag;                            /* Sort entries */
86 
87 static    dev_t     parsedev(char *);
88 static    void      replacenode(NODE *, NODE *);
89 static    void      set(char *, NODE *);
90 static    void      unset(char *, NODE *);
91 static    void      addchild(NODE *, NODE *);
92 static    int       nodecmp(const NODE *, const NODE *);
93 static    int       appendfield(FILE *, int, const char *, ...) __printflike(3, 4);
94 
95 #define REPLACEPTR(x,v)       do { if ((x)) free((x)); (x) = (v); } while (0)
96 
97 NODE *
spec(FILE * fp)98 spec(FILE *fp)
99 {
100           NODE *centry, *last, *pathparent, *cur;
101           char *p, *e, *next;
102           NODE ginfo, *root;
103           char *buf, *tname, *ntname;
104           size_t tnamelen, plen;
105 
106           root = NULL;
107           centry = last = NULL;
108           tname = NULL;
109           tnamelen = 0;
110           memset(&ginfo, 0, sizeof(ginfo));
111           for (mtree_lineno = 0;
112               (buf = fparseln(fp, NULL, &mtree_lineno, NULL,
113                     FPARSELN_UNESCCOMM));
114               free(buf)) {
115                     /* Skip leading whitespace. */
116                     for (p = buf; *p && isspace((unsigned char)*p); ++p)
117                               continue;
118 
119                     /* If nothing but whitespace, continue. */
120                     if (!*p)
121                               continue;
122 
123 #ifdef DEBUG
124                     fprintf(stderr, "line %lu: {%s}\n",
125                         (u_long)mtree_lineno, p);
126 #endif
127                     /* Grab file name, "$", "set", or "unset". */
128                     next = buf;
129                     while ((p = strsep(&next, " \t")) != NULL && *p == '\0')
130                               continue;
131                     if (p == NULL)
132                               mtree_err("missing field");
133 
134                     if (p[0] == '/') {
135                               if (strcmp(p + 1, "set") == 0)
136                                         set(next, &ginfo);
137                               else if (strcmp(p + 1, "unset") == 0)
138                                         unset(next, &ginfo);
139                               else
140                                         mtree_err("invalid specification `%s'", p);
141                               continue;
142                     }
143 
144                     if (strcmp(p, "..") == 0) {
145                               /* Don't go up, if haven't gone down. */
146                               if (root == NULL)
147                                         goto noparent;
148                               if (last->type != F_DIR || last->flags & F_DONE) {
149                                         if (last == root)
150                                                   goto noparent;
151                                         last = last->parent;
152                               }
153                               last->flags |= F_DONE;
154                               continue;
155 
156 noparent:           mtree_err("no parent node");
157                     }
158 
159                     plen = strlen(p) + 1;
160                     if (plen > tnamelen) {
161                               if ((ntname = realloc(tname, plen)) == NULL)
162                                         mtree_err("realloc: %s", strerror(errno));
163                               tname = ntname;
164                               tnamelen = plen;
165                     }
166                     if (strunvis(tname, p) == -1)
167                               mtree_err("strunvis failed on `%s'", p);
168                     p = tname;
169 
170                     pathparent = NULL;
171                     if (strchr(p, '/') != NULL) {
172                               cur = root;
173                               for (; (e = strchr(p, '/')) != NULL; p = e+1) {
174                                         if (p == e)
175                                                   continue; /* handle // */
176                                         *e = '\0';
177                                         if (strcmp(p, ".") != 0) {
178                                                   while (cur &&
179                                                       strcmp(cur->name, p) != 0) {
180                                                             cur = cur->next;
181                                                   }
182                                         }
183                                         if (cur == NULL || cur->type != F_DIR) {
184                                                   mtree_err("%s: %s", tname,
185                                                   "missing directory in specification");
186                                         }
187                                         *e = '/';
188                                         pathparent = cur;
189                                         cur = cur->child;
190                               }
191                               if (*p == '\0')
192                                         mtree_err("%s: empty leaf element", tname);
193                     }
194 
195                     if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL)
196                               mtree_err("%s", strerror(errno));
197                     *centry = ginfo;
198                     centry->lineno = mtree_lineno;
199                     strcpy(centry->name, p);
200 #define   MAGIC     "?*["
201                     if (strpbrk(p, MAGIC))
202                               centry->flags |= F_MAGIC;
203                     set(next, centry);
204 
205                     if (root == NULL) {
206                                         /*
207                                          * empty tree
208                                          */
209                               /*
210                                * Allow a bare "." root node by forcing it to
211                                * type=dir for compatibility with FreeBSD.
212                                */
213                               if (strcmp(centry->name, ".") == 0 && centry->type == 0)
214                                         centry->type = F_DIR;
215                               if (strcmp(centry->name, ".") != 0 ||
216                                   centry->type != F_DIR)
217                                         mtree_err(
218                                             "root node must be the directory `.'");
219                               last = root = centry;
220                               root->parent = root;
221                     } else if (pathparent != NULL) {
222                                         /*
223                                          * full path entry; add or replace
224                                          */
225                               centry->parent = pathparent;
226                               addchild(pathparent, centry);
227                               last = centry;
228                     } else if (strcmp(centry->name, ".") == 0) {
229                                         /*
230                                          * duplicate "." entry; always replace
231                                          */
232                               replacenode(root, centry);
233                     } else if (last->type == F_DIR && !(last->flags & F_DONE)) {
234                                         /*
235                                          * new relative child in current dir;
236                                          * add or replace
237                                          */
238                               centry->parent = last;
239                               addchild(last, centry);
240                               last = centry;
241                     } else {
242                                         /*
243                                          * new relative child in parent dir
244                                          * (after encountering ".." entry);
245                                          * add or replace
246                                          */
247                               centry->parent = last->parent;
248                               addchild(last->parent, centry);
249                               last = centry;
250                     }
251           }
252           return (root);
253 }
254 
255 void
free_nodes(NODE * root)256 free_nodes(NODE *root)
257 {
258           NODE      *cur, *next;
259 
260           if (root == NULL)
261                     return;
262 
263           next = NULL;
264           for (cur = root; cur != NULL; cur = next) {
265                     next = cur->next;
266                     free_nodes(cur->child);
267                     REPLACEPTR(cur->slink, NULL);
268                     REPLACEPTR(cur->md5digest, NULL);
269                     REPLACEPTR(cur->rmd160digest, NULL);
270                     REPLACEPTR(cur->sha1digest, NULL);
271                     REPLACEPTR(cur->sha256digest, NULL);
272                     REPLACEPTR(cur->sha384digest, NULL);
273                     REPLACEPTR(cur->sha512digest, NULL);
274                     REPLACEPTR(cur->tags, NULL);
275                     REPLACEPTR(cur, NULL);
276           }
277 }
278 
279 /*
280  * appendfield --
281  *        Like fprintf(), but output a space either before or after
282  *        the regular output, according to the pathlast flag.
283  */
284 static int
appendfield(FILE * fp,int pathlast,const char * fmt,...)285 appendfield(FILE *fp, int pathlast, const char *fmt, ...)
286 {
287           va_list ap;
288           int result;
289 
290           va_start(ap, fmt);
291           if (!pathlast)
292                     fprintf(fp, " ");
293           result = vprintf(fmt, ap);
294           if (pathlast)
295                     fprintf(fp, " ");
296           va_end(ap);
297           return result;
298 }
299 
300 /*
301  * dump_nodes --
302  *        dump the NODEs from `cur', based in the directory `dir'.
303  *        if pathlast is none zero, print the path last, otherwise print
304  *        it first.
305  */
306 void
dump_nodes(FILE * fp,const char * dir,NODE * root,int pathlast)307 dump_nodes(FILE *fp, const char *dir, NODE *root, int pathlast)
308 {
309           NODE      *cur;
310           char      path[MAXPATHLEN];
311           const char *name;
312           char      *str;
313           char      *p, *q;
314 
315           for (cur = root; cur != NULL; cur = cur->next) {
316                     if (cur->type != F_DIR && !matchtags(cur))
317                               continue;
318 
319                     if (snprintf(path, sizeof(path), "%s%s%s",
320                         dir, *dir ? "/" : "", cur->name)
321                         >= (int)sizeof(path))
322                               mtree_err("Pathname too long.");
323 
324                     if (!pathlast)
325                               fprintf(fp, "%s", vispath(path));
326 
327 #define MATCHFLAG(f)          ((keys & (f)) && (cur->flags & (f)))
328                     if (MATCHFLAG(F_TYPE))
329                               appendfield(fp, pathlast, "type=%s",
330                                   nodetype(cur->type));
331                     if (MATCHFLAG(F_UID | F_UNAME)) {
332                               if (keys & F_UNAME &&
333                                   (name = user_from_uid(cur->st_uid, 1)) != NULL)
334                                         appendfield(fp, pathlast, "uname=%s", name);
335                               else
336                                         appendfield(fp, pathlast, "uid=%u",
337                                             cur->st_uid);
338                     }
339                     if (MATCHFLAG(F_GID | F_GNAME)) {
340                               if (keys & F_GNAME &&
341                                   (name = group_from_gid(cur->st_gid, 1)) != NULL)
342                                         appendfield(fp, pathlast, "gname=%s", name);
343                               else
344                                         appendfield(fp, pathlast, "gid=%u",
345                                             cur->st_gid);
346                     }
347                     if (MATCHFLAG(F_MODE))
348                               appendfield(fp, pathlast, "mode=%#o", cur->st_mode);
349                     if (MATCHFLAG(F_DEV) &&
350                         (cur->type == F_BLOCK || cur->type == F_CHAR))
351                               appendfield(fp, pathlast, "device=%#jx",
352                                   (uintmax_t)cur->st_rdev);
353                     if (MATCHFLAG(F_NLINK))
354                               appendfield(fp, pathlast, "nlink=%ju",
355                                   (uintmax_t)cur->st_nlink);
356                     if (MATCHFLAG(F_SLINK))
357                               appendfield(fp, pathlast, "link=%s",
358                                   vispath(cur->slink));
359                     if (MATCHFLAG(F_SIZE))
360                               appendfield(fp, pathlast, "size=%ju",
361                                   (uintmax_t)cur->st_size);
362                     if (MATCHFLAG(F_TIME))
363                               appendfield(fp, pathlast, "time=%jd.%09ld",
364                                   (intmax_t)cur->st_mtimespec.tv_sec,
365                                   cur->st_mtimespec.tv_nsec);
366                     if (MATCHFLAG(F_CKSUM))
367                               appendfield(fp, pathlast, "cksum=%lu", cur->cksum);
368                     if (MATCHFLAG(F_MD5))
369                               appendfield(fp, pathlast, "%s=%s", MD5KEY,
370                                   cur->md5digest);
371                     if (MATCHFLAG(F_RMD160))
372                               appendfield(fp, pathlast, "%s=%s", RMD160KEY,
373                                   cur->rmd160digest);
374                     if (MATCHFLAG(F_SHA1))
375                               appendfield(fp, pathlast, "%s=%s", SHA1KEY,
376                                   cur->sha1digest);
377                     if (MATCHFLAG(F_SHA256))
378                               appendfield(fp, pathlast, "%s=%s", SHA256KEY,
379                                   cur->sha256digest);
380                     if (MATCHFLAG(F_SHA384))
381                               appendfield(fp, pathlast, "%s=%s", SHA384KEY,
382                                   cur->sha384digest);
383                     if (MATCHFLAG(F_SHA512))
384                               appendfield(fp, pathlast, "%s=%s", SHA512KEY,
385                                   cur->sha512digest);
386                     if (MATCHFLAG(F_FLAGS)) {
387                               str = flags_to_string(cur->st_flags, "none");
388                               appendfield(fp, pathlast, "flags=%s", str);
389                               free(str);
390                     }
391                     if (MATCHFLAG(F_IGN))
392                               appendfield(fp, pathlast, "ignore");
393                     if (MATCHFLAG(F_OPT))
394                               appendfield(fp, pathlast, "optional");
395                     if (MATCHFLAG(F_TAGS)) {
396                               /* don't output leading or trailing commas */
397                               p = cur->tags;
398                               while (*p == ',')
399                                         p++;
400                               q = p + strlen(p);
401                               while(q > p && q[-1] == ',')
402                                         q--;
403                               appendfield(fp, pathlast, "tags=%.*s", (int)(q - p), p);
404                     }
405                     puts(pathlast ? vispath(path) : "");
406 
407                     if (cur->child)
408                               dump_nodes(fp, path, cur->child, pathlast);
409           }
410 }
411 
412 /*
413  * vispath --
414  *        strsvis(3) encodes path, which must not be longer than MAXPATHLEN
415  *        characters long, and returns a pointer to a static buffer containing
416  *        the result.
417  */
418 char *
vispath(const char * path)419 vispath(const char *path)
420 {
421           static const char extra[] = { ' ', '\t', '\n', '\\', '#', '\0' };
422           static const char extra_glob[] = { ' ', '\t', '\n', '\\', '#', '*',
423               '?', '[', '\0' };
424           static char pathbuf[4*MAXPATHLEN + 1];
425 
426           if (flavor == F_NETBSD6)
427                     strsvis(pathbuf, path, VIS_CSTYLE, extra);
428           else
429                     strsvis(pathbuf, path, VIS_OCTAL, extra_glob);
430           return pathbuf;
431 }
432 
433 
434 static dev_t
parsedev(char * arg)435 parsedev(char *arg)
436 {
437 #define MAX_PACK_ARGS         3
438           u_long    numbers[MAX_PACK_ARGS];
439           char      *p, *ep, *dev;
440           int       argc;
441           pack_t    *pack;
442           dev_t     result;
443           const char *error = NULL;
444 
445           if ((dev = strchr(arg, ',')) != NULL) {
446                     *dev++='\0';
447                     if ((pack = pack_find(arg)) == NULL)
448                               mtree_err("unknown format `%s'", arg);
449                     argc = 0;
450                     while ((p = strsep(&dev, ",")) != NULL) {
451                               if (*p == '\0')
452                                         mtree_err("missing number");
453                               numbers[argc++] = strtoul(p, &ep, 0);
454                               if (*ep != '\0')
455                                         mtree_err("invalid number `%s'",
456                                             p);
457                               if (argc > MAX_PACK_ARGS)
458                                         mtree_err("too many arguments");
459                     }
460                     if (argc < 2)
461                               mtree_err("not enough arguments");
462                     result = (*pack)(argc, numbers, &error);
463                     if (error != NULL)
464                               mtree_err("%s", error);
465           } else {
466                     result = (dev_t)strtoul(arg, &ep, 0);
467                     if (*ep != '\0')
468                               mtree_err("invalid device `%s'", arg);
469           }
470           return (result);
471 }
472 
473 static void
replacenode(NODE * cur,NODE * new)474 replacenode(NODE *cur, NODE *new)
475 {
476 
477 #define REPLACE(x)  cur->x = new->x
478 #define REPLACESTR(x)         REPLACEPTR(cur->x,new->x)
479 
480           if (cur->type != new->type) {
481                     if (mtree_Mflag) {
482                                         /*
483                                          * merge entries with different types; we
484                                          * don't want children retained in this case.
485                                          */
486                               REPLACE(type);
487                               free_nodes(cur->child);
488                               cur->child = NULL;
489                     } else {
490                               mtree_err(
491                                   "existing entry for `%s', type `%s'"
492                                   " does not match type `%s'",
493                                   cur->name, nodetype(cur->type),
494                                   nodetype(new->type));
495                     }
496           }
497 
498           REPLACE(st_size);
499           REPLACE(st_mtimespec);
500           REPLACESTR(slink);
501           if (cur->slink != NULL) {
502                     if ((cur->slink = strdup(new->slink)) == NULL)
503                               mtree_err("memory allocation error");
504                     if (strunvis(cur->slink, new->slink) == -1)
505                               mtree_err("strunvis failed on `%s'", new->slink);
506                     free(new->slink);
507           }
508           REPLACE(st_uid);
509           REPLACE(st_gid);
510           REPLACE(st_mode);
511           REPLACE(st_rdev);
512           REPLACE(st_flags);
513           REPLACE(st_nlink);
514           REPLACE(cksum);
515           REPLACESTR(md5digest);
516           REPLACESTR(rmd160digest);
517           REPLACESTR(sha1digest);
518           REPLACESTR(sha256digest);
519           REPLACESTR(sha384digest);
520           REPLACESTR(sha512digest);
521           REPLACESTR(tags);
522           REPLACE(lineno);
523           REPLACE(flags);
524           free(new);
525 }
526 
527 static void
set(char * t,NODE * ip)528 set(char *t, NODE *ip)
529 {
530           int       type, value, len;
531           gid_t     gid;
532           uid_t     uid;
533           char      *kw, *val, *md, *ep;
534           void      *m;
535 
536           while ((kw = strsep(&t, "= \t")) != NULL) {
537                     if (*kw == '\0')
538                               continue;
539                     if (strcmp(kw, "all") == 0)
540                               mtree_err("invalid keyword `all'");
541                     ip->flags |= type = parsekey(kw, &value);
542                     if (!value)
543                               /* Just set flag bit (F_IGN and F_OPT) */
544                               continue;
545                     while ((val = strsep(&t, " \t")) != NULL && *val == '\0')
546                               continue;
547                     if (val == NULL)
548                               mtree_err("missing value");
549                     switch (type) {
550                     case F_CKSUM:
551                               ip->cksum = strtoul(val, &ep, 10);
552                               if (*ep)
553                                         mtree_err("invalid checksum `%s'", val);
554                               break;
555                     case F_DEV:
556                               ip->st_rdev = parsedev(val);
557                               break;
558                     case F_FLAGS:
559                               if (strcmp("none", val) == 0)
560                                         ip->st_flags = 0;
561                               else if (string_to_flags(&val, &ip->st_flags, NULL)
562                                   != 0)
563                                         mtree_err("invalid flag `%s'", val);
564                               break;
565                     case F_GID:
566                               ip->st_gid = (gid_t)strtoul(val, &ep, 10);
567                               if (*ep)
568                                         mtree_err("invalid gid `%s'", val);
569                               break;
570                     case F_GNAME:
571                               if (mtree_Wflag)    /* don't parse if whacking */
572                                         break;
573                               if (gid_from_group(val, &gid) == -1)
574                                         mtree_err("unknown group `%s'", val);
575                               ip->st_gid = gid;
576                               break;
577                     case F_MD5:
578                               if (val[0]=='0' && val[1]=='x')
579                                         md=&val[2];
580                               else
581                                         md=val;
582                               if ((ip->md5digest = strdup(md)) == NULL)
583                                         mtree_err("memory allocation error");
584                               break;
585                     case F_MODE:
586                               if ((m = setmode(val)) == NULL)
587                                         mtree_err("cannot set file mode `%s' (%s)",
588                                             val, strerror(errno));
589                               ip->st_mode = getmode(m, 0);
590                               free(m);
591                               break;
592                     case F_NLINK:
593                               ip->st_nlink = (nlink_t)strtoul(val, &ep, 10);
594                               if (*ep)
595                                         mtree_err("invalid link count `%s'", val);
596                               break;
597                     case F_RMD160:
598                               if (val[0]=='0' && val[1]=='x')
599                                         md=&val[2];
600                               else
601                                         md=val;
602                               if ((ip->rmd160digest = strdup(md)) == NULL)
603                                         mtree_err("memory allocation error");
604                               break;
605                     case F_SHA1:
606                               if (val[0]=='0' && val[1]=='x')
607                                         md=&val[2];
608                               else
609                                         md=val;
610                               if ((ip->sha1digest = strdup(md)) == NULL)
611                                         mtree_err("memory allocation error");
612                               break;
613                     case F_SIZE:
614                               ip->st_size = (off_t)strtoll(val, &ep, 10);
615                               if (*ep)
616                                         mtree_err("invalid size `%s'", val);
617                               break;
618                     case F_SLINK:
619                               if ((ip->slink = strdup(val)) == NULL)
620                                         mtree_err("memory allocation error");
621                               if (strunvis(ip->slink, val) == -1)
622                                         mtree_err("strunvis failed on `%s'", val);
623                               break;
624                     case F_TAGS:
625                               len = strlen(val) + 3;        /* "," + str + ",\0" */
626                               if ((ip->tags = malloc(len)) == NULL)
627                                         mtree_err("memory allocation error");
628                               snprintf(ip->tags, len, ",%s,", val);
629                               break;
630                     case F_TIME:
631                               ip->st_mtimespec.tv_sec =
632                                   (time_t)strtoll(val, &ep, 10);
633                               if (*ep != '.')
634                                         mtree_err("invalid time `%s'", val);
635                               val = ep + 1;
636                               ip->st_mtimespec.tv_nsec = strtol(val, &ep, 10);
637                               if (*ep)
638                                         mtree_err("invalid time `%s'", val);
639                               break;
640                     case F_TYPE:
641                               ip->type = parsetype(val);
642                               break;
643                     case F_UID:
644                               ip->st_uid = (uid_t)strtoul(val, &ep, 10);
645                               if (*ep)
646                                         mtree_err("invalid uid `%s'", val);
647                               break;
648                     case F_UNAME:
649                               if (mtree_Wflag)    /* don't parse if whacking */
650                                         break;
651                               if (uid_from_user(val, &uid) == -1)
652                                         mtree_err("unknown user `%s'", val);
653                               ip->st_uid = uid;
654                               break;
655                     case F_SHA256:
656                               if (val[0]=='0' && val[1]=='x')
657                                         md=&val[2];
658                               else
659                                         md=val;
660                               if ((ip->sha256digest = strdup(md)) == NULL)
661                                         mtree_err("memory allocation error");
662                               break;
663                     case F_SHA384:
664                               if (val[0]=='0' && val[1]=='x')
665                                         md=&val[2];
666                               else
667                                         md=val;
668                               if ((ip->sha384digest = strdup(md)) == NULL)
669                                         mtree_err("memory allocation error");
670                               break;
671                     case F_SHA512:
672                               if (val[0]=='0' && val[1]=='x')
673                                         md=&val[2];
674                               else
675                                         md=val;
676                               if ((ip->sha512digest = strdup(md)) == NULL)
677                                         mtree_err("memory allocation error");
678                               break;
679                     default:
680                               mtree_err(
681                                   "set(): unsupported key type 0x%x (INTERNAL ERROR)",
682                                   type);
683                               /* NOTREACHED */
684                     }
685           }
686 }
687 
688 static void
unset(char * t,NODE * ip)689 unset(char *t, NODE *ip)
690 {
691           char *p;
692 
693           while ((p = strsep(&t, " \t")) != NULL) {
694                     if (*p == '\0')
695                               continue;
696                     ip->flags &= ~parsekey(p, NULL);
697           }
698 }
699 
700 /*
701  * addchild --
702  *        Add the centry node as a child of the pathparent node.      If
703  *        centry is a duplicate, call replacenode().  If centry is not
704  *        a duplicate, insert it into the linked list referenced by
705  *        pathparent->child.  Keep the list sorted if Sflag is set.
706  */
707 static void
addchild(NODE * pathparent,NODE * centry)708 addchild(NODE *pathparent, NODE *centry)
709 {
710           NODE *samename;      /* node with the same name as centry */
711           NODE *replacepos;    /* if non-NULL, centry should replace this node */
712           NODE *insertpos;     /* if non-NULL, centry should be inserted
713                                     * after this node */
714           NODE *cur;           /* for stepping through the list */
715           NODE *last;          /* the last node in the list */
716           int cmp;
717 
718           samename = NULL;
719           replacepos = NULL;
720           insertpos = NULL;
721           last = NULL;
722           cur = pathparent->child;
723           if (cur == NULL) {
724                     /* centry is pathparent's first and only child node so far */
725                     pathparent->child = centry;
726                     return;
727           }
728 
729           /*
730            * pathparent already has at least one other child, so add the
731            * centry node to the list.
732            *
733            * We first scan through the list looking for an existing node
734            * with the same name (setting samename), and also looking
735            * for the correct position to replace or insert the new node
736            * (setting replacepos and/or insertpos).
737            */
738           for (; cur != NULL; last = cur, cur = cur->next) {
739                     if (strcmp(centry->name, cur->name) == 0) {
740                               samename = cur;
741                     }
742                     if (mtree_Sflag) {
743                               cmp = nodecmp(centry, cur);
744                               if (cmp == 0) {
745                                         replacepos = cur;
746                               } else if (cmp > 0) {
747                                         insertpos = cur;
748                               }
749                     }
750           }
751           if (! mtree_Sflag) {
752                     if (samename != NULL) {
753                               /* replace node with same name */
754                               replacepos = samename;
755                     } else {
756                               /* add new node at end of list */
757                               insertpos = last;
758                     }
759           }
760 
761           if (samename != NULL) {
762                     /*
763                      * We found a node with the same name above.  Call
764                      * replacenode(), which will either exit with an error,
765                      * or replace the information in the samename node and
766                      * free the information in the centry node.
767                      */
768                     replacenode(samename, centry);
769                     if (samename == replacepos) {
770                               /* The just-replaced node was in the correct position */
771                               return;
772                     }
773                     if (samename == insertpos || samename->prev == insertpos) {
774                               /*
775                                * We thought the new node should be just before
776                                * or just after the replaced node, but that would
777                                * be equivalent to just retaining the replaced node.
778                                */
779                               return;
780                     }
781 
782                     /*
783                      * The just-replaced node is in the wrong position in
784                      * the list.  This can happen if sort order depends on
785                      * criteria other than the node name.
786                      *
787                      * Make centry point to the just-replaced node.    Unlink
788                      * the just-replaced node from the list, and allow it to
789                      * be insterted in the correct position later.
790                      */
791                     centry = samename;
792                     if (centry->prev)
793                               centry->prev->next = centry->next;
794                     else {
795                               /* centry->next is the new head of the list */
796                               pathparent->child = centry->next;
797                               assert(centry->next != NULL);
798                     }
799                     if (centry->next)
800                               centry->next->prev = centry->prev;
801                     centry->prev = NULL;
802                     centry->next = NULL;
803           }
804 
805           if (insertpos == NULL) {
806                     /* insert centry at the beginning of the list */
807                     pathparent->child->prev = centry;
808                     centry->next = pathparent->child;
809                     centry->prev = NULL;
810                     pathparent->child = centry;
811           } else {
812                     /* insert centry into the list just after insertpos */
813                     centry->next = insertpos->next;
814                     insertpos->next = centry;
815                     centry->prev = insertpos;
816                     if (centry->next)
817                               centry->next->prev = centry;
818           }
819           return;
820 }
821 
822 /*
823  * nodecmp --
824  *        used as a comparison function by addchild() to control the order
825  *        in which entries appear within a list of sibling nodes.      We make
826  *        directories sort after non-directories, but otherwise sort in
827  *        strcmp() order.
828  *
829  * Keep this in sync with dcmp() in create.c.
830  */
831 static int
nodecmp(const NODE * a,const NODE * b)832 nodecmp(const NODE *a, const NODE *b)
833 {
834 
835           if ((a->type & F_DIR) != 0) {
836                     if ((b->type & F_DIR) == 0)
837                               return 1;
838           } else if ((b->type & F_DIR) != 0)
839                     return -1;
840           return strcmp(a->name, b->name);
841 }
842