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