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 32static 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 "mtree.h" 51#include "extern.h" 52 53int lineno; /* Current spec line number. */ 54 55static void set(char *, NODE *); 56static void unset(char *, NODE *); 57 58NODE * 59mtree_readspec(FILE *fi) 60{ 61 NODE *centry, *last; 62 char *p; 63 NODE ginfo, *root; 64 int c_cur, c_next; 65 char buf[2048]; 66 67 centry = last = root = NULL; 68 bzero(&ginfo, sizeof(ginfo)); 69 c_cur = c_next = 0; 70 for (lineno = 1; fgets(buf, sizeof(buf), fi); 71 ++lineno, c_cur = c_next, c_next = 0) { 72 /* Skip empty lines. */ 73 if (buf[0] == '\n') 74 continue; 75 76 /* Find end of line. */ 77 if ((p = index(buf, '\n')) == NULL) 78 errx(1, "line %d too long", lineno); 79 80 /* See if next line is continuation line. */ 81 if (p[-1] == '\\') { 82 --p; 83 c_next = 1; 84 } 85 86 /* Null-terminate the line. */ 87 *p = '\0'; 88 89 /* Skip leading whitespace. */ 90 for (p = buf; *p && isspace(*p); ++p); 91 92 /* If nothing but whitespace or comment char, continue. */ 93 if (!*p || *p == '#') 94 continue; 95 96#ifdef DEBUG 97 (void)fprintf(stderr, "line %d: {%s}\n", lineno, p); 98#endif 99 if (c_cur) { 100 set(p, centry); 101 continue; 102 } 103 104 /* Grab file name, "$", "set", or "unset". */ 105 if ((p = strtok(p, "\n\t ")) == NULL) 106 errx(1, "line %d: missing field", lineno); 107 108 if (p[0] == '/') 109 switch(p[1]) { 110 case 's': 111 if (strcmp(p + 1, "set")) 112 break; 113 set(NULL, &ginfo); 114 continue; 115 case 'u': 116 if (strcmp(p + 1, "unset")) 117 break; 118 unset(NULL, &ginfo); 119 continue; 120 } 121 122 if (index(p, '/')) 123 errx(1, "line %d: slash character in file name", 124 lineno); 125 126 if (!strcmp(p, "..")) { 127 /* Don't go up, if haven't gone down. */ 128 if (!root) 129 goto noparent; 130 if (last->type != F_DIR || last->flags & F_DONE) { 131 if (last == root) 132 goto noparent; 133 last = last->parent; 134 } 135 last->flags |= F_DONE; 136 continue; 137 138noparent: errx(1, "line %d: no parent node", lineno); 139 } 140 141 if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL) 142 errx(1, "calloc"); 143 *centry = ginfo; 144#define MAGIC "?*[" 145 if (strpbrk(p, MAGIC)) 146 centry->flags |= F_MAGIC; 147 if (strunvis(centry->name, p) == -1) 148 errx(1, "filename %s is ill-encoded", p); 149 set(NULL, centry); 150 151 if (!root) { 152 last = root = centry; 153 root->parent = root; 154 } else if (last->type == F_DIR && !(last->flags & F_DONE)) { 155 centry->parent = last; 156 last = last->child = centry; 157 } else { 158 centry->parent = last->parent; 159 centry->prev = last; 160 last = last->next = centry; 161 } 162 } 163 return (root); 164} 165 166static void 167set(char *t, NODE *ip) 168{ 169 int type; 170 char *kw, *val = NULL; 171 struct group *gr; 172 struct passwd *pw; 173 mode_t *m; 174 int value; 175 char *ep; 176 177 for (; (kw = strtok(t, "= \t\n")); t = NULL) { 178 ip->flags |= type = parsekey(kw, &value); 179 if ((value == 0) || (val = strtok(NULL, " \t\n")) == NULL) 180 errx(1, "line %d: missing value", lineno); 181 switch(type) { 182 case F_CKSUM: 183 ip->cksum = strtoul(val, &ep, 10); 184 if (*ep) 185 errx(1, "line %d: invalid checksum %s", 186 lineno, val); 187 break; 188 case F_MD5: 189 ip->md5digest = strdup(val); 190 if(!ip->md5digest) 191 errx(1, "strdup"); 192 break; 193 case F_SHA1: 194 ip->sha1digest = strdup(val); 195 if(!ip->sha1digest) 196 errx(1, "strdup"); 197 break; 198 case F_SHA256: 199 ip->sha256digest = strdup(val); 200 if(!ip->sha256digest) 201 errx(1, "strdup"); 202 break; 203 case F_RMD160: 204 ip->rmd160digest = strdup(val); 205 if(!ip->rmd160digest) 206 errx(1, "strdup"); 207 break; 208 case F_FLAGS: 209 if (strcmp("none", val) == 0) 210 ip->st_flags = 0; 211 else if (strtofflags(&val, &ip->st_flags, NULL) != 0) 212 errx(1, "line %d: invalid flag %s",lineno, val); 213 break; 214 case F_GID: 215 ip->st_gid = (gid_t)strtoul(val, &ep, 10); 216 if (*ep) 217 errx(1, "line %d: invalid gid %s", lineno, val); 218 break; 219 case F_GNAME: 220 if ((gr = getgrnam(val)) == NULL) 221 errx(1, "line %d: unknown group %s", lineno, val); 222 ip->st_gid = gr->gr_gid; 223 break; 224 case F_IGN: 225 /* just set flag bit */ 226 break; 227 case F_MODE: 228 if ((m = setmode(val)) == NULL) 229 errx(1, "line %d: invalid file mode %s", 230 lineno, val); 231 ip->st_mode = getmode(m, 0); 232 free(m); 233 break; 234 case F_NLINK: 235 ip->st_nlink = strtoul(val, &ep, 10); 236 if (*ep) 237 errx(1, "line %d: invalid link count %s", 238 lineno, val); 239 break; 240 case F_SIZE: 241 ip->st_size = strtoq(val, &ep, 10); 242 if (*ep) 243 errx(1, "line %d: invalid size %s", 244 lineno, val); 245 break; 246 case F_SLINK: 247 ip->slink = malloc(strlen(val) + 1); 248 if (ip->slink == NULL) 249 errx(1, "malloc"); 250 if (strunvis(ip->slink, val) == -1) 251 errx(1, "symlink %s is ill-encoded", val); 252 break; 253 case F_TIME: 254 ip->st_mtimespec.tv_sec = strtoul(val, &ep, 10); 255 if (*ep != '.') 256 errx(1, "line %d: invalid time %s", 257 lineno, val); 258 val = ep + 1; 259 ip->st_mtimespec.tv_nsec = strtoul(val, &ep, 10); 260 if (*ep) 261 errx(1, "line %d: invalid time %s", 262 lineno, val); 263 break; 264 case F_TYPE: 265 switch(*val) { 266 case 'b': 267 if (!strcmp(val, "block")) 268 ip->type = F_BLOCK; 269 break; 270 case 'c': 271 if (!strcmp(val, "char")) 272 ip->type = F_CHAR; 273 break; 274 case 'd': 275 if (!strcmp(val, "dir")) 276 ip->type = F_DIR; 277 break; 278 case 'f': 279 if (!strcmp(val, "file")) 280 ip->type = F_FILE; 281 if (!strcmp(val, "fifo")) 282 ip->type = F_FIFO; 283 break; 284 case 'l': 285 if (!strcmp(val, "link")) 286 ip->type = F_LINK; 287 break; 288 case 's': 289 if (!strcmp(val, "socket")) 290 ip->type = F_SOCK; 291 break; 292 default: 293 errx(1, "line %d: unknown file type %s", 294 lineno, val); 295 } 296 break; 297 case F_UID: 298 ip->st_uid = (uid_t)strtoul(val, &ep, 10); 299 if (*ep) 300 errx(1, "line %d: invalid uid %s", lineno, val); 301 break; 302 case F_UNAME: 303 if ((pw = getpwnam(val)) == NULL) 304 errx(1, "line %d: unknown user %s", lineno, val); 305 ip->st_uid = pw->pw_uid; 306 break; 307 } 308 } 309} 310 311static void 312unset(char *t, NODE *ip) 313{ 314 char *p; 315 316 while ((p = strtok(t, "\n\t "))) 317 ip->flags &= ~parsekey(p, NULL); 318} 319