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