1 #include <u.h> 2 #include <libc.h> 3 #include <bio.h> 4 #include <auth.h> 5 #include <fcall.h> 6 #include <disk.h> 7 8 enum{ 9 LEN = 8*1024, 10 HUNKS = 128, 11 }; 12 13 typedef struct File File; 14 struct File{ 15 char *new; 16 char *elem; 17 char *old; 18 char uid[NAMELEN]; 19 char gid[NAMELEN]; 20 ulong mode; 21 }; 22 23 typedef void Mkfserr(char*, void*); 24 typedef void Mkfsenum(char*, char*, Dir*, void*); 25 26 typedef struct Name Name; 27 struct Name { 28 int n; 29 char *s; 30 }; 31 32 typedef struct Mkaux Mkaux; 33 struct Mkaux { 34 Mkfserr *warn; 35 Mkfsenum *mkenum; 36 char *root; 37 char *proto; 38 jmp_buf jmp; 39 Biobuf *b; 40 41 Name oldfile; 42 Name fullname; 43 int lineno; 44 int indent; 45 46 void *a; 47 }; 48 49 static void domkfs(Mkaux *mkaux, File *me, int level); 50 51 static int copyfile(Mkaux*, File*, Dir*, int); 52 static void freefile(File*); 53 static File* getfile(Mkaux*, File*); 54 static char* getmode(Mkaux*, char*, ulong*); 55 static char* getname(Mkaux*, char*, char*, int); 56 static char* getpath(Mkaux*, char*); 57 static int mkfile(Mkaux*, File*); 58 static char* mkpath(Mkaux*, char*, char*); 59 static void mktree(Mkaux*, File*, int); 60 static void setnames(Mkaux*, File*); 61 static void skipdir(Mkaux*); 62 static void warn(Mkaux*, char *, ...); 63 64 //static void 65 //mprint(char *new, char *old, Dir *d, void*) 66 //{ 67 // print("%s %s %D\n", new, old, d); 68 //} 69 70 int 71 rdproto(char *proto, char *root, Mkfsenum *mkenum, Mkfserr *mkerr, void *a) 72 { 73 Mkaux mx, *m; 74 File file; 75 int rv; 76 77 m = &mx; 78 memset(&mx, 0, sizeof mx); 79 if(root == nil) 80 root = "/"; 81 82 m->root = root; 83 m->warn = mkerr; 84 m->mkenum = mkenum; 85 m->a = a; 86 m->proto = proto; 87 m->lineno = 0; 88 m->indent = 0; 89 if((m->b = Bopen(proto, OREAD)) == nil) { 90 werrstr("open '%s': %r", proto); 91 return -1; 92 } 93 94 memset(&file, 0, sizeof file); 95 file.new = ""; 96 file.old = nil; 97 98 rv = 0; 99 if(setjmp(m->jmp) == 0) 100 domkfs(m, &file, -1); 101 else 102 rv = -1; 103 free(m->oldfile.s); 104 free(m->fullname.s); 105 return rv; 106 } 107 108 static void* 109 emalloc(Mkaux *mkaux, ulong n) 110 { 111 void *v; 112 113 v = malloc(n); 114 if(v == nil) 115 longjmp(mkaux->jmp, 1); /* memory leak */ 116 memset(v, 0, n); 117 return v; 118 } 119 120 static char* 121 estrdup(Mkaux *mkaux, char *s) 122 { 123 s = strdup(s); 124 if(s == nil) 125 longjmp(mkaux->jmp, 1); /* memory leak */ 126 return s; 127 } 128 129 static void 130 domkfs(Mkaux *mkaux, File *me, int level) 131 { 132 File *child; 133 int rec; 134 135 child = getfile(mkaux, me); 136 if(!child) 137 return; 138 if((child->elem[0] == '+' || child->elem[0] == '*') && child->elem[1] == '\0'){ 139 rec = child->elem[0] == '+'; 140 free(child->new); 141 child->new = estrdup(mkaux, me->new); 142 setnames(mkaux, child); 143 mktree(mkaux, child, rec); 144 freefile(child); 145 child = getfile(mkaux, me); 146 } 147 while(child && mkaux->indent > level){ 148 if(mkfile(mkaux, child)) 149 domkfs(mkaux, child, mkaux->indent); 150 freefile(child); 151 child = getfile(mkaux, me); 152 } 153 if(child){ 154 freefile(child); 155 Bseek(mkaux->b, -Blinelen(mkaux->b), 1); 156 mkaux->lineno--; 157 } 158 } 159 160 static void 161 mktree(Mkaux *mkaux, File *me, int rec) 162 { 163 File child; 164 Dir d[HUNKS]; 165 int i, n, fd; 166 167 fd = open(mkaux->oldfile.s, OREAD); 168 if(fd < 0){ 169 warn(mkaux, "can't open %s: %r", mkaux->oldfile.s); 170 return; 171 } 172 173 child = *me; 174 while((n = dirread(fd, d, sizeof d)) > 0){ 175 n /= DIRLEN; 176 for(i = 0; i < n; i++){ 177 child.new = mkpath(mkaux, me->new, d[i].name); 178 if(me->old) 179 child.old = mkpath(mkaux, me->old, d[i].name); 180 child.elem = d[i].name; 181 setnames(mkaux, &child); 182 if(copyfile(mkaux, &child, &d[i], 1) && rec) 183 mktree(mkaux, &child, rec); 184 free(child.new); 185 if(child.old) 186 free(child.old); 187 } 188 } 189 close(fd); 190 } 191 192 static int 193 mkfile(Mkaux *mkaux, File *f) 194 { 195 Dir dir; 196 197 if(dirstat(mkaux->oldfile.s, &dir) < 0){ 198 warn(mkaux, "can't stat file %s: %r", mkaux->oldfile.s); 199 skipdir(mkaux); 200 return 0; 201 } 202 return copyfile(mkaux, f, &dir, 0); 203 } 204 205 enum { 206 SLOP = 30 207 }; 208 209 static void 210 setname(Mkaux *mkaux, Name *name, char *s1, char *s2) 211 { 212 int l; 213 214 l = strlen(s1)+strlen(s2)+1; 215 if(name->n < l) { 216 free(name->s); 217 name->s = emalloc(mkaux, l+SLOP); 218 name->n = l+SLOP; 219 } 220 snprint(name->s, name->n, "%s%s", s1, s2); 221 } 222 223 static int 224 copyfile(Mkaux *mkaux, File *f, Dir *d, int permonly) 225 { 226 ulong xmode; 227 char *p; 228 229 memmove(d->name, f->elem, NAMELEN); 230 if(d->type != 'M'){ 231 strncpy(d->uid, "sys", NAMELEN); 232 strncpy(d->gid, "sys", NAMELEN); 233 xmode = (d->mode >> 6) & 7; 234 d->mode |= xmode | (xmode << 3); 235 } 236 if(strcmp(f->uid, "-") != 0) 237 strncpy(d->uid, f->uid, NAMELEN); 238 if(strcmp(f->gid, "-") != 0) 239 strncpy(d->gid, f->gid, NAMELEN); 240 if(f->mode != ~0){ 241 if(permonly) 242 d->mode = (d->mode & ~0666) | (f->mode & 0666); 243 else if((d->mode&CHDIR) != (f->mode&CHDIR)) 244 warn(mkaux, "inconsistent mode for %s", f->new); 245 else 246 d->mode = f->mode; 247 } 248 249 setname(mkaux, &mkaux->fullname, mkaux->root, f->old ? f->old : f->new); 250 if(p = strrchr(f->new, '/')) 251 strcpy(d->name, p+1); 252 else 253 strcpy(d->name, f->new); 254 255 mkaux->mkenum(f->new, mkaux->fullname.s, d, mkaux->a); 256 return (d->mode & CHDIR) != 0; 257 } 258 259 static char * 260 mkpath(Mkaux *mkaux, char *prefix, char *elem) 261 { 262 char *p; 263 int n; 264 265 n = strlen(prefix) + strlen(elem) + 2; 266 p = emalloc(mkaux, n); 267 sprint(p, "%s/%s", prefix, elem); 268 return p; 269 } 270 271 static void 272 setnames(Mkaux *mkaux, File *f) 273 { 274 275 if(f->old){ 276 if(f->old[0] == '/') 277 setname(mkaux, &mkaux->oldfile, mkaux->root, f->old); 278 else 279 setname(mkaux, &mkaux->oldfile, f->old, ""); 280 } else 281 setname(mkaux, &mkaux->oldfile, mkaux->root, f->new); 282 } 283 284 static void 285 freefile(File *f) 286 { 287 if(f->old) 288 free(f->old); 289 if(f->new) 290 free(f->new); 291 free(f); 292 } 293 294 /* 295 * skip all files in the proto that 296 * could be in the current dir 297 */ 298 static void 299 skipdir(Mkaux *mkaux) 300 { 301 char *p, c; 302 int level; 303 304 if(mkaux->indent < 0) 305 return; 306 level = mkaux->indent; 307 for(;;){ 308 mkaux->indent = 0; 309 p = Brdline(mkaux->b, '\n'); 310 mkaux->lineno++; 311 if(!p){ 312 mkaux->indent = -1; 313 return; 314 } 315 while((c = *p++) != '\n') 316 if(c == ' ') 317 mkaux->indent++; 318 else if(c == '\t') 319 mkaux->indent += 8; 320 else 321 break; 322 if(mkaux->indent <= level){ 323 Bseek(mkaux->b, -Blinelen(mkaux->b), 1); 324 mkaux->lineno--; 325 return; 326 } 327 } 328 } 329 330 static File* 331 getfile(Mkaux *mkaux, File *old) 332 { 333 File *f; 334 char elem[NAMELEN]; 335 char *p; 336 int c; 337 338 if(mkaux->indent < 0) 339 return 0; 340 loop: 341 mkaux->indent = 0; 342 p = Brdline(mkaux->b, '\n'); 343 mkaux->lineno++; 344 if(!p){ 345 mkaux->indent = -1; 346 return 0; 347 } 348 while((c = *p++) != '\n') 349 if(c == ' ') 350 mkaux->indent++; 351 else if(c == '\t') 352 mkaux->indent += 8; 353 else 354 break; 355 if(c == '\n' || c == '#') 356 goto loop; 357 p--; 358 f = emalloc(mkaux, sizeof *f); 359 p = getname(mkaux, p, elem, sizeof elem); 360 if(p == nil) 361 return nil; 362 363 f->new = mkpath(mkaux, old->new, elem); 364 f->elem = utfrrune(f->new, L'/') + 1; 365 p = getmode(mkaux, p, &f->mode); 366 p = getname(mkaux, p, f->uid, sizeof f->uid); 367 if(p == nil) 368 return nil; 369 370 if(!*f->uid) 371 strcpy(f->uid, "-"); 372 p = getname(mkaux, p, f->gid, sizeof f->gid); 373 if(p == nil) 374 return nil; 375 376 if(!*f->gid) 377 strcpy(f->gid, "-"); 378 f->old = getpath(mkaux, p); 379 if(f->old && strcmp(f->old, "-") == 0){ 380 free(f->old); 381 f->old = 0; 382 } 383 setnames(mkaux, f); 384 385 return f; 386 } 387 388 static char* 389 getpath(Mkaux *mkaux, char *p) 390 { 391 char *q, *new; 392 int c, n; 393 394 while((c = *p) == ' ' || c == '\t') 395 p++; 396 q = p; 397 while((c = *q) != '\n' && c != ' ' && c != '\t') 398 q++; 399 if(q == p) 400 return 0; 401 n = q - p; 402 new = emalloc(mkaux, n + 1); 403 memcpy(new, p, n); 404 new[n] = 0; 405 return new; 406 } 407 408 static char* 409 getname(Mkaux *mkaux, char *p, char *buf, int len) 410 { 411 char *s; 412 int i, c; 413 414 while((c = *p) == ' ' || c == '\t') 415 p++; 416 i = 0; 417 while((c = *p) != '\n' && c != ' ' && c != '\t'){ 418 if(i < len) 419 buf[i++] = c; 420 p++; 421 } 422 if(i == len){ 423 buf[len-1] = '\0'; 424 warn(mkaux, "name %s too long; truncated", buf); 425 }else 426 buf[i] = '\0'; 427 428 if(buf[0] == '$'){ 429 s = getenv(buf+1); 430 if(s == 0){ 431 warn(mkaux, "can't read environment variable %s", buf+1); 432 skipdir(mkaux); 433 return nil; 434 } 435 strncpy(buf, s, NAMELEN-1); 436 buf[NAMELEN-1] = '\0'; 437 free(s); 438 } 439 return p; 440 } 441 442 static char* 443 getmode(Mkaux *mkaux, char *p, ulong *xmode) 444 { 445 char buf[7], *s; 446 ulong m; 447 448 *xmode = ~0; 449 p = getname(mkaux, p, buf, sizeof buf); 450 s = buf; 451 if(!*s || strcmp(s, "-") == 0) 452 return p; 453 m = 0; 454 if(*s == 'd'){ 455 m |= CHDIR; 456 s++; 457 } 458 if(*s == 'a'){ 459 m |= CHAPPEND; 460 s++; 461 } 462 if(*s == 'l'){ 463 m |= CHEXCL; 464 s++; 465 } 466 if(s[0] < '0' || s[0] > '7' 467 || s[1] < '0' || s[1] > '7' 468 || s[2] < '0' || s[2] > '7' 469 || s[3]){ 470 warn(mkaux, "bad mode specification %s", buf); 471 return p; 472 } 473 *xmode = m | strtoul(s, 0, 8); 474 return p; 475 } 476 477 static void 478 warn(Mkaux *mkaux, char *fmt, ...) 479 { 480 char buf[256]; 481 va_list va; 482 483 va_start(va, fmt); 484 doprint(buf, buf+sizeof(buf), fmt, va); 485 va_end(va); 486 487 if(mkaux->warn) 488 mkaux->warn(buf, mkaux->a); 489 else 490 fprint(2, "warning: %s\n", buf); 491 } 492