1 /* $NetBSD: spec.c,v 1.6 1995/03/07 21:12:12 cgd Exp $ */ 2 /* $OpenBSD: spec.c,v 1.12 2001/08/10 02:37:14 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. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37 #ifndef lint 38 #if 0 39 static const char sccsid[] = "@(#)spec.c 8.1 (Berkeley) 6/6/93"; 40 #else 41 static const char rcsid[] = "$OpenBSD: spec.c,v 1.12 2001/08/10 02:37:14 millert Exp $"; 42 #endif 43 #endif /* not lint */ 44 45 #include <sys/types.h> 46 #include <sys/stat.h> 47 #include <fts.h> 48 #include <pwd.h> 49 #include <grp.h> 50 #include <errno.h> 51 #include <unistd.h> 52 #include <stdio.h> 53 #include <ctype.h> 54 #include "mtree.h" 55 #include "extern.h" 56 57 int lineno; /* Current spec line number. */ 58 59 static void set __P((char *, NODE *)); 60 static void unset __P((char *, NODE *)); 61 62 NODE * 63 spec() 64 { 65 register NODE *centry, *last; 66 register char *p; 67 NODE ginfo, *root; 68 int c_cur, c_next; 69 char buf[2048]; 70 71 centry = last = root = NULL; 72 bzero(&ginfo, sizeof(ginfo)); 73 c_cur = c_next = 0; 74 for (lineno = 1; fgets(buf, sizeof(buf), stdin); 75 ++lineno, c_cur = c_next, c_next = 0) { 76 /* Skip empty lines. */ 77 if (buf[0] == '\n') 78 continue; 79 80 /* Find end of line. */ 81 if ((p = strchr(buf, '\n')) == NULL) 82 error("line %d too long", lineno); 83 84 /* See if next line is continuation line. */ 85 if (p[-1] == '\\') { 86 --p; 87 c_next = 1; 88 } 89 90 /* Null-terminate the line. */ 91 *p = '\0'; 92 93 /* Skip leading whitespace. */ 94 for (p = buf; *p && isspace(*p); ++p); 95 96 /* If nothing but whitespace or comment char, continue. */ 97 if (!*p || *p == '#') 98 continue; 99 100 #ifdef DEBUG 101 (void)fprintf(stderr, "line %d: {%s}\n", lineno, p); 102 #endif 103 if (c_cur) { 104 set(p, centry); 105 continue; 106 } 107 108 /* Grab file name, "$", "set", or "unset". */ 109 if ((p = strtok(p, "\n\t ")) == NULL) 110 error("missing field"); 111 112 if (p[0] == '/') 113 switch(p[1]) { 114 case 's': 115 if (strcmp(p + 1, "set")) 116 break; 117 set(NULL, &ginfo); 118 continue; 119 case 'u': 120 if (strcmp(p + 1, "unset")) 121 break; 122 unset(NULL, &ginfo); 123 continue; 124 } 125 126 if (strchr(p, '/')) 127 error("slash character in file name"); 128 129 if (!strcmp(p, "..")) { 130 /* Don't go up, if haven't gone down. */ 131 if (!root) 132 goto noparent; 133 if (last->type != F_DIR || last->flags & F_DONE) { 134 if (last == root) 135 goto noparent; 136 last = last->parent; 137 } 138 last->flags |= F_DONE; 139 continue; 140 141 noparent: error("no parent node"); 142 } 143 144 if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL) 145 error("%s", strerror(errno)); 146 *centry = ginfo; 147 (void)strcpy(centry->name, p); 148 #define MAGIC "?*[" 149 if (strpbrk(p, MAGIC)) 150 centry->flags |= F_MAGIC; 151 set(NULL, centry); 152 153 if (!root) { 154 last = root = centry; 155 root->parent = root; 156 } else if (last->type == F_DIR && !(last->flags & F_DONE)) { 157 centry->parent = last; 158 last = last->child = centry; 159 } else { 160 centry->parent = last->parent; 161 centry->prev = last; 162 last = last->next = centry; 163 } 164 } 165 return (root); 166 } 167 168 static void 169 set(t, ip) 170 char *t; 171 register NODE *ip; 172 { 173 register int type; 174 char *kw, *val = NULL; 175 struct group *gr; 176 struct passwd *pw; 177 mode_t *m; 178 int value; 179 u_int32_t fset, fclr; 180 char *ep; 181 182 for (; (kw = strtok(t, "= \t\n")); t = NULL) { 183 ip->flags |= type = parsekey(kw, &value); 184 if (value && (val = strtok(NULL, " \t\n")) == NULL) 185 error("missing value"); 186 switch(type) { 187 case F_CKSUM: 188 ip->cksum = strtoul(val, &ep, 10); 189 if (*ep) 190 error("invalid checksum %s", val); 191 break; 192 case F_MD5: 193 ip->md5digest = strdup(val); 194 if (!ip->md5digest) 195 error("%s", strerror(errno)); 196 break; 197 case F_FLAGS: 198 if (!strcmp(val, "none")) { 199 ip->file_flags = 0; 200 break; 201 } 202 if (strtofflags(&val, &fset, &fclr)) 203 error("%s", strerror(errno)); 204 ip->file_flags = fset; 205 break; 206 case F_GID: 207 ip->st_gid = strtoul(val, &ep, 10); 208 if (*ep) 209 error("invalid gid %s", val); 210 break; 211 case F_GNAME: 212 if ((gr = getgrnam(val)) == NULL) 213 error("unknown group %s", val); 214 ip->st_gid = gr->gr_gid; 215 break; 216 case F_IGN: 217 /* just set flag bit */ 218 break; 219 case F_MODE: 220 if ((m = setmode(val)) == NULL) 221 error("invalid file mode %s", val); 222 ip->st_mode = getmode(m, 0); 223 free(m); 224 break; 225 case F_NLINK: 226 ip->st_nlink = strtoul(val, &ep, 10); 227 if (*ep) 228 error("invalid link count %s", val); 229 break; 230 case F_RMD160: 231 ip->rmd160digest = strdup(val); 232 if (!ip->rmd160digest) 233 error("%s", strerror(errno)); 234 break; 235 case F_SHA1: 236 ip->sha1digest = strdup(val); 237 if (!ip->sha1digest) 238 error("%s", strerror(errno)); 239 break; 240 case F_SIZE: 241 ip->st_size = strtouq(val, &ep, 10); 242 if (*ep) 243 error("invalid size %s", val); 244 break; 245 case F_SLINK: 246 if ((ip->slink = strdup(val)) == NULL) 247 error("%s", strerror(errno)); 248 break; 249 case F_TIME: 250 ip->st_mtimespec.tv_sec = strtoul(val, &ep, 10); 251 if (*ep != '.') 252 error("invalid time %s", val); 253 val = ep + 1; 254 ip->st_mtimespec.tv_nsec = strtoul(val, &ep, 10); 255 if (*ep) 256 error("invalid time %s", val); 257 break; 258 case F_TYPE: 259 switch(*val) { 260 case 'b': 261 if (!strcmp(val, "block")) 262 ip->type = F_BLOCK; 263 break; 264 case 'c': 265 if (!strcmp(val, "char")) 266 ip->type = F_CHAR; 267 break; 268 case 'd': 269 if (!strcmp(val, "dir")) 270 ip->type = F_DIR; 271 break; 272 case 'f': 273 if (!strcmp(val, "file")) 274 ip->type = F_FILE; 275 if (!strcmp(val, "fifo")) 276 ip->type = F_FIFO; 277 break; 278 case 'l': 279 if (!strcmp(val, "link")) 280 ip->type = F_LINK; 281 break; 282 case 's': 283 if (!strcmp(val, "socket")) 284 ip->type = F_SOCK; 285 break; 286 default: 287 error("unknown file type %s", val); 288 } 289 break; 290 case F_UID: 291 ip->st_uid = strtoul(val, &ep, 10); 292 if (*ep) 293 error("invalid uid %s", val); 294 break; 295 case F_UNAME: 296 if ((pw = getpwnam(val)) == NULL) 297 error("unknown user %s", val); 298 ip->st_uid = pw->pw_uid; 299 break; 300 } 301 } 302 } 303 304 static void 305 unset(t, ip) 306 char *t; 307 register NODE *ip; 308 { 309 register char *p; 310 311 while ((p = strtok(t, "\n\t "))) 312 ip->flags &= ~parsekey(p, NULL); 313 } 314