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(char*); 57 static int mkfile(Mkaux*, File*); 58 static char* mkpath(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 71 int 72 rdproto(char *proto, char *root, Mkfsenum *mkenum, Mkfserr *mkerr, void *a) 73 { 74 Mkaux mx, *m; 75 File file; 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 domkfs(m, &file, -1); 98 free(m->oldfile.s); 99 free(m->fullname.s); 100 return 0; 101 } 102 103 static void 104 domkfs(Mkaux *mkaux, File *me, int level) 105 { 106 File *child; 107 int rec; 108 109 child = getfile(mkaux, me); 110 if(!child) 111 return; 112 if((child->elem[0] == '+' || child->elem[0] == '*') && child->elem[1] == '\0'){ 113 rec = child->elem[0] == '+'; 114 free(child->new); 115 child->new = estrdup(me->new); 116 setnames(mkaux, child); 117 mktree(mkaux, child, rec); 118 freefile(child); 119 child = getfile(mkaux, me); 120 } 121 while(child && mkaux->indent > level){ 122 if(mkfile(mkaux, child)) 123 domkfs(mkaux, child, mkaux->indent); 124 freefile(child); 125 child = getfile(mkaux, me); 126 } 127 if(child){ 128 freefile(child); 129 Bseek(mkaux->b, -Blinelen(mkaux->b), 1); 130 mkaux->lineno--; 131 } 132 } 133 134 static void 135 mktree(Mkaux *mkaux, File *me, int rec) 136 { 137 File child; 138 Dir d[HUNKS]; 139 int i, n, fd; 140 141 fd = open(mkaux->oldfile.s, OREAD); 142 if(fd < 0){ 143 warn(mkaux, "can't open %s: %r", mkaux->oldfile.s); 144 return; 145 } 146 147 child = *me; 148 while((n = dirread(fd, d, sizeof d)) > 0){ 149 n /= DIRLEN; 150 for(i = 0; i < n; i++){ 151 child.new = mkpath(me->new, d[i].name); 152 if(me->old) 153 child.old = mkpath(me->old, d[i].name); 154 child.elem = d[i].name; 155 setnames(mkaux, &child); 156 if(copyfile(mkaux, &child, &d[i], 1) && rec) 157 mktree(mkaux, &child, rec); 158 free(child.new); 159 if(child.old) 160 free(child.old); 161 } 162 } 163 close(fd); 164 } 165 166 static int 167 mkfile(Mkaux *mkaux, File *f) 168 { 169 Dir dir; 170 171 if(dirstat(mkaux->oldfile.s, &dir) < 0){ 172 warn(mkaux, "can't stat file %s: %r", mkaux->oldfile.s); 173 skipdir(mkaux); 174 return 0; 175 } 176 return copyfile(mkaux, f, &dir, 0); 177 } 178 179 enum { 180 SLOP = 30 181 }; 182 183 static void 184 setname(Name *name, char *s1, char *s2) 185 { 186 int l; 187 188 l = strlen(s1)+strlen(s2)+1; 189 if(name->n < l) { 190 free(name->s); 191 name->s = emalloc(l+SLOP); 192 name->n = l+SLOP; 193 } 194 snprint(name->s, name->n, "%s%s", s1, s2); 195 } 196 197 static int 198 copyfile(Mkaux *mkaux, File *f, Dir *d, int permonly) 199 { 200 ulong xmode; 201 char *p; 202 203 memmove(d->name, f->elem, NAMELEN); 204 if(d->type != 'M'){ 205 strncpy(d->uid, "sys", NAMELEN); 206 strncpy(d->gid, "sys", NAMELEN); 207 xmode = (d->mode >> 6) & 7; 208 d->mode |= xmode | (xmode << 3); 209 } 210 if(strcmp(f->uid, "-") != 0) 211 strncpy(d->uid, f->uid, NAMELEN); 212 if(strcmp(f->gid, "-") != 0) 213 strncpy(d->gid, f->gid, NAMELEN); 214 if(f->mode != ~0){ 215 if(permonly) 216 d->mode = (d->mode & ~0666) | (f->mode & 0666); 217 else if((d->mode&CHDIR) != (f->mode&CHDIR)) 218 warn(mkaux, "inconsistent mode for %s", f->new); 219 else 220 d->mode = f->mode; 221 } 222 223 setname(&mkaux->fullname, mkaux->root, f->old ? f->old : f->new); 224 if(p = strrchr(f->new, '/')) 225 strcpy(d->name, p+1); 226 else 227 strcpy(d->name, f->new); 228 229 mkaux->mkenum(f->new, mkaux->fullname.s, d, mkaux->a); 230 return (d->mode & CHDIR) != 0; 231 } 232 233 static char * 234 mkpath(char *prefix, char *elem) 235 { 236 char *p; 237 int n; 238 239 n = strlen(prefix) + strlen(elem) + 2; 240 p = emalloc(n); 241 sprint(p, "%s/%s", prefix, elem); 242 return p; 243 } 244 245 static void 246 setnames(Mkaux *mkaux, File *f) 247 { 248 249 if(f->old){ 250 if(f->old[0] == '/') 251 setname(&mkaux->oldfile, mkaux->root, f->old); 252 else 253 setname(&mkaux->oldfile, f->old, ""); 254 } else 255 setname(&mkaux->oldfile, mkaux->root, f->new); 256 } 257 258 static void 259 freefile(File *f) 260 { 261 if(f->old) 262 free(f->old); 263 if(f->new) 264 free(f->new); 265 free(f); 266 } 267 268 /* 269 * skip all files in the proto that 270 * could be in the current dir 271 */ 272 static void 273 skipdir(Mkaux *mkaux) 274 { 275 char *p, c; 276 int level; 277 278 if(mkaux->indent < 0) 279 return; 280 level = mkaux->indent; 281 for(;;){ 282 mkaux->indent = 0; 283 p = Brdline(mkaux->b, '\n'); 284 mkaux->lineno++; 285 if(!p){ 286 mkaux->indent = -1; 287 return; 288 } 289 while((c = *p++) != '\n') 290 if(c == ' ') 291 mkaux->indent++; 292 else if(c == '\t') 293 mkaux->indent += 8; 294 else 295 break; 296 if(mkaux->indent <= level){ 297 Bseek(mkaux->b, -Blinelen(mkaux->b), 1); 298 mkaux->lineno--; 299 return; 300 } 301 } 302 } 303 304 static File* 305 getfile(Mkaux *mkaux, File *old) 306 { 307 File *f; 308 char elem[NAMELEN]; 309 char *p; 310 int c; 311 312 if(mkaux->indent < 0) 313 return 0; 314 loop: 315 mkaux->indent = 0; 316 p = Brdline(mkaux->b, '\n'); 317 mkaux->lineno++; 318 if(!p){ 319 mkaux->indent = -1; 320 return 0; 321 } 322 while((c = *p++) != '\n') 323 if(c == ' ') 324 mkaux->indent++; 325 else if(c == '\t') 326 mkaux->indent += 8; 327 else 328 break; 329 if(c == '\n' || c == '#') 330 goto loop; 331 p--; 332 f = emalloc(sizeof *f); 333 p = getname(mkaux, p, elem, sizeof elem); 334 if(p == nil) 335 return nil; 336 337 f->new = mkpath(old->new, elem); 338 f->elem = utfrrune(f->new, L'/') + 1; 339 p = getmode(mkaux, p, &f->mode); 340 p = getname(mkaux, p, f->uid, sizeof f->uid); 341 if(p == nil) 342 return nil; 343 344 if(!*f->uid) 345 strcpy(f->uid, "-"); 346 p = getname(mkaux, p, f->gid, sizeof f->gid); 347 if(p == nil) 348 return nil; 349 350 if(!*f->gid) 351 strcpy(f->gid, "-"); 352 f->old = getpath(p); 353 if(f->old && strcmp(f->old, "-") == 0){ 354 free(f->old); 355 f->old = 0; 356 } 357 setnames(mkaux, f); 358 359 return f; 360 } 361 362 static char* 363 getpath(char *p) 364 { 365 char *q, *new; 366 int c, n; 367 368 while((c = *p) == ' ' || c == '\t') 369 p++; 370 q = p; 371 while((c = *q) != '\n' && c != ' ' && c != '\t') 372 q++; 373 if(q == p) 374 return 0; 375 n = q - p; 376 new = emalloc(n + 1); 377 memcpy(new, p, n); 378 new[n] = 0; 379 return new; 380 } 381 382 static char* 383 getname(Mkaux *mkaux, char *p, char *buf, int len) 384 { 385 char *s; 386 int i, c; 387 388 while((c = *p) == ' ' || c == '\t') 389 p++; 390 i = 0; 391 while((c = *p) != '\n' && c != ' ' && c != '\t'){ 392 if(i < len) 393 buf[i++] = c; 394 p++; 395 } 396 if(i == len){ 397 buf[len-1] = '\0'; 398 warn(mkaux, "name %s too long; truncated", buf); 399 }else 400 buf[i] = '\0'; 401 402 if(buf[0] == '$'){ 403 s = getenv(buf+1); 404 if(s == 0){ 405 warn(mkaux, "can't read environment variable %s", buf+1); 406 skipdir(mkaux); 407 return nil; 408 } 409 strncpy(buf, s, NAMELEN-1); 410 buf[NAMELEN-1] = '\0'; 411 free(s); 412 } 413 return p; 414 } 415 416 static char* 417 getmode(Mkaux *mkaux, char *p, ulong *xmode) 418 { 419 char buf[7], *s; 420 ulong m; 421 422 *xmode = ~0; 423 p = getname(mkaux, p, buf, sizeof buf); 424 s = buf; 425 if(!*s || strcmp(s, "-") == 0) 426 return p; 427 m = 0; 428 if(*s == 'd'){ 429 m |= CHDIR; 430 s++; 431 } 432 if(*s == 'a'){ 433 m |= CHAPPEND; 434 s++; 435 } 436 if(*s == 'l'){ 437 m |= CHEXCL; 438 s++; 439 } 440 if(s[0] < '0' || s[0] > '7' 441 || s[1] < '0' || s[1] > '7' 442 || s[2] < '0' || s[2] > '7' 443 || s[3]){ 444 warn(mkaux, "bad mode specification %s", buf); 445 return p; 446 } 447 *xmode = m | strtoul(s, 0, 8); 448 return p; 449 } 450 451 static void 452 warn(Mkaux *mkaux, char *fmt, ...) 453 { 454 char buf[256]; 455 va_list va; 456 457 va_start(va, fmt); 458 doprint(buf, buf+sizeof(buf), fmt, va); 459 va_end(va); 460 461 if(mkaux->warn) 462 mkaux->warn(buf, mkaux->a); 463 else 464 fprint(2, "warning: %s\n", buf); 465 } 466