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