Blob
1 #include <u.h>2 #include <libc.h>3 #include <auth.h>4 #include <bio.h>6 #define mkdir plan9mkdir7 #define getmode plan9_getmode8 #define setuid plan9_setuid10 enum{11 LEN = 8*1024,12 HUNKS = 128,14 /*15 * types of destination file sytems16 */17 Kfs = 0,18 Fs,19 Archive,20 };22 typedef struct File File;24 struct File{25 char *new;26 char *elem;27 char *old;28 char *uid;29 char *gid;30 ulong mode;31 };33 void arch(Dir*);34 void copy(Dir*);35 int copyfile(File*, Dir*, int);36 void* emalloc(ulong);37 void error(char *, ...);38 void freefile(File*);39 File* getfile(File*);40 char* getmode(char*, ulong*);41 char* getname(char*, char**);42 char* getpath(char*);43 void kfscmd(char *);44 void mkdir(Dir*);45 int mkfile(File*);46 void mkfs(File*, int);47 char* mkpath(char*, char*);48 void mktree(File*, int);49 void mountkfs(char*);50 void printfile(File*);51 void setnames(File*);52 void setusers(void);53 void skipdir(void);54 char* strdup(char*);55 int uptodate(Dir*, char*);56 void usage(void);57 void warn(char *, ...);59 Biobuf *b;60 Biobuf bout; /* stdout when writing archive */61 uchar boutbuf[2*LEN];62 char newfile[LEN];63 char oldfile[LEN];64 char *proto;65 char *cputype;66 char *users;67 char *oldroot;68 char *newroot;69 char *prog = "mkfs";70 int lineno;71 char *buf;72 char *zbuf;73 int buflen = 1024-8;74 int indent;75 int verb;76 int modes;77 int ream;78 int debug;79 int xflag;80 int sfd;81 int fskind; /* Kfs, Fs, Archive */82 int setuid; /* on Fs: set uid and gid? */83 char *user;85 void86 main(int argc, char **argv)87 {88 File file;89 char *name;90 int i, errs;92 quotefmtinstall();93 user = getuser();94 name = "";95 memset(&file, 0, sizeof file);96 file.new = "";97 file.old = 0;98 oldroot = "";99 newroot = "/n/kfs";100 users = 0;101 fskind = Kfs;102 ARGBEGIN{103 case 'a':104 if(fskind != Kfs) {105 fprint(2, "cannot use -a with -d\n");106 usage();107 }108 fskind = Archive;109 newroot = "";110 Binits(&bout, 1, OWRITE, boutbuf, sizeof boutbuf);111 break;112 case 'd':113 if(fskind != Kfs) {114 fprint(2, "cannot use -d with -a\n");115 usage();116 }117 fskind = Fs;118 newroot = ARGF();119 break;120 case 'D':121 debug = 1;122 break;123 case 'n':124 name = EARGF(usage());125 break;126 case 'p':127 modes = 1;128 break;129 case 'r':130 ream = 1;131 break;132 case 's':133 oldroot = ARGF();134 break;135 case 'u':136 users = ARGF();137 break;138 case 'U':139 setuid = 1;140 break;141 case 'v':142 verb = 1;143 break;144 case 'x':145 xflag = 1;146 break;147 case 'z':148 buflen = atoi(ARGF())-8;149 break;150 default:151 usage();152 }ARGEND154 if(!argc)155 usage();157 buf = emalloc(buflen);158 zbuf = emalloc(buflen);159 memset(zbuf, 0, buflen);161 mountkfs(name);162 kfscmd("allow");163 proto = "users";164 setusers();165 cputype = getenv("cputype");166 if(cputype == 0)167 cputype = "68020";169 errs = 0;170 for(i = 0; i < argc; i++){171 proto = argv[i];172 fprint(2, "processing %q\n", proto);174 b = Bopen(proto, OREAD);175 if(!b){176 fprint(2, "%q: can't open %q: skipping\n", prog, proto);177 errs++;178 continue;179 }181 lineno = 0;182 indent = 0;183 mkfs(&file, -1);184 Bterm(b);185 }186 fprint(2, "file system made\n");187 kfscmd("disallow");188 kfscmd("sync");189 if(errs)190 exits("skipped protos");191 if(fskind == Archive){192 Bprint(&bout, "end of archive\n");193 Bterm(&bout);194 }195 exits(0);196 }198 void199 mkfs(File *me, int level)200 {201 File *child;202 int rec;204 child = getfile(me);205 if(!child)206 return;207 if((child->elem[0] == '+' || child->elem[0] == '*') && child->elem[1] == '\0'){208 rec = child->elem[0] == '+';209 free(child->new);210 child->new = strdup(me->new);211 setnames(child);212 mktree(child, rec);213 freefile(child);214 child = getfile(me);215 }216 while(child && indent > level){217 if(mkfile(child))218 mkfs(child, indent);219 freefile(child);220 child = getfile(me);221 }222 if(child){223 freefile(child);224 Bseek(b, -Blinelen(b), 1);225 lineno--;226 }227 }229 void230 mktree(File *me, int rec)231 {232 File child;233 Dir *d;234 int i, n, fd;236 fd = open(oldfile, OREAD);237 if(fd < 0){238 warn("can't open %q: %r", oldfile);239 return;240 }242 child = *me;243 while((n = dirread(fd, &d)) > 0){244 for(i = 0; i < n; i++){245 child.new = mkpath(me->new, d[i].name);246 if(me->old)247 child.old = mkpath(me->old, d[i].name);248 child.elem = d[i].name;249 setnames(&child);250 if(copyfile(&child, &d[i], 1) && rec)251 mktree(&child, rec);252 free(child.new);253 if(child.old)254 free(child.old);255 }256 }257 close(fd);258 }260 int261 mkfile(File *f)262 {263 Dir *dir;265 if((dir = dirstat(oldfile)) == nil){266 warn("can't stat file %q: %r", oldfile);267 skipdir();268 return 0;269 }270 return copyfile(f, dir, 0);271 }273 int274 copyfile(File *f, Dir *d, int permonly)275 {276 ulong mode;277 Dir nd;279 if(xflag){280 Bprint(&bout, "%q\t%ld\t%lld\n", f->new, d->mtime, d->length);281 return (d->mode & DMDIR) != 0;282 }283 if(verb && (fskind == Archive || ream))284 fprint(2, "%q\n", f->new);285 d->name = f->elem;286 if(d->type != 'M' && d->type != 'Z'){287 d->uid = "sys";288 d->gid = "sys";289 mode = (d->mode >> 6) & 7;290 d->mode |= mode | (mode << 3);291 }292 if(strcmp(f->uid, "-") != 0)293 d->uid = f->uid;294 if(strcmp(f->gid, "-") != 0)295 d->gid = f->gid;296 if(fskind == Fs && !setuid){297 d->uid = "";298 d->gid = "";299 }300 if(f->mode != ~0){301 if(permonly)302 d->mode = (d->mode & ~0666) | (f->mode & 0666);303 else if((d->mode&DMDIR) != (f->mode&DMDIR))304 warn("inconsistent mode for %q", f->new);305 else306 d->mode = f->mode;307 }308 if(!uptodate(d, newfile)){309 if(verb && (fskind != Archive && ream == 0))310 fprint(2, "%q\n", f->new);311 if(d->mode & DMDIR)312 mkdir(d);313 else314 copy(d);315 }else if(modes){316 nulldir(&nd);317 nd.mode = d->mode;318 nd.gid = d->gid;319 nd.mtime = d->mtime;320 if(verb && (fskind != Archive && ream == 0))321 fprint(2, "%q\n", f->new);322 if(dirwstat(newfile, &nd) < 0)323 warn("can't set modes for %q: %r", f->new);324 nulldir(&nd);325 nd.uid = d->uid;326 dirwstat(newfile, &nd);327 }328 return (d->mode & DMDIR) != 0;329 }331 /*332 * check if file to is up to date with333 * respect to the file represented by df334 */335 int336 uptodate(Dir *df, char *to)337 {338 int ret;339 Dir *dt;341 if(fskind == Archive || ream || (dt = dirstat(to)) == nil)342 return 0;343 ret = dt->mtime >= df->mtime;344 free(dt);345 return ret;346 }348 void349 copy(Dir *d)350 {351 char cptmp[LEN], *p;352 int f, t, n, needwrite, nowarnyet = 1;353 vlong tot, len;354 Dir nd;356 f = open(oldfile, OREAD);357 if(f < 0){358 warn("can't open %q: %r", oldfile);359 return;360 }361 t = -1;362 if(fskind == Archive)363 arch(d);364 else{365 strcpy(cptmp, newfile);366 p = utfrrune(cptmp, L'/');367 if(!p)368 error("internal temporary file error");369 strcpy(p+1, "__mkfstmp");370 t = create(cptmp, OWRITE, 0666);371 if(t < 0){372 warn("can't create %q: %r", newfile);373 close(f);374 return;375 }376 }378 needwrite = 0;379 for(tot = 0; tot < d->length; tot += n){380 len = d->length - tot;381 /* don't read beyond d->length */382 if (len > buflen)383 len = buflen;384 n = read(f, buf, len);385 if(n <= 0) {386 if(n < 0 && nowarnyet) {387 warn("can't read %q: %r", oldfile);388 nowarnyet = 0;389 }390 /*391 * don't quit: pad to d->length (in pieces) to agree392 * with the length in the header, already emitted.393 */394 memset(buf, 0, len);395 n = len;396 }397 if(fskind == Archive){398 if(Bwrite(&bout, buf, n) != n)399 error("write error: %r");400 }else if(memcmp(buf, zbuf, n) == 0){401 if(seek(t, n, 1) < 0)402 error("can't write zeros to %q: %r", newfile);403 needwrite = 1;404 }else{405 if(write(t, buf, n) < n)406 error("can't write %q: %r", newfile);407 needwrite = 0;408 }409 }410 close(f);411 if(needwrite){412 if(seek(t, -1, 1) < 0 || write(t, zbuf, 1) != 1)413 error("can't write zero at end of %q: %r", newfile);414 }415 if(tot != d->length){416 /* this should no longer happen */417 warn("wrong number of bytes written to %q (was %lld should be %lld)\n",418 newfile, tot, d->length);419 if(fskind == Archive){420 warn("seeking to proper position\n");421 /* does no good if stdout is a pipe */422 Bseek(&bout, d->length - tot, 1);423 }424 }425 if(fskind == Archive)426 return;427 remove(newfile);428 nulldir(&nd);429 nd.mode = d->mode;430 nd.gid = d->gid;431 nd.mtime = d->mtime;432 nd.name = d->name;433 if(dirfwstat(t, &nd) < 0)434 error("can't move tmp file to %q: %r", newfile);435 nulldir(&nd);436 nd.uid = d->uid;437 dirfwstat(t, &nd);438 close(t);439 }441 void442 mkdir(Dir *d)443 {444 Dir *d1;445 Dir nd;446 int fd;448 if(fskind == Archive){449 arch(d);450 return;451 }452 fd = create(newfile, OREAD, d->mode);453 nulldir(&nd);454 nd.mode = d->mode;455 nd.gid = d->gid;456 nd.mtime = d->mtime;457 if(fd < 0){458 if((d1 = dirstat(newfile)) == nil || !(d1->mode & DMDIR)){459 free(d1);460 error("can't create %q", newfile);461 }462 free(d1);463 if(dirwstat(newfile, &nd) < 0)464 warn("can't set modes for %q: %r", newfile);465 nulldir(&nd);466 nd.uid = d->uid;467 dirwstat(newfile, &nd);468 return;469 }470 if(dirfwstat(fd, &nd) < 0)471 warn("can't set modes for %q: %r", newfile);472 nulldir(&nd);473 nd.uid = d->uid;474 dirfwstat(fd, &nd);475 close(fd);476 }478 void479 arch(Dir *d)480 {481 Bprint(&bout, "%q %luo %q %q %lud %lld\n",482 newfile, d->mode, d->uid, d->gid, d->mtime, d->length);483 }485 char *486 mkpath(char *prefix, char *elem)487 {488 char *p;489 int n;491 n = strlen(prefix) + strlen(elem) + 2;492 p = emalloc(n);493 sprint(p, "%s/%s", prefix, elem);494 return p;495 }497 char *498 strdup(char *s)499 {500 char *t;502 t = emalloc(strlen(s) + 1);503 return strcpy(t, s);504 }506 void507 setnames(File *f)508 {509 sprint(newfile, "%s%s", newroot, f->new);510 if(f->old){511 if(f->old[0] == '/')512 sprint(oldfile, "%s%s", oldroot, f->old);513 else514 strcpy(oldfile, f->old);515 }else516 sprint(oldfile, "%s%s", oldroot, f->new);517 if(strlen(newfile) >= sizeof newfile518 || strlen(oldfile) >= sizeof oldfile)519 error("name overfile");520 }522 void523 freefile(File *f)524 {525 if(f->old)526 free(f->old);527 if(f->new)528 free(f->new);529 free(f);530 }532 /*533 * skip all files in the proto that534 * could be in the current dir535 */536 void537 skipdir(void)538 {539 char *p, c;540 int level;542 if(indent < 0 || b == nil) /* b is nil when copying adm/users */543 return;544 level = indent;545 for(;;){546 indent = 0;547 p = Brdline(b, '\n');548 lineno++;549 if(!p){550 indent = -1;551 return;552 }553 while((c = *p++) != '\n')554 if(c == ' ')555 indent++;556 else if(c == '\t')557 indent += 8;558 else559 break;560 if(indent <= level){561 Bseek(b, -Blinelen(b), 1);562 lineno--;563 return;564 }565 }566 }568 File*569 getfile(File *old)570 {571 File *f;572 char *elem;573 char *p;574 int c;576 if(indent < 0)577 return 0;578 loop:579 indent = 0;580 p = Brdline(b, '\n');581 lineno++;582 if(!p){583 indent = -1;584 return 0;585 }586 while((c = *p++) != '\n')587 if(c == ' ')588 indent++;589 else if(c == '\t')590 indent += 8;591 else592 break;593 if(c == '\n' || c == '#')594 goto loop;595 p--;596 f = emalloc(sizeof *f);597 p = getname(p, &elem);598 if(debug)599 fprint(2, "getfile: %q root %q\n", elem, old->new);600 f->new = mkpath(old->new, elem);601 f->elem = utfrrune(f->new, L'/') + 1;602 p = getmode(p, &f->mode);603 p = getname(p, &f->uid);604 if(!*f->uid)605 f->uid = "-";606 p = getname(p, &f->gid);607 if(!*f->gid)608 f->gid = "-";609 f->old = getpath(p);610 if(f->old && strcmp(f->old, "-") == 0){611 free(f->old);612 f->old = 0;613 }614 setnames(f);616 if(debug)617 printfile(f);619 return f;620 }622 char*623 getpath(char *p)624 {625 char *q, *new;626 int c, n;628 while((c = *p) == ' ' || c == '\t')629 p++;630 q = p;631 while((c = *q) != '\n' && c != ' ' && c != '\t')632 q++;633 if(q == p)634 return 0;635 n = q - p;636 new = emalloc(n + 1);637 memcpy(new, p, n);638 new[n] = 0;639 return new;640 }642 char*643 getname(char *p, char **buf)644 {645 char *s, *start;646 int c;648 while((c = *p) == ' ' || c == '\t')649 p++;651 start = p;652 while((c = *p) != '\n' && c != ' ' && c != '\t' && c != '\0')653 p++;655 *buf = malloc(p+1-start);656 if(*buf == nil)657 return nil;658 memmove(*buf, start, p-start);659 (*buf)[p-start] = '\0';661 if(**buf == '$'){662 s = getenv(*buf+1);663 if(s == 0){664 warn("can't read environment variable %q", *buf+1);665 skipdir();666 free(*buf);667 return nil;668 }669 free(*buf);670 *buf = s;671 }672 return p;673 }675 char*676 getmode(char *p, ulong *xmode)677 {678 char *buf, *s;679 ulong m;681 *xmode = ~0;682 p = getname(p, &buf);683 if(p == nil)684 return nil;686 s = buf;687 if(!*s || strcmp(s, "-") == 0)688 return p;689 m = 0;690 if(*s == 'd'){691 m |= DMDIR;692 s++;693 }694 if(*s == 'a'){695 m |= DMAPPEND;696 s++;697 }698 if(*s == 'l'){699 m |= DMEXCL;700 s++;701 }702 if(s[0] < '0' || s[0] > '7'703 || s[1] < '0' || s[1] > '7'704 || s[2] < '0' || s[2] > '7'705 || s[3]){706 warn("bad mode specification %q", buf);707 free(buf);708 return p;709 }710 *xmode = m | strtoul(s, 0, 8);711 free(buf);712 return p;713 }715 void716 setusers(void)717 {718 File file;719 int m;721 if(fskind != Kfs)722 return;723 m = modes;724 modes = 1;725 file.uid = "adm";726 file.gid = "adm";727 file.mode = DMDIR|0775;728 file.new = "/adm";729 file.elem = "adm";730 file.old = 0;731 setnames(&file);732 strcpy(oldfile, file.new); /* Don't use root for /adm */733 mkfile(&file);734 file.new = "/adm/users";735 file.old = users;736 file.elem = "users";737 file.mode = 0664;738 setnames(&file);739 if (file.old)740 strcpy(oldfile, file.old); /* Don't use root for /adm/users */741 mkfile(&file);742 kfscmd("user");743 mkfile(&file);744 file.mode = DMDIR|0775;745 file.new = "/adm";746 file.old = "/adm";747 file.elem = "adm";748 setnames(&file);749 strcpy(oldfile, file.old); /* Don't use root for /adm */750 mkfile(&file);751 modes = m;752 }754 void755 mountkfs(char *name)756 {757 if(fskind != Kfs)758 return;759 sysfatal("no kfs: use -a or -d");760 }762 void763 kfscmd(char *cmd)764 {765 char buf[4*1024];766 int n;768 if(fskind != Kfs)769 return;770 if(write(sfd, cmd, strlen(cmd)) != strlen(cmd)){771 fprint(2, "%q: error writing %q: %r", prog, cmd);772 return;773 }774 for(;;){775 n = read(sfd, buf, sizeof buf - 1);776 if(n <= 0)777 return;778 buf[n] = '\0';779 if(strcmp(buf, "done") == 0 || strcmp(buf, "success") == 0)780 return;781 if(strcmp(buf, "unknown command") == 0){782 fprint(2, "%q: command %q not recognized\n", prog, cmd);783 return;784 }785 }786 }788 void *789 emalloc(ulong n)790 {791 void *p;793 if((p = malloc(n)) == 0)794 error("out of memory");795 return p;796 }798 void799 error(char *fmt, ...)800 {801 char buf[1024];802 va_list arg;804 sprint(buf, "%q: %q:%d: ", prog, proto, lineno);805 va_start(arg, fmt);806 vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg);807 va_end(arg);808 fprint(2, "%s\n", buf);809 kfscmd("disallow");810 kfscmd("sync");811 exits(0);812 }814 void815 warn(char *fmt, ...)816 {817 char buf[1024];818 va_list arg;820 sprint(buf, "%q: %q:%d: ", prog, proto, lineno);821 va_start(arg, fmt);822 vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg);823 va_end(arg);824 fprint(2, "%s\n", buf);825 }827 void828 printfile(File *f)829 {830 if(f->old)831 fprint(2, "%q from %q %q %q %lo\n", f->new, f->old, f->uid, f->gid, f->mode);832 else833 fprint(2, "%q %q %q %lo\n", f->new, f->uid, f->gid, f->mode);834 }836 void837 usage(void)838 {839 fprint(2, "usage: %q [-aprvx] [-d root] [-n name] [-s source] [-u users] [-z n] proto ...\n", prog);840 exits("usage");841 }