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