Blob


1 #include <u.h>
2 #include <libc.h>
3 #include <auth.h>
4 #include <bio.h>
6 #define mkdir plan9mkdir
7 #define getmode plan9_getmode
8 #define setuid plan9_setuid
10 enum{
11 LEN = 8*1024,
12 HUNKS = 128,
14 /*
15 * types of destination file sytems
16 */
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 void
86 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();
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();
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 }ARGEND
154 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;
181 lineno = 0;
182 indent = 0;
183 mkfs(&file, -1);
184 Bterm(b);
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);
195 exits(0);
198 void
199 mkfs(File *me, int level)
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);
216 while(child && indent > level){
217 if(mkfile(child))
218 mkfs(child, indent);
219 freefile(child);
220 child = getfile(me);
222 if(child){
223 freefile(child);
224 Bseek(b, -Blinelen(b), 1);
225 lineno--;
229 void
230 mktree(File *me, int rec)
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;
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);
257 close(fd);
260 int
261 mkfile(File *f)
263 Dir *dir;
265 if((dir = dirstat(oldfile)) == nil){
266 warn("can't stat file %q: %r", oldfile);
267 skipdir();
268 return 0;
270 return copyfile(f, dir, 0);
273 int
274 copyfile(File *f, Dir *d, int permonly)
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;
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);
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 = "";
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 else
306 d->mode = f->mode;
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 else
314 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);
328 return (d->mode & DMDIR) != 0;
331 /*
332 * check if file to is up to date with
333 * respect to the file represented by df
334 */
335 int
336 uptodate(Dir *df, char *to)
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;
348 void
349 copy(Dir *d)
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;
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;
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;
390 /*
391 * don't quit: pad to d->length (in pieces) to agree
392 * with the length in the header, already emitted.
393 */
394 memset(buf, 0, len);
395 n = len;
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;
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);
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);
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);
441 void
442 mkdir(Dir *d)
444 Dir *d1;
445 Dir nd;
446 int fd;
448 if(fskind == Archive){
449 arch(d);
450 return;
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);
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;
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);
478 void
479 arch(Dir *d)
481 Bprint(&bout, "%q %luo %q %q %lud %lld\n",
482 newfile, d->mode, d->uid, d->gid, d->mtime, d->length);
485 char *
486 mkpath(char *prefix, char *elem)
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;
497 char *
498 strdup(char *s)
500 char *t;
502 t = emalloc(strlen(s) + 1);
503 return strcpy(t, s);
506 void
507 setnames(File *f)
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 else
514 strcpy(oldfile, f->old);
515 }else
516 sprint(oldfile, "%s%s", oldroot, f->new);
517 if(strlen(newfile) >= sizeof newfile
518 || strlen(oldfile) >= sizeof oldfile)
519 error("name overfile");
522 void
523 freefile(File *f)
525 if(f->old)
526 free(f->old);
527 if(f->new)
528 free(f->new);
529 free(f);
532 /*
533 * skip all files in the proto that
534 * could be in the current dir
535 */
536 void
537 skipdir(void)
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;
553 while((c = *p++) != '\n')
554 if(c == ' ')
555 indent++;
556 else if(c == '\t')
557 indent += 8;
558 else
559 break;
560 if(indent <= level){
561 Bseek(b, -Blinelen(b), 1);
562 lineno--;
563 return;
568 File*
569 getfile(File *old)
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;
586 while((c = *p++) != '\n')
587 if(c == ' ')
588 indent++;
589 else if(c == '\t')
590 indent += 8;
591 else
592 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;
614 setnames(f);
616 if(debug)
617 printfile(f);
619 return f;
622 char*
623 getpath(char *p)
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;
642 char*
643 getname(char *p, char **buf)
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;
669 free(*buf);
670 *buf = s;
672 return p;
675 char*
676 getmode(char *p, ulong *xmode)
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++;
694 if(*s == 'a'){
695 m |= DMAPPEND;
696 s++;
698 if(*s == 'l'){
699 m |= DMEXCL;
700 s++;
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;
710 *xmode = m | strtoul(s, 0, 8);
711 free(buf);
712 return p;
715 void
716 setusers(void)
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;
754 void
755 mountkfs(char *name)
757 if(fskind != Kfs)
758 return;
759 sysfatal("no kfs: use -a or -d");
762 void
763 kfscmd(char *cmd)
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;
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;
788 void *
789 emalloc(ulong n)
791 void *p;
793 if((p = malloc(n)) == 0)
794 error("out of memory");
795 return p;
798 void
799 error(char *fmt, ...)
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);
814 void
815 warn(char *fmt, ...)
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);
827 void
828 printfile(File *f)
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 else
833 fprint(2, "%q %q %q %lo\n", f->new, f->uid, f->gid, f->mode);
836 void
837 usage(void)
839 fprint(2, "usage: %q [-aprvx] [-d root] [-n name] [-s source] [-u users] [-z n] proto ...\n", prog);
840 exits("usage");