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