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 *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**); 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; 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)) > 0){ 175 for(i = 0; i < n; i++){ 176 child.new = mkpath(mkaux, me->new, d[i].name); 177 if(me->old) 178 child.old = mkpath(mkaux, me->old, d[i].name); 179 child.elem = d[i].name; 180 setnames(mkaux, &child); 181 if((!(d[i].mode&DMDIR) || rec) && copyfile(mkaux, &child, &d[i], 1) && rec) 182 mktree(mkaux, &child, rec); 183 free(child.new); 184 if(child.old) 185 free(child.old); 186 } 187 } 188 close(fd); 189 } 190 191 static int 192 mkfile(Mkaux *mkaux, File *f) 193 { 194 Dir *d; 195 196 if((d = dirstat(mkaux->oldfile.s)) == nil){ 197 warn(mkaux, "can't stat file %s: %r", mkaux->oldfile.s); 198 skipdir(mkaux); 199 return 0; 200 } 201 return copyfile(mkaux, f, d, 0); 202 } 203 204 enum { 205 SLOP = 30 206 }; 207 208 static void 209 setname(Mkaux *mkaux, Name *name, char *s1, char *s2) 210 { 211 int l; 212 213 l = strlen(s1)+strlen(s2)+1; 214 if(name->n < l+SLOP/2) { 215 free(name->s); 216 name->s = emalloc(mkaux, l+SLOP); 217 name->n = l+SLOP; 218 } 219 snprint(name->s, name->n, "%s%s%s", s1, s1[0]==0 || s1[strlen(s1)-1]!='/' ? "/" : "", s2); 220 } 221 222 static int 223 copyfile(Mkaux *mkaux, File *f, Dir *d, int permonly) 224 { 225 Dir *nd; 226 ulong xmode; 227 char *p; 228 229 setname(mkaux, &mkaux->fullname, mkaux->root, f->old ? f->old : f->new); 230 /* 231 * Extra stat here is inefficient but accounts for binds. 232 */ 233 if((nd = dirstat(mkaux->fullname.s)) != nil) 234 d = nd; 235 236 d->name = f->elem; 237 if(d->type != 'M'){ 238 d->uid = "sys"; 239 d->gid = "sys"; 240 xmode = (d->mode >> 6) & 7; 241 d->mode |= xmode | (xmode << 3); 242 } 243 if(strcmp(f->uid, "-") != 0) 244 d->uid = f->uid; 245 if(strcmp(f->gid, "-") != 0) 246 d->gid = f->gid; 247 if(f->mode != ~0){ 248 if(permonly) 249 d->mode = (d->mode & ~0666) | (f->mode & 0666); 250 else if((d->mode&DMDIR) != (f->mode&DMDIR)) 251 warn(mkaux, "inconsistent mode for %s", f->new); 252 else 253 d->mode = f->mode; 254 } 255 256 if(p = strrchr(f->new, '/')) 257 d->name = p+1; 258 else 259 d->name = f->new; 260 261 mkaux->mkenum(f->new, mkaux->fullname.s, d, mkaux->a); 262 xmode = d->mode; 263 free(nd); 264 return (xmode&DMDIR) != 0; 265 } 266 267 static char * 268 mkpath(Mkaux *mkaux, char *prefix, char *elem) 269 { 270 char *p; 271 int n; 272 273 n = strlen(prefix) + strlen(elem) + 2; 274 p = emalloc(mkaux, n); 275 strcpy(p, prefix); 276 strcat(p, "/"); 277 strcat(p, elem); 278 return p; 279 } 280 281 static void 282 setnames(Mkaux *mkaux, File *f) 283 { 284 285 if(f->old){ 286 if(f->old[0] == '/') 287 setname(mkaux, &mkaux->oldfile, f->old, ""); 288 else 289 setname(mkaux, &mkaux->oldfile, mkaux->root, f->old); 290 } else 291 setname(mkaux, &mkaux->oldfile, mkaux->root, f->new); 292 } 293 294 static void 295 freefile(File *f) 296 { 297 if(f->old) 298 free(f->old); 299 if(f->new) 300 free(f->new); 301 free(f); 302 } 303 304 /* 305 * skip all files in the proto that 306 * could be in the current dir 307 */ 308 static void 309 skipdir(Mkaux *mkaux) 310 { 311 char *p, c; 312 int level; 313 314 if(mkaux->indent < 0) 315 return; 316 level = mkaux->indent; 317 for(;;){ 318 mkaux->indent = 0; 319 p = Brdline(mkaux->b, '\n'); 320 mkaux->lineno++; 321 if(!p){ 322 mkaux->indent = -1; 323 return; 324 } 325 while((c = *p++) != '\n') 326 if(c == ' ') 327 mkaux->indent++; 328 else if(c == '\t') 329 mkaux->indent += 8; 330 else 331 break; 332 if(mkaux->indent <= level){ 333 Bseek(mkaux->b, -Blinelen(mkaux->b), 1); 334 mkaux->lineno--; 335 return; 336 } 337 } 338 } 339 340 static File* 341 getfile(Mkaux *mkaux, File *old) 342 { 343 File *f; 344 char *elem; 345 char *p; 346 int c; 347 348 if(mkaux->indent < 0) 349 return 0; 350 loop: 351 mkaux->indent = 0; 352 p = Brdline(mkaux->b, '\n'); 353 mkaux->lineno++; 354 if(!p){ 355 mkaux->indent = -1; 356 return 0; 357 } 358 while((c = *p++) != '\n') 359 if(c == ' ') 360 mkaux->indent++; 361 else if(c == '\t') 362 mkaux->indent += 8; 363 else 364 break; 365 if(c == '\n' || c == '#') 366 goto loop; 367 p--; 368 f = emalloc(mkaux, sizeof *f); 369 p = getname(mkaux, p, &elem); 370 if(p == nil) 371 return nil; 372 373 f->new = mkpath(mkaux, old->new, elem); 374 free(elem); 375 f->elem = utfrrune(f->new, L'/') + 1; 376 p = getmode(mkaux, p, &f->mode); 377 p = getname(mkaux, p, &f->uid); /* LEAK */ 378 if(p == nil) 379 return nil; 380 381 if(!*f->uid) 382 strcpy(f->uid, "-"); 383 p = getname(mkaux, p, &f->gid); /* LEAK */ 384 if(p == nil) 385 return nil; 386 387 if(!*f->gid) 388 strcpy(f->gid, "-"); 389 f->old = getpath(mkaux, p); 390 if(f->old && strcmp(f->old, "-") == 0){ 391 free(f->old); 392 f->old = 0; 393 } 394 setnames(mkaux, f); 395 396 return f; 397 } 398 399 static char* 400 getpath(Mkaux *mkaux, char *p) 401 { 402 char *q, *new; 403 int c, n; 404 405 while((c = *p) == ' ' || c == '\t') 406 p++; 407 q = p; 408 while((c = *q) != '\n' && c != ' ' && c != '\t') 409 q++; 410 if(q == p) 411 return 0; 412 n = q - p; 413 new = emalloc(mkaux, n + 1); 414 memcpy(new, p, n); 415 new[n] = 0; 416 return new; 417 } 418 419 static char* 420 getname(Mkaux *mkaux, char *p, char **buf) 421 { 422 char *s, *start; 423 int c; 424 425 while((c = *p) == ' ' || c == '\t') 426 p++; 427 428 start = p; 429 while((c = *p) != '\n' && c != ' ' && c != '\t') 430 p++; 431 432 *buf = malloc(p+2-start); /* +2: need at least 2 bytes; might strcpy "-" into buf */ 433 if(*buf == nil) 434 return nil; 435 memmove(*buf, start, p-start); 436 437 (*buf)[p-start] = '\0'; 438 439 if(**buf == '$'){ 440 s = getenv(*buf+1); 441 if(s == 0){ 442 warn(mkaux, "can't read environment variable %s", *buf+1); 443 skipdir(mkaux); 444 free(*buf); 445 return nil; 446 } 447 free(*buf); 448 *buf = s; 449 } 450 return p; 451 } 452 453 static char* 454 getmode(Mkaux *mkaux, char *p, ulong *xmode) 455 { 456 char *buf, *s; 457 ulong m; 458 459 *xmode = ~0; 460 p = getname(mkaux, p, &buf); 461 if(p == nil) 462 return nil; 463 464 s = buf; 465 if(!*s || strcmp(s, "-") == 0) 466 return p; 467 m = 0; 468 if(*s == 'd'){ 469 m |= DMDIR; 470 s++; 471 } 472 if(*s == 'a'){ 473 m |= DMAPPEND; 474 s++; 475 } 476 if(*s == 'l'){ 477 m |= DMEXCL; 478 s++; 479 } 480 if(s[0] < '0' || s[0] > '7' 481 || s[1] < '0' || s[1] > '7' 482 || s[2] < '0' || s[2] > '7' 483 || s[3]){ 484 warn(mkaux, "bad mode specification %s", buf); 485 free(buf); 486 return p; 487 } 488 *xmode = m | strtoul(s, 0, 8); 489 free(buf); 490 return p; 491 } 492 493 static void 494 warn(Mkaux *mkaux, char *fmt, ...) 495 { 496 char buf[256]; 497 va_list va; 498 499 va_start(va, fmt); 500 vseprint(buf, buf+sizeof(buf), fmt, va); 501 va_end(va); 502 503 if(mkaux->warn) 504 mkaux->warn(buf, mkaux->a); 505 else 506 fprint(2, "warning: %s\n", buf); 507 } 508