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