summaryrefslogtreecommitdiff
path: root/appl/cmd/disk/mkfs.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd/disk/mkfs.b')
-rw-r--r--appl/cmd/disk/mkfs.b778
1 files changed, 778 insertions, 0 deletions
diff --git a/appl/cmd/disk/mkfs.b b/appl/cmd/disk/mkfs.b
new file mode 100644
index 00000000..8b07aa8f
--- /dev/null
+++ b/appl/cmd/disk/mkfs.b
@@ -0,0 +1,778 @@
+implement Mkfs;
+
+include "sys.m";
+ sys: Sys;
+ Dir, sprint, fprint: import sys;
+
+include "draw.m";
+
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+
+include "string.m";
+ str: String;
+
+include "arg.m";
+ arg: Arg;
+
+Mkfs: module
+{
+ init: fn(nil: ref Draw->Context, nil: list of string);
+};
+
+LEN: con Sys->ATOMICIO;
+HUNKS: con 128;
+
+Kfs, Fs, Archive: con iota; # types of destination file sytems
+
+File: adt {
+ new: string;
+ elem: string;
+ old: string;
+ uid: string;
+ gid: string;
+ mode: int;
+};
+
+b: ref Iobuf;
+bout: ref Iobuf; # stdout when writing archive
+newfile: string;
+oldfile: string;
+proto: string;
+cputype: string;
+users: string;
+oldroot: string;
+newroot: string;
+prog := "mkfs";
+lineno := 0;
+buf: array of byte;
+zbuf: array of byte;
+buflen := 1024-8;
+indent: int;
+verb: int;
+modes: int;
+ream: int;
+debug: int;
+xflag: int;
+qflag := 1;
+sfd: ref Sys->FD;
+fskind: int; # Kfs, Fs, Archive
+user: string;
+stderr: ref Sys->FD;
+usrid, grpid : string;
+setuid: int;
+
+usage()
+{
+ fprint(stderr, "usage: %s [-apqrvx] [-d root] [-n kfsname] [-s src-fs] [-u userfile] [-z n] proto ...\n", prog);
+ quit("usage");
+}
+
+init(nil: ref Draw->Context, args: list of string)
+{
+ sys = load Sys Sys->PATH;
+ bufio = load Bufio Bufio->PATH;
+ str = load String String->PATH;
+ arg = load Arg Arg->PATH;
+
+ sys->pctl(Sys->NEWPGRP|Sys->FORKNS|Sys->FORKFD, nil);
+
+ stderr = sys->fildes(2);
+ if(arg == nil)
+ error(sys->sprint("can't load %s: %r", Arg->PATH));
+
+ user = getuser();
+ if(user == nil)
+ user = "none";
+ name := "";
+ file := ref File;
+ file.new = "";
+ file.old = nil;
+ file.mode = 0;
+ oldroot = "";
+ newroot = "/n/kfs";
+ users = nil;
+ fskind = Kfs; # i suspect Inferno default should be different
+ arg->init(args);
+ while((c := arg->opt()) != 0)
+ case c {
+ 'a' =>
+ fskind = Archive;
+ newroot = "";
+ bout = bufio->fopen(sys->fildes(1), Sys->OWRITE);
+ if(bout == nil)
+ error(sys->sprint("can't open standard output for archive: %r"));
+ 'd' =>
+ fskind = Fs;
+ newroot = reqarg("destination directory (-d)");
+ 'D' =>
+ debug = 1;
+ 'n' =>
+ name = reqarg("kfs instance name (-n)");
+ 'p' =>
+ modes = 1;
+ 'q' =>
+ qflag = 0;
+ 'r' =>
+ ream = 1;
+ 's' =>
+ oldroot = reqarg("source directory (-d)");
+ 'u' =>
+ users = reqarg("/adm/users file (-u)");
+ 'v' =>
+ verb = 1;
+ 'x' =>
+ xflag = 1;
+ 'z' =>
+ (buflen, nil) = str->toint(reqarg("buffer length (-z)"), 10);
+ buflen -= 8; # qid.path and tag at end of each kfs block
+ 'U' =>
+ usrid = reqarg("user name (-U)");
+ 'G' =>
+ grpid = reqarg("group name (-G)");
+ 'S' =>
+ setuid = 1;
+ * =>
+ usage();
+ }
+
+ args = arg->argv();
+ if(args == nil)
+ usage();
+
+ buf = array [buflen] of byte;
+ zbuf = array [buflen] of { * => byte 0 };
+
+ mountkfs(name);
+ kfscmd("allow");
+ proto = "users";
+ setusers();
+ cputype = getenv("cputype");
+ if(cputype == nil)
+ cputype = "dis";
+
+ errs := 0;
+ for(; args != nil; args = tl args){
+ proto = hd args;
+ fprint(stderr, "processing %s\n", proto);
+
+ b = bufio->open(proto, Sys->OREAD);
+ if(b == nil){
+ fprint(stderr, "%s: can't open %s: %r: skipping\n", prog, proto);
+ errs++;
+ continue;
+ }
+
+ lineno = 0;
+ indent = 0;
+ mkfs(file, -1);
+ b.close();
+ }
+ fprint(stderr, "file system made\n");
+ kfscmd("disallow");
+ kfscmd("sync");
+ if(errs)
+ quit("skipped protos");
+ if(fskind == Archive){
+ bout.puts("end of archive\n");
+ if(bout.flush() == Bufio->ERROR)
+ error(sys->sprint("write error: %r"));
+ }
+}
+
+quit(why: string)
+{
+ if(bout != nil)
+ bout.flush();
+ if(why != nil)
+ raise "fail:"+why;
+ exit;
+}
+
+reqarg(what: string): string
+{
+ if((o := arg->arg()) == nil){
+ sys->fprint(stderr, "%s: missing %s\n", prog, what);
+ quit("usage");
+ }
+ return o;
+}
+
+mkfs(me: ref File, level: int)
+{
+ (child, fp) := getfile(me);
+ if(child == nil)
+ return;
+ if(child.elem == "+" || child.elem == "*" || child.elem == "%"){
+ rec := child.elem[0] == '+';
+ filesonly := child.elem[0] == '%';
+ child.new = me.new;
+ setnames(child);
+ mktree(child, rec, filesonly);
+ (child, fp) = getfile(me);
+ }
+ while(child != nil && indent > level){
+ if(mkfile(child))
+ mkfs(child, indent);
+ (child, fp) = getfile(me);
+ }
+ if(child != nil){
+ b.seek(fp, 0);
+ lineno--;
+ }
+}
+
+mktree(me: ref File, rec: int, filesonly: int)
+{
+ fd := sys->open(oldfile, Sys->OREAD);
+ if(fd == nil){
+ warn(sys->sprint("can't open %s: %r", oldfile));
+ return;
+ }
+
+ child := ref *me;
+ r := ref Rec(nil, 0);
+ for(;;){
+ (n, d) := sys->dirread(fd);
+ if(n <= 0)
+ break;
+ for(i := 0; i < n; i++)
+ if (!recall(d[i].name, r)) {
+ if(filesonly && d[i].mode & Sys->DMDIR)
+ continue;
+ child.new = mkpath(me.new, d[i].name);
+ if(me.old != nil)
+ child.old = mkpath(me.old, d[i].name);
+ child.elem = d[i].name;
+ setnames(child);
+ if(copyfile(child, ref d[i], 1) && rec)
+ mktree(child, rec, filesonly);
+ }
+ }
+}
+
+# Recall namespace fix
+# -- remove duplicates (could use Readdir->init(,Readdir->COMPACT))
+# obc
+
+Rec: adt
+{
+ ad: array of string;
+ l: int;
+};
+
+AL : con HUNKS;
+recall(e : string, r : ref Rec) : int
+{
+ if (r.ad == nil) r.ad = array[AL] of string;
+ # double array
+ if (r.l >= len r.ad) {
+ nar := array[2*(len r.ad)] of string;
+ nar[0:] = r.ad;
+ r.ad = nar;
+ }
+ for(i := 0; i < r.l; i++)
+ if (r.ad[i] == e) return 1;
+ r.ad[r.l++] = e;
+ return 0;
+}
+
+mkfile(f: ref File): int
+{
+ (i, dir) := sys->stat(oldfile);
+ if(i < 0){
+ warn(sys->sprint("can't stat file %s: %r", oldfile));
+ skipdir();
+ return 0;
+ }
+ return copyfile(f, ref dir, 0);
+}
+
+copyfile(f: ref File, d: ref Dir, permonly: int): int
+{
+ mode: int;
+
+ if(xflag && bout != nil){
+ bout.puts(sys->sprint("%s\t%d\t%bd\n", quoted(f.new), d.mtime, d.length));
+ return (d.mode & Sys->DMDIR) != 0;
+ }
+ d.name = f.elem;
+ if(d.dtype != 'M' && d.dtype != 'U'){ # hmm... Indeed!
+ d.uid = "inferno";
+ d.gid = "inferno";
+ mode = (d.mode >> 6) & 7;
+ d.mode |= mode | (mode << 3);
+ }
+ if(f.uid != "-")
+ d.uid = f.uid;
+ if(f.gid != "-")
+ d.gid = f.gid;
+ if(fskind == Fs && !setuid){ # new system: set to nil
+ d.uid = user;
+ d.gid = user;
+ }
+ if (usrid != nil)
+ d.uid = usrid;
+ if (grpid != nil)
+ d.gid = grpid;
+ if(f.mode != ~0){
+ if(permonly)
+ d.mode = (d.mode & ~8r666) | (f.mode & 8r666);
+ else if((d.mode&Sys->DMDIR) != (f.mode&Sys->DMDIR))
+ warn(sys->sprint("inconsistent mode for %s", f.new));
+ else
+ d.mode = f.mode;
+ }
+ if(!uptodate(d, newfile)){
+ if(d.mode & Sys->DMDIR)
+ mkdir(d);
+ else {
+ if(verb)
+ fprint(stderr, "%s\n", f.new);
+ copy(d);
+ }
+ }else if(modes){
+ nd := sys->nulldir;
+ nd.mode = d.mode;
+ nd.mtime = d.mtime;
+ nd.gid = d.gid;
+ if(sys->wstat(newfile, nd) < 0)
+ warn(sys->sprint("can't set modes for %s: %r", f.new));
+ # do the uid separately since different file systems object
+ nd = sys->nulldir;
+ nd.uid = d.uid;
+ sys->wstat(newfile, nd);
+ }
+ return (d.mode & Sys->DMDIR) != 0;
+}
+
+
+# check if file to is up to date with
+# respect to the file represented by df
+
+uptodate(df: ref Dir, newf: string): int
+{
+ if(fskind == Archive || ream)
+ return 0;
+ (i, dt) := sys->stat(newf);
+ if(i < 0)
+ return 0;
+ return dt.mtime >= df.mtime;
+}
+
+copy(d: ref Dir)
+{
+ t: ref Sys->FD;
+ n: int;
+
+ f := sys->open(oldfile, Sys->OREAD);
+ if(f == nil){
+ warn(sys->sprint("can't open %s: %r", oldfile));
+ return;
+ }
+ t = nil;
+ if(fskind == Archive)
+ arch(d);
+ else{
+ (dname, fname) := str->splitr(newfile, "/");
+ if(fname == nil)
+ error(sys->sprint("internal temporary file error (%s)", dname));
+ cptmp := dname+"__mkfstmp";
+ t = sys->create(cptmp, Sys->OWRITE, 8r666);
+ if(t == nil){
+ warn(sys->sprint("can't create %s: %r", newfile));
+ return;
+ }
+ }
+
+ for(tot := big 0;; tot += big n){
+ n = sys->read(f, buf, buflen);
+ if(n < 0){
+ warn(sys->sprint("can't read %s: %r", oldfile));
+ break;
+ }
+ if(n == 0)
+ break;
+ if(fskind == Archive){
+ if(bout.write(buf, n) != n)
+ error(sys->sprint("write error: %r"));
+ }else if(buf[0:buflen] == zbuf[0:buflen]){
+ if(sys->seek(t, big buflen, 1) < big 0)
+ error(sys->sprint("can't write zeros to %s: %r", newfile));
+ }else if(sys->write(t, buf, n) < n)
+ error(sys->sprint("can't write %s: %r", newfile));
+ }
+ f = nil;
+ if(tot != d.length){
+ warn(sys->sprint("wrong number bytes written to %s (was %bd should be %bd)",
+ newfile, tot, d.length));
+ if(fskind == Archive){
+ warn("seeking to proper position");
+ bout.seek(d.length - tot, 1);
+ }
+ }
+ if(fskind == Archive)
+ return;
+ sys->remove(newfile);
+ nd := sys->nulldir;
+ nd.name = d.name;
+ nd.mode = d.mode;
+ nd.mtime = d.mtime;
+ if(sys->fwstat(t, nd) < 0)
+ error(sys->sprint("can't move tmp file to %s: %r", newfile));
+ nd = sys->nulldir;
+ nd.gid = d.gid;
+ if(sys->fwstat(t, nd) < 0)
+ warn(sys->sprint("can't set group id of %s to %s: %r", newfile, d.gid));
+ nd.gid = nil;
+ nd.uid = d.uid;
+ sys->fwstat(t, nd);
+}
+
+mkdir(d: ref Dir)
+{
+ if(fskind == Archive){
+ arch(d);
+ return;
+ }
+ fd := sys->create(newfile, Sys->OREAD, d.mode);
+ nd := sys->nulldir;
+ nd.mode = d.mode;
+ nd.gid = d.gid;
+ nd.mtime = d.mtime;
+ if(fd == nil){
+ (i, d1) := sys->stat(newfile);
+ if(i < 0 || !(d1.mode & Sys->DMDIR))
+ error(sys->sprint("can't create %s", newfile));
+ if(sys->wstat(newfile, nd) < 0)
+ warn(sys->sprint("can't set modes for %s: %r", newfile));
+ nd = sys->nulldir;
+ nd.uid = d.uid;
+ sys->wstat(newfile, nd);
+ return;
+ }
+ if(sys->fwstat(fd, nd) < 0)
+ warn(sys->sprint("can't set modes for %s: %r", newfile));
+ nd = sys->nulldir;
+ nd.uid = d.uid;
+ sys->fwstat(fd, nd);
+}
+
+arch(d: ref Dir)
+{
+ bout.puts(sys->sprint("%s %s %s %s %ud %bd\n",
+ quoted(newfile), octal(d.mode), d.uid, d.gid, d.mtime, d.length));
+}
+
+mkpath(prefix, elem: string): string
+{
+ return sys->sprint("%s/%s", prefix, elem);
+}
+
+setnames(f: ref File)
+{
+ newfile = newroot+f.new;
+ if(f.old != nil){
+ if(f.old[0] == '/')
+ oldfile = oldroot+f.old;
+ else
+ oldfile = f.old;
+ }else
+ oldfile = oldroot+f.new;
+}
+
+#
+# skip all files in the proto that
+# could be in the current dir
+#
+skipdir()
+{
+ if(indent < 0)
+ return;
+ level := indent;
+ for(;;){
+ indent = 0;
+ fp := b.offset();
+ p := b.gets('\n');
+ lineno++;
+ if(p == nil){
+ indent = -1;
+ return;
+ }
+ for(j := 0; (c := p[j++]) != '\n';)
+ if(c == ' ')
+ indent++;
+ else if(c == '\t')
+ indent += 8;
+ else
+ break;
+ if(indent <= level){
+ b.seek(fp, 0);
+ lineno--;
+ return;
+ }
+ }
+}
+
+getfile(old: ref File): (ref File, big)
+{
+ f: ref File;
+ p, elem: string;
+ c: int;
+
+ if(indent < 0)
+ return (nil, big 0);
+ fp := b.offset();
+ do {
+ indent = 0;
+ p = b.gets('\n');
+ lineno++;
+ if(p == nil){
+ indent = -1;
+ return (nil, big 0);
+ }
+ for(; (c = p[0]) != '\n'; p = p[1:])
+ if(c == ' ')
+ indent++;
+ else if(c == '\t')
+ indent += 8;
+ else
+ break;
+ } while(c == '\n' || c == '#');
+ f = ref File;
+ (elem, p) = getname(p);
+ if(debug)
+ fprint(stderr, "getfile: %s root %s\n", elem, old.new);
+ f.new = mkpath(old.new, elem);
+ (nil, f.elem) = str->splitr(f.new, "/");
+ if(f.elem == nil)
+ error(sys->sprint("can't find file name component of %s", f.new));
+ (f.mode, p) = getmode(p);
+ (f.uid, p) = getname(p);
+ if(f.uid == nil)
+ f.uid = "-";
+ (f.gid, p) = getname(p);
+ if(f.gid == nil)
+ f.gid = "-";
+ f.old = getpath(p);
+ if(f.old == "-")
+ f.old = nil;
+ setnames(f);
+
+ if(debug)
+ printfile(f);
+
+ return (f, fp);
+}
+
+getpath(p: string): string
+{
+ for(; (c := p[0]) == ' ' || c == '\t'; p = p[1:])
+ ;
+ for(n := 0; (c = p[n]) != '\n' && c != ' ' && c != '\t'; n++)
+ ;
+ return p[0:n];
+}
+
+getname(p: string): (string, string)
+{
+ for(; (c := p[0]) == ' ' || c == '\t'; p = p[1:])
+ ;
+ i := 0;
+ s := "";
+ quoted := 0;
+ for(; (c = p[0]) != '\n' && (c != ' ' && c != '\t' || quoted); p = p[1:]){
+ if(quoted && c == '\'' && p[1] == '\'')
+ p = p[1:];
+ else if(c == '\'' && qflag){
+ quoted = !quoted;
+ continue;
+ }
+ s[i++] = c;
+ }
+ if(len s > 0 && s[0] == '$'){
+ s = getenv(s[1:]);
+ if(s == nil)
+ error(sys->sprint("can't read environment variable %s", s));
+ }
+ return (s, p);
+}
+
+getenv(s: string): string
+{
+ if(s == "user")
+ return getuser();
+ return readfile("/env/"+s);
+}
+
+getuser(): string
+{
+ return readfile("/dev/user");
+}
+
+readfile(f: string): string
+{
+ fd := sys->open(f, Sys->OREAD);
+ if(fd != nil){
+ a := array[256] of byte;
+ n := sys->read(fd, a, len a);
+ if(n > 0)
+ return string a[0:n];
+ }
+ return nil;
+}
+
+getmode(p: string): (int, string)
+{
+ s: string;
+
+ (s, p) = getname(p);
+ if(s == nil || s == "-")
+ return (~0, p);
+ os := s;
+ m := 0;
+ if(s[0] == 'd'){
+ m |= Sys->DMDIR;
+ s = s[1:];
+ }
+ if(s[0] == 'a'){
+ m |= Sys->DMAPPEND;
+ s = s[1:];
+ }
+ if(s[0] == 'l'){
+ m |= Sys->DMEXCL;
+ s = s[1:];
+ }
+
+ for(i:=0; i<len s || i < 3; i++)
+ if(i >= len s || !(s[i]>='0' && s[i]<='7')){
+ warn(sys->sprint("bad mode specification %s", os));
+ return (~0, p);
+ }
+ (v, nil) := str->toint(s, 8);
+ return (m|v, p);
+}
+
+quoted(s: string): string
+{
+ if(qflag)
+ return sys->sprint("%q", s);
+ return s;
+}
+
+setusers()
+{
+ if(fskind != Kfs)
+ return;
+ file := ref File;
+ m := modes;
+ modes = 1;
+ file.uid = "adm";
+ file.gid = "adm";
+ file.mode = Sys->DMDIR|8r775;
+ file.new = "/adm";
+ file.elem = "adm";
+ file.old = nil;
+ setnames(file);
+ mkfile(file);
+ file.new = "/adm/users";
+ file.old = users;
+ file.elem = "users";
+ file.mode = 8r664;
+ setnames(file);
+ mkfile(file);
+ kfscmd("user");
+ mkfile(file);
+ file.mode = Sys->DMDIR|8r775;
+ file.new = "/adm";
+ file.old = "/adm";
+ file.elem = "adm";
+ setnames(file);
+ mkfile(file);
+ modes = m;
+}
+
+# this isn't right for the current #K
+mountkfs(name: string)
+{
+ kname: string;
+
+ if(fskind != Kfs)
+ return;
+ if(name != nil)
+ kname = sys->sprint("/srv/kfs.%s", name);
+ else
+ kname = "/srv/kfs";
+ fd := sys->open(kname, Sys->ORDWR);
+ if(fd == nil){
+ fprint(stderr, "%s: can't open %s: %r\n", prog, kname);
+ quit("open kfs");
+ }
+ if(sys->mount(fd, nil, "/n/kfs", Sys->MREPL|Sys->MCREATE, "") < 0){
+ fprint(stderr, "%s: can't mount kfs on /n/kfs: %r\n", prog);
+ quit("mount kfs");
+ }
+ kname += ".cmd";
+ sfd = sys->open(kname, Sys->ORDWR);
+ if(sfd == nil){
+ fprint(stderr, "%s: can't open %s: %r\n", prog, kname);
+ quit("open kfscmd");
+ }
+}
+
+kfscmd(cmd: string)
+{
+ if(fskind != Kfs || sfd == nil)
+ return;
+ a := array of byte cmd;
+ if(sys->write(sfd, a, len a) != len a){
+ fprint(stderr, "%s: error writing %s: %r", prog, cmd);
+ return;
+ }
+ for(;;){
+ reply := array[4*1024] of byte;
+ n := sys->read(sfd, reply, len reply);
+ if(n <= 0)
+ return;
+ s := string reply[0:n];
+ if(s == "done" || s == "success")
+ return;
+ if(s == "unknown command"){
+ fprint(stderr, "%s: command %s not recognized\n", prog, cmd);
+ return;
+ }
+ }
+}
+
+error(s: string)
+{
+ fprint(stderr, "%s: %s: %d: %s\n", prog, proto, lineno, s);
+ kfscmd("disallow");
+ kfscmd("sync");
+ quit("error");
+}
+
+warn(s: string)
+{
+ fprint(stderr, "%s: %s: %d: %s\n", prog, proto, lineno, s);
+}
+
+printfile(f: ref File)
+{
+ if(f.old != nil)
+ fprint(stderr, "%s from %s %s %s %s\n", f.new, f.old, f.uid, f.gid, octal(f.mode));
+ else
+ fprint(stderr, "%s %s %s %s\n", f.new, f.uid, f.gid, octal(f.mode));
+}
+
+octal(i: int): string
+{
+ s := "";
+ do {
+ t: string;
+ t[0] = '0' + (i&7);
+ s = t+s;
+ } while((i = (i>>3)&~(7<<29)) != 0);
+ return s;
+}