spec.c revision 1.31
1/* $NetBSD: spec.c,v 1.31 2001/10/05 15:32:57 lukem Exp $ */ 2 3/*- 4 * Copyright (c) 1989, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36/*- 37 * Copyright (c) 2001 The NetBSD Foundation, Inc. 38 * All rights reserved. 39 * 40 * This code is derived from software contributed to The NetBSD Foundation 41 * by Luke Mewburn of Wasabi Systems. 42 * 43 * Redistribution and use in source and binary forms, with or without 44 * modification, are permitted provided that the following conditions 45 * are met: 46 * 1. Redistributions of source code must retain the above copyright 47 * notice, this list of conditions and the following disclaimer. 48 * 2. Redistributions in binary form must reproduce the above copyright 49 * notice, this list of conditions and the following disclaimer in the 50 * documentation and/or other materials provided with the distribution. 51 * 3. All advertising materials mentioning features or use of this software 52 * must display the following acknowledgement: 53 * This product includes software developed by the NetBSD 54 * Foundation, Inc. and its contributors. 55 * 4. Neither the name of The NetBSD Foundation nor the names of its 56 * contributors may be used to endorse or promote products derived 57 * from this software without specific prior written permission. 58 * 59 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 60 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 61 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 62 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 63 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 64 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 65 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 66 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 67 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 68 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 69 * POSSIBILITY OF SUCH DAMAGE. 70 */ 71 72#include <sys/cdefs.h> 73#ifndef lint 74#if 0 75static char sccsid[] = "@(#)spec.c 8.2 (Berkeley) 4/28/95"; 76#else 77__RCSID("$NetBSD: spec.c,v 1.31 2001/10/05 15:32:57 lukem Exp $"); 78#endif 79#endif /* not lint */ 80 81#include <sys/param.h> 82#include <sys/stat.h> 83 84#include <ctype.h> 85#include <errno.h> 86#include <fts.h> 87#include <grp.h> 88#include <pwd.h> 89#include <stdio.h> 90#include <string.h> 91#include <unistd.h> 92#include <util.h> 93#include <vis.h> 94 95#include "mtree.h" 96#include "extern.h" 97 98size_t lineno; /* Current spec line number. */ 99 100static void set(char *, NODE *); 101static void unset(char *, NODE *); 102 103NODE * 104spec(void) 105{ 106 NODE *centry, *last, *pathparent, *cur; 107 char *p, *e; 108 NODE ginfo, *root; 109 char *buf, *tname; 110 size_t tnamelen, plen; 111 112 root = NULL; 113 centry = last = NULL; 114 tname = NULL; 115 tnamelen = 0; 116 memset(&ginfo, 0, sizeof(ginfo)); 117 for (lineno = 0; 118 (buf = fparseln(stdin, NULL, &lineno, NULL, 119 FPARSELN_UNESCCOMM | FPARSELN_UNESCCONT | FPARSELN_UNESCESC)); 120 free(buf)) { 121 /* Skip leading whitespace. */ 122 for (p = buf; *p && isspace((unsigned char)*p); ++p) 123 continue; 124 125 /* If nothing but whitespace, continue. */ 126 if (!*p) 127 continue; 128 129#ifdef DEBUG 130 (void)fprintf(stderr, "line %d: {%s}\n", lineno, p); 131#endif 132 133 /* Grab file name, "$", "set", or "unset". */ 134 if ((p = strtok(p, "\n\t ")) == NULL) 135 mtree_err("missing field"); 136 137 if (p[0] == '/') { 138 if (strcmp(p + 1, "set") == 0) 139 set(NULL, &ginfo); 140 else if (strcmp(p + 1, "unset") == 0) 141 unset(NULL, &ginfo); 142 else 143 mtree_err("invalid specification `%s'", p); 144 continue; 145 } 146 147 if (strcmp(p, "..") == 0) { 148 /* Don't go up, if haven't gone down. */ 149 if (root == NULL) 150 goto noparent; 151 if (last->type != F_DIR || last->flags & F_DONE) { 152 if (last == root) 153 goto noparent; 154 last = last->parent; 155 } 156 last->flags |= F_DONE; 157 continue; 158 159noparent: mtree_err("no parent node"); 160 } 161 162 plen = strlen(p) + 1; 163 if (plen > tnamelen) { 164 tnamelen = plen; 165 if ((tname = realloc(tname, tnamelen)) == NULL) 166 mtree_err("realloc: %s", strerror(errno)); 167 } 168 if (strunvis(tname, p) == -1) 169 mtree_err("strunvis failed on `%s'", p); 170 p = tname; 171 172 pathparent = NULL; 173 if (strchr(p, '/') != NULL) { 174 cur = root; 175 for (; (e = strchr(p, '/')) != NULL; p = e+1) { 176 if (p == e) 177 continue; /* handle // */ 178 *e = '\0'; 179 if (strcmp(p, ".") != 0) { 180 while (cur && strcmp(cur->name, p)) { 181 cur = cur->next; 182 } 183 } 184 if (cur == NULL || cur->type != F_DIR) { 185 mtree_err("%s: %s", tname, 186 strerror(ENOENT)); 187 } 188 *e = '/'; 189 pathparent = cur; 190 cur = cur->child; 191 } 192 if (*p == '\0') 193 mtree_err("%s: empty leaf element", tname); 194 for (; cur != NULL; cur = cur->next) { 195 if (strcmp(cur->name, p) == 0) 196 mtree_err("%s: %s", p, 197 strerror(EEXIST)); 198 } 199 } 200 201 if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL) 202 mtree_err("%s", strerror(errno)); 203 *centry = ginfo; 204 strcpy(centry->name, p); 205#define MAGIC "?*[" 206 if (strpbrk(p, MAGIC)) 207 centry->flags |= F_MAGIC; 208 set(NULL, centry); 209 210 if (root == NULL) { 211 last = root = centry; 212 root->parent = root; 213 } else if (pathparent != NULL) { 214 centry->parent = pathparent; 215 cur = pathparent->child; 216 if (cur == NULL) 217 pathparent->child = centry; 218 else { 219 while (cur->next != NULL) 220 cur = cur->next; 221 cur->next = centry; 222 centry->prev = cur; 223 } 224 last = centry; 225 } else if (last->type == F_DIR && !(last->flags & F_DONE)) { 226 centry->parent = last; 227 last = last->child = centry; 228 } else { 229 centry->parent = last->parent; 230 centry->prev = last; 231 last = last->next = centry; 232 } 233 } 234 return (root); 235} 236 237/* 238 * dump_nodes -- 239 * dump the NODEs from `cur', based in the directory `dir' 240 */ 241void 242dump_nodes(const char *dir, NODE *root) 243{ 244 NODE *cur; 245 char path[MAXPATHLEN + 1]; 246 const char *name; 247 248 for (cur = root; cur != NULL; cur = cur->next) { 249 if (cur->type != F_DIR && !matchtags(cur)) 250 continue; 251 252 if (snprintf(path, sizeof(path), "%s%s%s", 253 dir, *dir ? "/" : "", cur->name) 254 >= sizeof(path)) 255 mtree_err("Pathname too long."); 256 257#define MATCHFLAG(f) ((keys & (f)) && (cur->flags & (f))) 258 if (MATCHFLAG(F_TYPE)) 259 printf("type=%s ", nodetype(cur->type)); 260 if (MATCHFLAG(F_UID | F_UNAME)) { 261 if (keys & F_UNAME && 262 (name = user_from_uid(cur->st_uid, 1)) != NULL) 263 printf("uname=%s ", name); 264 else 265 printf("uid=%u ", cur->st_uid); 266 } 267 if (MATCHFLAG(F_GID | F_GNAME)) { 268 if (keys & F_GNAME && 269 (name = group_from_gid(cur->st_gid, 1)) != NULL) 270 printf("gname=%s ", name); 271 else 272 printf("gid=%u ", cur->st_gid); 273 } 274 if (MATCHFLAG(F_MODE)) 275 printf("mode=%#o ", cur->st_mode); 276 if (MATCHFLAG(F_NLINK)) 277 printf("nlink=%d ", cur->st_nlink); 278 if (MATCHFLAG(F_SLINK)) 279 printf("link=%s ", cur->slink); 280 if (MATCHFLAG(F_SIZE)) 281 printf("size=%lld ", (long long)cur->st_size); 282 if (MATCHFLAG(F_TIME)) 283 printf("time=%ld.%ld ", (long)cur->st_mtimespec.tv_sec, 284 cur->st_mtimespec.tv_nsec); 285 if (MATCHFLAG(F_CKSUM)) 286 printf("cksum=%lu ", cur->cksum); 287 if (MATCHFLAG(F_MD5)) 288 printf("md5=%s ", cur->md5sum); 289 if (MATCHFLAG(F_FLAGS)) 290 printf("flags=%s ", 291 flags_to_string(cur->st_flags, "none")); 292 if (MATCHFLAG(F_IGN)) 293 printf("ignore "); 294 if (MATCHFLAG(F_OPT)) 295 printf("optional "); 296 if (MATCHFLAG(F_TAGS)) 297 printf("tags=%s ", cur->tags); 298 puts(path); 299 300 if (cur->child) 301 dump_nodes(path, cur->child); 302 } 303} 304 305static void 306set(char *t, NODE *ip) 307{ 308 int type; 309 gid_t gid; 310 uid_t uid; 311 char *kw, *val, *md; 312 void *m; 313 int value, len; 314 char *ep; 315 316 val = NULL; 317 for (; (kw = strtok(t, "= \t\n")) != NULL; t = NULL) { 318 if (strcmp(kw, "all") == 0) 319 mtree_err("invalid keyword `all'"); 320 ip->flags |= type = parsekey(kw, &value); 321 if (value && (val = strtok(NULL, " \t\n")) == NULL) 322 mtree_err("missing value"); 323 switch(type) { 324 case F_CKSUM: 325 ip->cksum = strtoul(val, &ep, 10); 326 if (*ep) 327 mtree_err("invalid checksum `%s'", val); 328 break; 329 case F_FLAGS: 330 if (strcmp("none", val) == 0) 331 ip->st_flags = 0; 332 else if (string_to_flags(&val, &ip->st_flags, NULL) != 0) 333 mtree_err("invalid flag `%s'", val); 334 break; 335 case F_GID: 336 ip->st_gid = (gid_t)strtoul(val, &ep, 10); 337 if (*ep) 338 mtree_err("invalid gid `%s'", val); 339 break; 340 case F_GNAME: 341 if (gid_from_group(val, &gid) == -1) 342 mtree_err("unknown group `%s'", val); 343 ip->st_gid = gid; 344 break; 345 case F_IGN: 346 case F_OPT: 347 /* just set flag bit */ 348 break; 349 case F_MD5: 350 if (val[0]=='0' && val[1]=='x') 351 md=&val[2]; 352 else 353 md=val; 354 if ((ip->md5sum = strdup(md)) == NULL) 355 mtree_err("memory allocation error"); 356 break; 357 case F_MODE: 358 if ((m = setmode(val)) == NULL) 359 mtree_err("invalid file mode `%s'", val); 360 ip->st_mode = getmode(m, 0); 361 free(m); 362 break; 363 case F_NLINK: 364 ip->st_nlink = (nlink_t)strtoul(val, &ep, 10); 365 if (*ep) 366 mtree_err("invalid link count `%s'", val); 367 break; 368 case F_SIZE: 369 ip->st_size = (off_t)strtoq(val, &ep, 10); 370 if (*ep) 371 mtree_err("invalid size `%s'", val); 372 break; 373 case F_SLINK: 374 if ((ip->slink = strdup(val)) == NULL) 375 mtree_err("memory allocation error"); 376 break; 377 case F_TAGS: 378 len = strlen(val) + 3; /* "," + str + ",\0" */ 379 if ((ip->tags = malloc(len)) == NULL) 380 mtree_err("memory allocation error"); 381 snprintf(ip->tags, len, ",%s,", val); 382 break; 383 case F_TIME: 384 ip->st_mtimespec.tv_sec = 385 (time_t)strtoul(val, &ep, 10); 386 if (*ep != '.') 387 mtree_err("invalid time `%s'", val); 388 val = ep + 1; 389 ip->st_mtimespec.tv_nsec = strtol(val, &ep, 10); 390 if (*ep) 391 mtree_err("invalid time `%s'", val); 392 break; 393 case F_TYPE: 394 ip->type = parsetype(val); 395 break; 396 case F_UID: 397 ip->st_uid = (uid_t)strtoul(val, &ep, 10); 398 if (*ep) 399 mtree_err("invalid uid `%s'", val); 400 break; 401 case F_UNAME: 402 if (uid_from_user(val, &uid) == -1) 403 mtree_err("unknown user `%s'", val); 404 ip->st_uid = uid; 405 break; 406 default: 407 mtree_err( 408 "set(): unsupported key type 0x%x (INTERNAL ERROR)", 409 type); 410 /* NOTREACHED */ 411 } 412 } 413} 414 415static void 416unset(char *t, NODE *ip) 417{ 418 char *p; 419 420 while ((p = strtok(t, "\n\t ")) != NULL) { 421 if (strcmp(p, "all") == 0) 422 mtree_err("invalid keyword `all'"); 423 ip->flags &= ~parsekey(p, NULL); 424 } 425} 426