1273796Sbrooks/* $NetBSD: spec.c,v 1.89 2014/04/24 17:22:41 christos Exp $ */ 2244541Sbrooks 3244541Sbrooks/*- 4244541Sbrooks * Copyright (c) 1989, 1993 5244541Sbrooks * The Regents of the University of California. All rights reserved. 6244541Sbrooks * 7244541Sbrooks * Redistribution and use in source and binary forms, with or without 8244541Sbrooks * modification, are permitted provided that the following conditions 9244541Sbrooks * are met: 10244541Sbrooks * 1. Redistributions of source code must retain the above copyright 11244541Sbrooks * notice, this list of conditions and the following disclaimer. 12244541Sbrooks * 2. Redistributions in binary form must reproduce the above copyright 13244541Sbrooks * notice, this list of conditions and the following disclaimer in the 14244541Sbrooks * documentation and/or other materials provided with the distribution. 15244541Sbrooks * 3. Neither the name of the University nor the names of its contributors 16244541Sbrooks * may be used to endorse or promote products derived from this software 17244541Sbrooks * without specific prior written permission. 18244541Sbrooks * 19244541Sbrooks * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20244541Sbrooks * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21244541Sbrooks * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22244541Sbrooks * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23244541Sbrooks * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24244541Sbrooks * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25244541Sbrooks * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26244541Sbrooks * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27244541Sbrooks * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28244541Sbrooks * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29244541Sbrooks * SUCH DAMAGE. 30244541Sbrooks */ 31244541Sbrooks 32244541Sbrooks/*- 33244541Sbrooks * Copyright (c) 2001-2004 The NetBSD Foundation, Inc. 34244541Sbrooks * All rights reserved. 35244541Sbrooks * 36244541Sbrooks * This code is derived from software contributed to The NetBSD Foundation 37244541Sbrooks * by Luke Mewburn of Wasabi Systems. 38244541Sbrooks * 39244541Sbrooks * Redistribution and use in source and binary forms, with or without 40244541Sbrooks * modification, are permitted provided that the following conditions 41244541Sbrooks * are met: 42244541Sbrooks * 1. Redistributions of source code must retain the above copyright 43244541Sbrooks * notice, this list of conditions and the following disclaimer. 44244541Sbrooks * 2. Redistributions in binary form must reproduce the above copyright 45244541Sbrooks * notice, this list of conditions and the following disclaimer in the 46244541Sbrooks * documentation and/or other materials provided with the distribution. 47244541Sbrooks * 48244541Sbrooks * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 49244541Sbrooks * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 50244541Sbrooks * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 51244541Sbrooks * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 52244541Sbrooks * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 53244541Sbrooks * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 54244541Sbrooks * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 55244541Sbrooks * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 56244541Sbrooks * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 57244541Sbrooks * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 58244541Sbrooks * POSSIBILITY OF SUCH DAMAGE. 59244541Sbrooks */ 60244541Sbrooks 61244541Sbrooks#if HAVE_NBTOOL_CONFIG_H 62244541Sbrooks#include "nbtool_config.h" 63244541Sbrooks#endif 64244541Sbrooks 65244541Sbrooks#include <sys/cdefs.h> 66244541Sbrooks#if defined(__RCSID) && !defined(lint) 67244541Sbrooks#if 0 68244541Sbrooksstatic char sccsid[] = "@(#)spec.c 8.2 (Berkeley) 4/28/95"; 69244541Sbrooks#else 70273796Sbrooks__RCSID("$NetBSD: spec.c,v 1.89 2014/04/24 17:22:41 christos Exp $"); 71244541Sbrooks#endif 72244541Sbrooks#endif /* not lint */ 73244541Sbrooks 74244541Sbrooks#include <sys/param.h> 75244541Sbrooks#include <sys/stat.h> 76244541Sbrooks 77244541Sbrooks#include <assert.h> 78244541Sbrooks#include <ctype.h> 79244541Sbrooks#include <errno.h> 80244541Sbrooks#include <grp.h> 81244541Sbrooks#include <pwd.h> 82244541Sbrooks#include <stdarg.h> 83258437Sbrooks#include <stdio.h> 84256687Sbrooks#include <stdint.h> 85244541Sbrooks#include <stdlib.h> 86244541Sbrooks#include <string.h> 87244541Sbrooks#include <unistd.h> 88244541Sbrooks#include <vis.h> 89244541Sbrooks#include <util.h> 90244541Sbrooks 91244541Sbrooks#include "extern.h" 92244541Sbrooks#include "pack_dev.h" 93244541Sbrooks 94244541Sbrookssize_t mtree_lineno; /* Current spec line number */ 95244541Sbrooksint mtree_Mflag; /* Merge duplicate entries */ 96244541Sbrooksint mtree_Wflag; /* Don't "whack" permissions */ 97244541Sbrooksint mtree_Sflag; /* Sort entries */ 98244541Sbrooks 99244541Sbrooksstatic dev_t parsedev(char *); 100244541Sbrooksstatic void replacenode(NODE *, NODE *); 101244541Sbrooksstatic void set(char *, NODE *); 102244541Sbrooksstatic void unset(char *, NODE *); 103244541Sbrooksstatic void addchild(NODE *, NODE *); 104244541Sbrooksstatic int nodecmp(const NODE *, const NODE *); 105273796Sbrooksstatic int appendfield(FILE *, int, const char *, ...) __printflike(3, 4); 106244541Sbrooks 107244541Sbrooks#define REPLACEPTR(x,v) do { if ((x)) free((x)); (x) = (v); } while (0) 108244541Sbrooks 109244541SbrooksNODE * 110244541Sbrooksspec(FILE *fp) 111244541Sbrooks{ 112244541Sbrooks NODE *centry, *last, *pathparent, *cur; 113244541Sbrooks char *p, *e, *next; 114244541Sbrooks NODE ginfo, *root; 115244541Sbrooks char *buf, *tname, *ntname; 116244541Sbrooks size_t tnamelen, plen; 117244541Sbrooks 118244541Sbrooks root = NULL; 119244541Sbrooks centry = last = NULL; 120244541Sbrooks tname = NULL; 121244541Sbrooks tnamelen = 0; 122244541Sbrooks memset(&ginfo, 0, sizeof(ginfo)); 123244541Sbrooks for (mtree_lineno = 0; 124244541Sbrooks (buf = fparseln(fp, NULL, &mtree_lineno, NULL, 125244541Sbrooks FPARSELN_UNESCCOMM)); 126244541Sbrooks free(buf)) { 127244541Sbrooks /* Skip leading whitespace. */ 128244541Sbrooks for (p = buf; *p && isspace((unsigned char)*p); ++p) 129244541Sbrooks continue; 130244541Sbrooks 131244541Sbrooks /* If nothing but whitespace, continue. */ 132244541Sbrooks if (!*p) 133244541Sbrooks continue; 134244541Sbrooks 135244541Sbrooks#ifdef DEBUG 136244541Sbrooks fprintf(stderr, "line %lu: {%s}\n", 137244541Sbrooks (u_long)mtree_lineno, p); 138244541Sbrooks#endif 139244541Sbrooks /* Grab file name, "$", "set", or "unset". */ 140244541Sbrooks next = buf; 141244541Sbrooks while ((p = strsep(&next, " \t")) != NULL && *p == '\0') 142244541Sbrooks continue; 143244541Sbrooks if (p == NULL) 144244541Sbrooks mtree_err("missing field"); 145244541Sbrooks 146244541Sbrooks if (p[0] == '/') { 147244541Sbrooks if (strcmp(p + 1, "set") == 0) 148244541Sbrooks set(next, &ginfo); 149244541Sbrooks else if (strcmp(p + 1, "unset") == 0) 150244541Sbrooks unset(next, &ginfo); 151244541Sbrooks else 152244541Sbrooks mtree_err("invalid specification `%s'", p); 153244541Sbrooks continue; 154244541Sbrooks } 155244541Sbrooks 156244541Sbrooks if (strcmp(p, "..") == 0) { 157244541Sbrooks /* Don't go up, if haven't gone down. */ 158244541Sbrooks if (root == NULL) 159244541Sbrooks goto noparent; 160244541Sbrooks if (last->type != F_DIR || last->flags & F_DONE) { 161244541Sbrooks if (last == root) 162244541Sbrooks goto noparent; 163244541Sbrooks last = last->parent; 164244541Sbrooks } 165244541Sbrooks last->flags |= F_DONE; 166244541Sbrooks continue; 167244541Sbrooks 168244541Sbrooksnoparent: mtree_err("no parent node"); 169244541Sbrooks } 170244541Sbrooks 171244541Sbrooks plen = strlen(p) + 1; 172244541Sbrooks if (plen > tnamelen) { 173244541Sbrooks if ((ntname = realloc(tname, plen)) == NULL) 174244541Sbrooks mtree_err("realloc: %s", strerror(errno)); 175244541Sbrooks tname = ntname; 176244541Sbrooks tnamelen = plen; 177244541Sbrooks } 178244541Sbrooks if (strunvis(tname, p) == -1) 179244541Sbrooks mtree_err("strunvis failed on `%s'", p); 180244541Sbrooks p = tname; 181244541Sbrooks 182244541Sbrooks pathparent = NULL; 183244541Sbrooks if (strchr(p, '/') != NULL) { 184244541Sbrooks cur = root; 185244541Sbrooks for (; (e = strchr(p, '/')) != NULL; p = e+1) { 186244541Sbrooks if (p == e) 187244541Sbrooks continue; /* handle // */ 188244541Sbrooks *e = '\0'; 189244541Sbrooks if (strcmp(p, ".") != 0) { 190244541Sbrooks while (cur && 191244541Sbrooks strcmp(cur->name, p) != 0) { 192244541Sbrooks cur = cur->next; 193244541Sbrooks } 194244541Sbrooks } 195244541Sbrooks if (cur == NULL || cur->type != F_DIR) { 196244541Sbrooks mtree_err("%s: %s", tname, 197244541Sbrooks "missing directory in specification"); 198244541Sbrooks } 199244541Sbrooks *e = '/'; 200244541Sbrooks pathparent = cur; 201244541Sbrooks cur = cur->child; 202244541Sbrooks } 203244541Sbrooks if (*p == '\0') 204244541Sbrooks mtree_err("%s: empty leaf element", tname); 205244541Sbrooks } 206244541Sbrooks 207244541Sbrooks if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL) 208244541Sbrooks mtree_err("%s", strerror(errno)); 209244541Sbrooks *centry = ginfo; 210244541Sbrooks centry->lineno = mtree_lineno; 211244541Sbrooks strcpy(centry->name, p); 212244541Sbrooks#define MAGIC "?*[" 213244541Sbrooks if (strpbrk(p, MAGIC)) 214244541Sbrooks centry->flags |= F_MAGIC; 215244541Sbrooks set(next, centry); 216244541Sbrooks 217244541Sbrooks if (root == NULL) { 218244541Sbrooks /* 219244541Sbrooks * empty tree 220244541Sbrooks */ 221256687Sbrooks /* 222256687Sbrooks * Allow a bare "." root node by forcing it to 223256687Sbrooks * type=dir for compatibility with FreeBSD. 224256687Sbrooks */ 225256687Sbrooks if (strcmp(centry->name, ".") == 0 && centry->type == 0) 226256687Sbrooks centry->type = F_DIR; 227244541Sbrooks if (strcmp(centry->name, ".") != 0 || 228244541Sbrooks centry->type != F_DIR) 229244541Sbrooks mtree_err( 230244541Sbrooks "root node must be the directory `.'"); 231244541Sbrooks last = root = centry; 232244541Sbrooks root->parent = root; 233244541Sbrooks } else if (pathparent != NULL) { 234244541Sbrooks /* 235244541Sbrooks * full path entry; add or replace 236244541Sbrooks */ 237244541Sbrooks centry->parent = pathparent; 238244541Sbrooks addchild(pathparent, centry); 239244541Sbrooks last = centry; 240244541Sbrooks } else if (strcmp(centry->name, ".") == 0) { 241244541Sbrooks /* 242244541Sbrooks * duplicate "." entry; always replace 243244541Sbrooks */ 244244541Sbrooks replacenode(root, centry); 245244541Sbrooks } else if (last->type == F_DIR && !(last->flags & F_DONE)) { 246244541Sbrooks /* 247244541Sbrooks * new relative child in current dir; 248244541Sbrooks * add or replace 249244541Sbrooks */ 250244541Sbrooks centry->parent = last; 251244541Sbrooks addchild(last, centry); 252244541Sbrooks last = centry; 253244541Sbrooks } else { 254244541Sbrooks /* 255244541Sbrooks * new relative child in parent dir 256244541Sbrooks * (after encountering ".." entry); 257244541Sbrooks * add or replace 258244541Sbrooks */ 259244541Sbrooks centry->parent = last->parent; 260244541Sbrooks addchild(last->parent, centry); 261244541Sbrooks last = centry; 262244541Sbrooks } 263244541Sbrooks } 264244541Sbrooks return (root); 265244541Sbrooks} 266244541Sbrooks 267244541Sbrooksvoid 268244541Sbrooksfree_nodes(NODE *root) 269244541Sbrooks{ 270244541Sbrooks NODE *cur, *next; 271244541Sbrooks 272244541Sbrooks if (root == NULL) 273244541Sbrooks return; 274244541Sbrooks 275244541Sbrooks next = NULL; 276244541Sbrooks for (cur = root; cur != NULL; cur = next) { 277244541Sbrooks next = cur->next; 278244541Sbrooks free_nodes(cur->child); 279244541Sbrooks REPLACEPTR(cur->slink, NULL); 280244541Sbrooks REPLACEPTR(cur->md5digest, NULL); 281244541Sbrooks REPLACEPTR(cur->rmd160digest, NULL); 282244541Sbrooks REPLACEPTR(cur->sha1digest, NULL); 283244541Sbrooks REPLACEPTR(cur->sha256digest, NULL); 284244541Sbrooks REPLACEPTR(cur->sha384digest, NULL); 285244541Sbrooks REPLACEPTR(cur->sha512digest, NULL); 286244541Sbrooks REPLACEPTR(cur->tags, NULL); 287244541Sbrooks REPLACEPTR(cur, NULL); 288244541Sbrooks } 289244541Sbrooks} 290244541Sbrooks 291244541Sbrooks/* 292244541Sbrooks * appendfield -- 293273796Sbrooks * Like fprintf(), but output a space either before or after 294244541Sbrooks * the regular output, according to the pathlast flag. 295244541Sbrooks */ 296244541Sbrooksstatic int 297273796Sbrooksappendfield(FILE *fp, int pathlast, const char *fmt, ...) 298244541Sbrooks{ 299244541Sbrooks va_list ap; 300244541Sbrooks int result; 301244541Sbrooks 302244541Sbrooks va_start(ap, fmt); 303244541Sbrooks if (!pathlast) 304273796Sbrooks fprintf(fp, " "); 305244541Sbrooks result = vprintf(fmt, ap); 306244541Sbrooks if (pathlast) 307273796Sbrooks fprintf(fp, " "); 308244541Sbrooks va_end(ap); 309244541Sbrooks return result; 310244541Sbrooks} 311244541Sbrooks 312244541Sbrooks/* 313244541Sbrooks * dump_nodes -- 314244541Sbrooks * dump the NODEs from `cur', based in the directory `dir'. 315244541Sbrooks * if pathlast is none zero, print the path last, otherwise print 316244541Sbrooks * it first. 317244541Sbrooks */ 318244541Sbrooksvoid 319273796Sbrooksdump_nodes(FILE *fp, const char *dir, NODE *root, int pathlast) 320244541Sbrooks{ 321244541Sbrooks NODE *cur; 322244541Sbrooks char path[MAXPATHLEN]; 323244541Sbrooks const char *name; 324244541Sbrooks char *str; 325244541Sbrooks char *p, *q; 326244541Sbrooks 327244541Sbrooks for (cur = root; cur != NULL; cur = cur->next) { 328244541Sbrooks if (cur->type != F_DIR && !matchtags(cur)) 329244541Sbrooks continue; 330244541Sbrooks 331244541Sbrooks if (snprintf(path, sizeof(path), "%s%s%s", 332244541Sbrooks dir, *dir ? "/" : "", cur->name) 333244541Sbrooks >= (int)sizeof(path)) 334244541Sbrooks mtree_err("Pathname too long."); 335244541Sbrooks 336244541Sbrooks if (!pathlast) 337273796Sbrooks fprintf(fp, "%s", vispath(path)); 338244541Sbrooks 339244541Sbrooks#define MATCHFLAG(f) ((keys & (f)) && (cur->flags & (f))) 340244541Sbrooks if (MATCHFLAG(F_TYPE)) 341273796Sbrooks appendfield(fp, pathlast, "type=%s", 342273796Sbrooks nodetype(cur->type)); 343244541Sbrooks if (MATCHFLAG(F_UID | F_UNAME)) { 344244541Sbrooks if (keys & F_UNAME && 345244541Sbrooks (name = user_from_uid(cur->st_uid, 1)) != NULL) 346273796Sbrooks appendfield(fp, pathlast, "uname=%s", name); 347244541Sbrooks else 348273796Sbrooks appendfield(fp, pathlast, "uid=%u", 349273796Sbrooks cur->st_uid); 350244541Sbrooks } 351244541Sbrooks if (MATCHFLAG(F_GID | F_GNAME)) { 352244541Sbrooks if (keys & F_GNAME && 353244541Sbrooks (name = group_from_gid(cur->st_gid, 1)) != NULL) 354273796Sbrooks appendfield(fp, pathlast, "gname=%s", name); 355244541Sbrooks else 356273796Sbrooks appendfield(fp, pathlast, "gid=%u", 357273796Sbrooks cur->st_gid); 358244541Sbrooks } 359244541Sbrooks if (MATCHFLAG(F_MODE)) 360273796Sbrooks appendfield(fp, pathlast, "mode=%#o", cur->st_mode); 361244541Sbrooks if (MATCHFLAG(F_DEV) && 362244541Sbrooks (cur->type == F_BLOCK || cur->type == F_CHAR)) 363273796Sbrooks appendfield(fp, pathlast, "device=%#jx", 364256687Sbrooks (uintmax_t)cur->st_rdev); 365244541Sbrooks if (MATCHFLAG(F_NLINK)) 366312072Skib appendfield(fp, pathlast, "nlink=%ju", 367312072Skib (uintmax_t)cur->st_nlink); 368244541Sbrooks if (MATCHFLAG(F_SLINK)) 369273796Sbrooks appendfield(fp, pathlast, "link=%s", 370273796Sbrooks vispath(cur->slink)); 371244541Sbrooks if (MATCHFLAG(F_SIZE)) 372273796Sbrooks appendfield(fp, pathlast, "size=%ju", 373256687Sbrooks (uintmax_t)cur->st_size); 374244541Sbrooks if (MATCHFLAG(F_TIME)) 375273796Sbrooks appendfield(fp, pathlast, "time=%jd.%09ld", 376256687Sbrooks (intmax_t)cur->st_mtimespec.tv_sec, 377244541Sbrooks cur->st_mtimespec.tv_nsec); 378244541Sbrooks if (MATCHFLAG(F_CKSUM)) 379273796Sbrooks appendfield(fp, pathlast, "cksum=%lu", cur->cksum); 380244541Sbrooks if (MATCHFLAG(F_MD5)) 381273796Sbrooks appendfield(fp, pathlast, "%s=%s", MD5KEY, 382273796Sbrooks cur->md5digest); 383244541Sbrooks if (MATCHFLAG(F_RMD160)) 384273796Sbrooks appendfield(fp, pathlast, "%s=%s", RMD160KEY, 385244541Sbrooks cur->rmd160digest); 386244541Sbrooks if (MATCHFLAG(F_SHA1)) 387273796Sbrooks appendfield(fp, pathlast, "%s=%s", SHA1KEY, 388244541Sbrooks cur->sha1digest); 389244541Sbrooks if (MATCHFLAG(F_SHA256)) 390273796Sbrooks appendfield(fp, pathlast, "%s=%s", SHA256KEY, 391244541Sbrooks cur->sha256digest); 392244541Sbrooks if (MATCHFLAG(F_SHA384)) 393273796Sbrooks appendfield(fp, pathlast, "%s=%s", SHA384KEY, 394244541Sbrooks cur->sha384digest); 395244541Sbrooks if (MATCHFLAG(F_SHA512)) 396273796Sbrooks appendfield(fp, pathlast, "%s=%s", SHA512KEY, 397244541Sbrooks cur->sha512digest); 398244541Sbrooks if (MATCHFLAG(F_FLAGS)) { 399244541Sbrooks str = flags_to_string(cur->st_flags, "none"); 400273796Sbrooks appendfield(fp, pathlast, "flags=%s", str); 401244541Sbrooks free(str); 402244541Sbrooks } 403244541Sbrooks if (MATCHFLAG(F_IGN)) 404273796Sbrooks appendfield(fp, pathlast, "ignore"); 405244541Sbrooks if (MATCHFLAG(F_OPT)) 406273796Sbrooks appendfield(fp, pathlast, "optional"); 407244541Sbrooks if (MATCHFLAG(F_TAGS)) { 408244541Sbrooks /* don't output leading or trailing commas */ 409244541Sbrooks p = cur->tags; 410244541Sbrooks while (*p == ',') 411244541Sbrooks p++; 412244541Sbrooks q = p + strlen(p); 413244541Sbrooks while(q > p && q[-1] == ',') 414244541Sbrooks q--; 415273796Sbrooks appendfield(fp, pathlast, "tags=%.*s", (int)(q - p), p); 416244541Sbrooks } 417244541Sbrooks puts(pathlast ? vispath(path) : ""); 418244541Sbrooks 419244541Sbrooks if (cur->child) 420273796Sbrooks dump_nodes(fp, path, cur->child, pathlast); 421244541Sbrooks } 422244541Sbrooks} 423244541Sbrooks 424244541Sbrooks/* 425244541Sbrooks * vispath -- 426244541Sbrooks * strsvis(3) encodes path, which must not be longer than MAXPATHLEN 427244541Sbrooks * characters long, and returns a pointer to a static buffer containing 428244541Sbrooks * the result. 429244541Sbrooks */ 430244541Sbrookschar * 431244541Sbrooksvispath(const char *path) 432244541Sbrooks{ 433244541Sbrooks static const char extra[] = { ' ', '\t', '\n', '\\', '#', '\0' }; 434244541Sbrooks static const char extra_glob[] = { ' ', '\t', '\n', '\\', '#', '*', 435244541Sbrooks '?', '[', '\0' }; 436244541Sbrooks static char pathbuf[4*MAXPATHLEN + 1]; 437244541Sbrooks 438244541Sbrooks if (flavor == F_NETBSD6) 439244541Sbrooks strsvis(pathbuf, path, VIS_CSTYLE, extra); 440244541Sbrooks else 441244541Sbrooks strsvis(pathbuf, path, VIS_OCTAL, extra_glob); 442244541Sbrooks return pathbuf; 443244541Sbrooks} 444244541Sbrooks 445244541Sbrooks 446244541Sbrooksstatic dev_t 447244541Sbrooksparsedev(char *arg) 448244541Sbrooks{ 449244541Sbrooks#define MAX_PACK_ARGS 3 450244541Sbrooks u_long numbers[MAX_PACK_ARGS]; 451244541Sbrooks char *p, *ep, *dev; 452244541Sbrooks int argc; 453244541Sbrooks pack_t *pack; 454244541Sbrooks dev_t result; 455244541Sbrooks const char *error = NULL; 456244541Sbrooks 457244541Sbrooks if ((dev = strchr(arg, ',')) != NULL) { 458244541Sbrooks *dev++='\0'; 459244541Sbrooks if ((pack = pack_find(arg)) == NULL) 460244541Sbrooks mtree_err("unknown format `%s'", arg); 461244541Sbrooks argc = 0; 462244541Sbrooks while ((p = strsep(&dev, ",")) != NULL) { 463244541Sbrooks if (*p == '\0') 464244541Sbrooks mtree_err("missing number"); 465244541Sbrooks numbers[argc++] = strtoul(p, &ep, 0); 466244541Sbrooks if (*ep != '\0') 467244541Sbrooks mtree_err("invalid number `%s'", 468244541Sbrooks p); 469244541Sbrooks if (argc > MAX_PACK_ARGS) 470244541Sbrooks mtree_err("too many arguments"); 471244541Sbrooks } 472244541Sbrooks if (argc < 2) 473244541Sbrooks mtree_err("not enough arguments"); 474244541Sbrooks result = (*pack)(argc, numbers, &error); 475244541Sbrooks if (error != NULL) 476244541Sbrooks mtree_err("%s", error); 477244541Sbrooks } else { 478244541Sbrooks result = (dev_t)strtoul(arg, &ep, 0); 479244541Sbrooks if (*ep != '\0') 480244541Sbrooks mtree_err("invalid device `%s'", arg); 481244541Sbrooks } 482244541Sbrooks return (result); 483244541Sbrooks} 484244541Sbrooks 485244541Sbrooksstatic void 486244541Sbrooksreplacenode(NODE *cur, NODE *new) 487244541Sbrooks{ 488244541Sbrooks 489244541Sbrooks#define REPLACE(x) cur->x = new->x 490244541Sbrooks#define REPLACESTR(x) REPLACEPTR(cur->x,new->x) 491244541Sbrooks 492244541Sbrooks if (cur->type != new->type) { 493244541Sbrooks if (mtree_Mflag) { 494244541Sbrooks /* 495244541Sbrooks * merge entries with different types; we 496244541Sbrooks * don't want children retained in this case. 497244541Sbrooks */ 498244541Sbrooks REPLACE(type); 499244541Sbrooks free_nodes(cur->child); 500244541Sbrooks cur->child = NULL; 501244541Sbrooks } else { 502244541Sbrooks mtree_err( 503244541Sbrooks "existing entry for `%s', type `%s'" 504244541Sbrooks " does not match type `%s'", 505244541Sbrooks cur->name, nodetype(cur->type), 506244541Sbrooks nodetype(new->type)); 507244541Sbrooks } 508244541Sbrooks } 509244541Sbrooks 510244541Sbrooks REPLACE(st_size); 511244541Sbrooks REPLACE(st_mtimespec); 512244541Sbrooks REPLACESTR(slink); 513244541Sbrooks if (cur->slink != NULL) { 514244541Sbrooks if ((cur->slink = strdup(new->slink)) == NULL) 515244541Sbrooks mtree_err("memory allocation error"); 516244541Sbrooks if (strunvis(cur->slink, new->slink) == -1) 517244541Sbrooks mtree_err("strunvis failed on `%s'", new->slink); 518244541Sbrooks free(new->slink); 519244541Sbrooks } 520244541Sbrooks REPLACE(st_uid); 521244541Sbrooks REPLACE(st_gid); 522244541Sbrooks REPLACE(st_mode); 523244541Sbrooks REPLACE(st_rdev); 524244541Sbrooks REPLACE(st_flags); 525244541Sbrooks REPLACE(st_nlink); 526244541Sbrooks REPLACE(cksum); 527244541Sbrooks REPLACESTR(md5digest); 528244541Sbrooks REPLACESTR(rmd160digest); 529244541Sbrooks REPLACESTR(sha1digest); 530244541Sbrooks REPLACESTR(sha256digest); 531244541Sbrooks REPLACESTR(sha384digest); 532244541Sbrooks REPLACESTR(sha512digest); 533244541Sbrooks REPLACESTR(tags); 534244541Sbrooks REPLACE(lineno); 535244541Sbrooks REPLACE(flags); 536244541Sbrooks free(new); 537244541Sbrooks} 538244541Sbrooks 539244541Sbrooksstatic void 540244541Sbrooksset(char *t, NODE *ip) 541244541Sbrooks{ 542244541Sbrooks int type, value, len; 543244541Sbrooks gid_t gid; 544244541Sbrooks uid_t uid; 545244541Sbrooks char *kw, *val, *md, *ep; 546244541Sbrooks void *m; 547244541Sbrooks 548244541Sbrooks while ((kw = strsep(&t, "= \t")) != NULL) { 549244541Sbrooks if (*kw == '\0') 550244541Sbrooks continue; 551244541Sbrooks if (strcmp(kw, "all") == 0) 552244541Sbrooks mtree_err("invalid keyword `all'"); 553244541Sbrooks ip->flags |= type = parsekey(kw, &value); 554244541Sbrooks if (!value) 555244541Sbrooks /* Just set flag bit (F_IGN and F_OPT) */ 556244541Sbrooks continue; 557244541Sbrooks while ((val = strsep(&t, " \t")) != NULL && *val == '\0') 558244541Sbrooks continue; 559244541Sbrooks if (val == NULL) 560244541Sbrooks mtree_err("missing value"); 561244541Sbrooks switch (type) { 562244541Sbrooks case F_CKSUM: 563244541Sbrooks ip->cksum = strtoul(val, &ep, 10); 564244541Sbrooks if (*ep) 565244541Sbrooks mtree_err("invalid checksum `%s'", val); 566244541Sbrooks break; 567244541Sbrooks case F_DEV: 568244541Sbrooks ip->st_rdev = parsedev(val); 569244541Sbrooks break; 570244541Sbrooks case F_FLAGS: 571244541Sbrooks if (strcmp("none", val) == 0) 572244541Sbrooks ip->st_flags = 0; 573244541Sbrooks else if (string_to_flags(&val, &ip->st_flags, NULL) 574244541Sbrooks != 0) 575244541Sbrooks mtree_err("invalid flag `%s'", val); 576244541Sbrooks break; 577244541Sbrooks case F_GID: 578244541Sbrooks ip->st_gid = (gid_t)strtoul(val, &ep, 10); 579244541Sbrooks if (*ep) 580244541Sbrooks mtree_err("invalid gid `%s'", val); 581244541Sbrooks break; 582244541Sbrooks case F_GNAME: 583244541Sbrooks if (mtree_Wflag) /* don't parse if whacking */ 584244541Sbrooks break; 585244541Sbrooks if (gid_from_group(val, &gid) == -1) 586244541Sbrooks mtree_err("unknown group `%s'", val); 587244541Sbrooks ip->st_gid = gid; 588244541Sbrooks break; 589244541Sbrooks case F_MD5: 590244541Sbrooks if (val[0]=='0' && val[1]=='x') 591244541Sbrooks md=&val[2]; 592244541Sbrooks else 593244541Sbrooks md=val; 594244541Sbrooks if ((ip->md5digest = strdup(md)) == NULL) 595244541Sbrooks mtree_err("memory allocation error"); 596244541Sbrooks break; 597244541Sbrooks case F_MODE: 598244541Sbrooks if ((m = setmode(val)) == NULL) 599244541Sbrooks mtree_err("cannot set file mode `%s' (%s)", 600244541Sbrooks val, strerror(errno)); 601244541Sbrooks ip->st_mode = getmode(m, 0); 602244541Sbrooks free(m); 603244541Sbrooks break; 604244541Sbrooks case F_NLINK: 605244541Sbrooks ip->st_nlink = (nlink_t)strtoul(val, &ep, 10); 606244541Sbrooks if (*ep) 607244541Sbrooks mtree_err("invalid link count `%s'", val); 608244541Sbrooks break; 609244541Sbrooks case F_RMD160: 610244541Sbrooks if (val[0]=='0' && val[1]=='x') 611244541Sbrooks md=&val[2]; 612244541Sbrooks else 613244541Sbrooks md=val; 614244541Sbrooks if ((ip->rmd160digest = strdup(md)) == NULL) 615244541Sbrooks mtree_err("memory allocation error"); 616244541Sbrooks break; 617244541Sbrooks case F_SHA1: 618244541Sbrooks if (val[0]=='0' && val[1]=='x') 619244541Sbrooks md=&val[2]; 620244541Sbrooks else 621244541Sbrooks md=val; 622244541Sbrooks if ((ip->sha1digest = strdup(md)) == NULL) 623244541Sbrooks mtree_err("memory allocation error"); 624244541Sbrooks break; 625244541Sbrooks case F_SIZE: 626244541Sbrooks ip->st_size = (off_t)strtoll(val, &ep, 10); 627244541Sbrooks if (*ep) 628244541Sbrooks mtree_err("invalid size `%s'", val); 629244541Sbrooks break; 630244541Sbrooks case F_SLINK: 631244541Sbrooks if ((ip->slink = strdup(val)) == NULL) 632244541Sbrooks mtree_err("memory allocation error"); 633244541Sbrooks if (strunvis(ip->slink, val) == -1) 634244541Sbrooks mtree_err("strunvis failed on `%s'", val); 635244541Sbrooks break; 636244541Sbrooks case F_TAGS: 637244541Sbrooks len = strlen(val) + 3; /* "," + str + ",\0" */ 638244541Sbrooks if ((ip->tags = malloc(len)) == NULL) 639244541Sbrooks mtree_err("memory allocation error"); 640244541Sbrooks snprintf(ip->tags, len, ",%s,", val); 641244541Sbrooks break; 642244541Sbrooks case F_TIME: 643244541Sbrooks ip->st_mtimespec.tv_sec = 644244541Sbrooks (time_t)strtoll(val, &ep, 10); 645244541Sbrooks if (*ep != '.') 646244541Sbrooks mtree_err("invalid time `%s'", val); 647244541Sbrooks val = ep + 1; 648244541Sbrooks ip->st_mtimespec.tv_nsec = strtol(val, &ep, 10); 649244541Sbrooks if (*ep) 650244541Sbrooks mtree_err("invalid time `%s'", val); 651244541Sbrooks break; 652244541Sbrooks case F_TYPE: 653244541Sbrooks ip->type = parsetype(val); 654244541Sbrooks break; 655244541Sbrooks case F_UID: 656244541Sbrooks ip->st_uid = (uid_t)strtoul(val, &ep, 10); 657244541Sbrooks if (*ep) 658244541Sbrooks mtree_err("invalid uid `%s'", val); 659244541Sbrooks break; 660244541Sbrooks case F_UNAME: 661244541Sbrooks if (mtree_Wflag) /* don't parse if whacking */ 662244541Sbrooks break; 663244541Sbrooks if (uid_from_user(val, &uid) == -1) 664244541Sbrooks mtree_err("unknown user `%s'", val); 665244541Sbrooks ip->st_uid = uid; 666244541Sbrooks break; 667244541Sbrooks case F_SHA256: 668244541Sbrooks if (val[0]=='0' && val[1]=='x') 669244541Sbrooks md=&val[2]; 670244541Sbrooks else 671244541Sbrooks md=val; 672244541Sbrooks if ((ip->sha256digest = strdup(md)) == NULL) 673244541Sbrooks mtree_err("memory allocation error"); 674244541Sbrooks break; 675244541Sbrooks case F_SHA384: 676244541Sbrooks if (val[0]=='0' && val[1]=='x') 677244541Sbrooks md=&val[2]; 678244541Sbrooks else 679244541Sbrooks md=val; 680244541Sbrooks if ((ip->sha384digest = strdup(md)) == NULL) 681244541Sbrooks mtree_err("memory allocation error"); 682244541Sbrooks break; 683244541Sbrooks case F_SHA512: 684244541Sbrooks if (val[0]=='0' && val[1]=='x') 685244541Sbrooks md=&val[2]; 686244541Sbrooks else 687244541Sbrooks md=val; 688244541Sbrooks if ((ip->sha512digest = strdup(md)) == NULL) 689244541Sbrooks mtree_err("memory allocation error"); 690244541Sbrooks break; 691244541Sbrooks default: 692244541Sbrooks mtree_err( 693244541Sbrooks "set(): unsupported key type 0x%x (INTERNAL ERROR)", 694244541Sbrooks type); 695244541Sbrooks /* NOTREACHED */ 696244541Sbrooks } 697244541Sbrooks } 698244541Sbrooks} 699244541Sbrooks 700244541Sbrooksstatic void 701244541Sbrooksunset(char *t, NODE *ip) 702244541Sbrooks{ 703244541Sbrooks char *p; 704244541Sbrooks 705244541Sbrooks while ((p = strsep(&t, " \t")) != NULL) { 706244541Sbrooks if (*p == '\0') 707244541Sbrooks continue; 708244541Sbrooks ip->flags &= ~parsekey(p, NULL); 709244541Sbrooks } 710244541Sbrooks} 711244541Sbrooks 712244541Sbrooks/* 713244541Sbrooks * addchild -- 714244541Sbrooks * Add the centry node as a child of the pathparent node. If 715244541Sbrooks * centry is a duplicate, call replacenode(). If centry is not 716244541Sbrooks * a duplicate, insert it into the linked list referenced by 717244541Sbrooks * pathparent->child. Keep the list sorted if Sflag is set. 718244541Sbrooks */ 719244541Sbrooksstatic void 720244541Sbrooksaddchild(NODE *pathparent, NODE *centry) 721244541Sbrooks{ 722244541Sbrooks NODE *samename; /* node with the same name as centry */ 723244541Sbrooks NODE *replacepos; /* if non-NULL, centry should replace this node */ 724244541Sbrooks NODE *insertpos; /* if non-NULL, centry should be inserted 725244541Sbrooks * after this node */ 726244541Sbrooks NODE *cur; /* for stepping through the list */ 727244541Sbrooks NODE *last; /* the last node in the list */ 728244541Sbrooks int cmp; 729244541Sbrooks 730244541Sbrooks samename = NULL; 731244541Sbrooks replacepos = NULL; 732244541Sbrooks insertpos = NULL; 733244541Sbrooks last = NULL; 734244541Sbrooks cur = pathparent->child; 735244541Sbrooks if (cur == NULL) { 736244541Sbrooks /* centry is pathparent's first and only child node so far */ 737244541Sbrooks pathparent->child = centry; 738244541Sbrooks return; 739244541Sbrooks } 740244541Sbrooks 741244541Sbrooks /* 742244541Sbrooks * pathparent already has at least one other child, so add the 743244541Sbrooks * centry node to the list. 744244541Sbrooks * 745244541Sbrooks * We first scan through the list looking for an existing node 746244541Sbrooks * with the same name (setting samename), and also looking 747244541Sbrooks * for the correct position to replace or insert the new node 748244541Sbrooks * (setting replacepos and/or insertpos). 749244541Sbrooks */ 750244541Sbrooks for (; cur != NULL; last = cur, cur = cur->next) { 751244541Sbrooks if (strcmp(centry->name, cur->name) == 0) { 752244541Sbrooks samename = cur; 753244541Sbrooks } 754244541Sbrooks if (mtree_Sflag) { 755244541Sbrooks cmp = nodecmp(centry, cur); 756244541Sbrooks if (cmp == 0) { 757244541Sbrooks replacepos = cur; 758244541Sbrooks } else if (cmp > 0) { 759244541Sbrooks insertpos = cur; 760244541Sbrooks } 761244541Sbrooks } 762244541Sbrooks } 763244541Sbrooks if (! mtree_Sflag) { 764244541Sbrooks if (samename != NULL) { 765244541Sbrooks /* replace node with same name */ 766244541Sbrooks replacepos = samename; 767244541Sbrooks } else { 768244541Sbrooks /* add new node at end of list */ 769244541Sbrooks insertpos = last; 770244541Sbrooks } 771244541Sbrooks } 772244541Sbrooks 773244541Sbrooks if (samename != NULL) { 774244541Sbrooks /* 775244541Sbrooks * We found a node with the same name above. Call 776244541Sbrooks * replacenode(), which will either exit with an error, 777244541Sbrooks * or replace the information in the samename node and 778244541Sbrooks * free the information in the centry node. 779244541Sbrooks */ 780244541Sbrooks replacenode(samename, centry); 781244541Sbrooks if (samename == replacepos) { 782244541Sbrooks /* The just-replaced node was in the correct position */ 783244541Sbrooks return; 784244541Sbrooks } 785244541Sbrooks if (samename == insertpos || samename->prev == insertpos) { 786244541Sbrooks /* 787244541Sbrooks * We thought the new node should be just before 788244541Sbrooks * or just after the replaced node, but that would 789244541Sbrooks * be equivalent to just retaining the replaced node. 790244541Sbrooks */ 791244541Sbrooks return; 792244541Sbrooks } 793244541Sbrooks 794244541Sbrooks /* 795244541Sbrooks * The just-replaced node is in the wrong position in 796244541Sbrooks * the list. This can happen if sort order depends on 797244541Sbrooks * criteria other than the node name. 798244541Sbrooks * 799244541Sbrooks * Make centry point to the just-replaced node. Unlink 800244541Sbrooks * the just-replaced node from the list, and allow it to 801244541Sbrooks * be insterted in the correct position later. 802244541Sbrooks */ 803244541Sbrooks centry = samename; 804244541Sbrooks if (centry->prev) 805244541Sbrooks centry->prev->next = centry->next; 806244541Sbrooks else { 807244541Sbrooks /* centry->next is the new head of the list */ 808244541Sbrooks pathparent->child = centry->next; 809244541Sbrooks assert(centry->next != NULL); 810244541Sbrooks } 811244541Sbrooks if (centry->next) 812244541Sbrooks centry->next->prev = centry->prev; 813244541Sbrooks centry->prev = NULL; 814244541Sbrooks centry->next = NULL; 815244541Sbrooks } 816244541Sbrooks 817244541Sbrooks if (insertpos == NULL) { 818244541Sbrooks /* insert centry at the beginning of the list */ 819244541Sbrooks pathparent->child->prev = centry; 820244541Sbrooks centry->next = pathparent->child; 821244541Sbrooks centry->prev = NULL; 822244541Sbrooks pathparent->child = centry; 823244541Sbrooks } else { 824244541Sbrooks /* insert centry into the list just after insertpos */ 825244541Sbrooks centry->next = insertpos->next; 826244541Sbrooks insertpos->next = centry; 827244541Sbrooks centry->prev = insertpos; 828244541Sbrooks if (centry->next) 829244541Sbrooks centry->next->prev = centry; 830244541Sbrooks } 831244541Sbrooks return; 832244541Sbrooks} 833244541Sbrooks 834244541Sbrooks/* 835244541Sbrooks * nodecmp -- 836244541Sbrooks * used as a comparison function by addchild() to control the order 837244541Sbrooks * in which entries appear within a list of sibling nodes. We make 838244541Sbrooks * directories sort after non-directories, but otherwise sort in 839244541Sbrooks * strcmp() order. 840244541Sbrooks * 841244541Sbrooks * Keep this in sync with dcmp() in create.c. 842244541Sbrooks */ 843244541Sbrooksstatic int 844244541Sbrooksnodecmp(const NODE *a, const NODE *b) 845244541Sbrooks{ 846244541Sbrooks 847244541Sbrooks if ((a->type & F_DIR) != 0) { 848244541Sbrooks if ((b->type & F_DIR) == 0) 849244541Sbrooks return 1; 850244541Sbrooks } else if ((b->type & F_DIR) != 0) 851244541Sbrooks return -1; 852244541Sbrooks return strcmp(a->name, b->name); 853244541Sbrooks} 854