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