implement Newns;## Build a new namespace from a file## new create a new namespace from current directory (use cd)# fork split the namespace before modification# nodev disallow device attaches# bind [-abrci] from to# mount [-abrci9] [net!]machine[!svc] to [spec]# import [-abrci9] [net!]machine[!svc] [remotedir] dir# unmount [-i] [from] to# cd directory## -i to bind/mount/unmount means continue in the face of errors#include "sys.m"; sys: Sys; FD, FileIO, Connection: import Sys; stderr: ref FD; include "draw.m"; include "bufio.m"; bio: Bufio; Iobuf: import bio; include "newns.m"; #include "sh.m"; include "keyring.m"; kr: Keyring; include "security.m"; au: Auth; include "factotum.m"; include "arg.m"; arg: Arg; include "string.m"; str: String; newns(user: string, file: string): string{ sys = load Sys Sys->PATH; kr = load Keyring Keyring->PATH; stderr = sys->fildes(2); # Could do some authentication here, and bail if no good FIXME if(user == nil) ; bio = load Bufio Bufio->PATH; if(bio == nil) return sys->sprint("cannot load %s: %r", Bufio->PATH); arg = load Arg Arg->PATH; if (arg == nil) return sys->sprint("cannot load %s: %r", Arg->PATH); au = load Auth Auth->PATH; if(au == nil) return sys->sprint("cannot load %s: %r", Auth->PATH); err := au->init(); if(err != nil) return "Auth->init: "+err; str = load String String->PATH; # no check, because we'll live without it if(file == nil){ file = "namespace"; if(sys->stat(file).t0 < 0) file = "/lib/namespace"; } mfp := bio->open(file, bio->OREAD); if(mfp==nil) return sys->sprint("cannot open %q: %r", file); if(0 && user != nil){ sys->pctl(Sys->FORKENV, nil); setenv("user", user); setenv("home", "/usr/"+user); } facfd := sys->open("/mnt/factotum/rpc", Sys->ORDWR); return nsfile(mfp, facfd);} nsfile(b: ref Iobuf, facfd: ref Sys->FD): string{ e := ""; while((l := b.gets('\n')) != nil){ if(str != nil) slist := str->unquoted(l); else (nil, slist) = sys->tokenize(l, " \t\n\r"); # old way, in absence of String if(slist == nil) continue; e = nsop(expand(slist), facfd); if(e != "") break; } return e;} expand(l: list of string): list of string{ nl: list of string; for(; l != nil; l = tl l){ s := hd l; for(i := 0; i < len s; i++) if(s[i] == '$'){ for(j := i+1; j < len s; j++) if((c := s[j]) == '.' || c == '/' || c == '$') break; if(j > i+1){ (ok, v) := getenv(s[i+1:j]); if(!ok) return nil; s = s[0:i] + v + s[j:]; i = i + len v; } } nl = s :: nl; } l = nil; for(; nl != nil; nl = tl nl) l = hd nl :: l; return l;} nsop(argv: list of string, facfd: ref Sys->FD): string{ # ignore comments if(argv == nil || (hd argv)[0] == '#') return nil; e := ""; c := 0; cmdstr := hd argv; case cmdstr { "." => if(tl argv == nil) return ".: needs a filename"; nsf := hd tl argv; mfp := bio->open(nsf, bio->OREAD); if(mfp==nil) return sys->sprint("can't open %q for read %r", nsf); e = nsfile(mfp, facfd); "new" => c = Sys->NEWNS | Sys->FORKENV; "clear" => if(sys->pctl(Sys->FORKNS, nil) < 0 || sys->bind("#/", "/", Sys->MREPL) < 0 || sys->chdir("/") < 0 || sys->pctl(Sys->NEWNS, nil) < 0) return sys->sprint("%r"); return nil; "fork" => c = Sys->FORKNS; "nodev" => c = Sys->NODEVS; "bind" => e = bind(argv); "mount" => e = mount(argv, facfd); "unmount" => e = unmount(argv); "import" => e = import9(argv, facfd); "cd" => if(len argv != 2) return "cd: must have one argument"; if(sys->chdir(hd tl argv) < 0) return sys->sprint("%r"); * => e = "invalid namespace command"; } if(c != 0) { if(sys->pctl(c, nil) < 0) return sys->sprint("%r"); } return e;} Moptres: adt { argv: list of string; flags: int; alg: string; keyfile: string; ignore: int; use9: int;}; mopt(argv: list of string): (ref Moptres, string){ r := ref Moptres(nil, 0, "none", nil, 0, 0); arg->init(argv); while ((opt := arg->opt()) != 0) { case opt { 'i' => r.ignore = 1; 'a' => r.flags |= sys->MAFTER; 'b' => r.flags |= sys->MBEFORE; 'c' => r.flags |= sys->MCREATE; 'r' => r.flags |= sys->MREPL; 'k' => if((r.keyfile = arg->arg()) == nil) return (nil, "mount: missing arg to -k option"); 'C' => if((r.alg = arg->arg()) == nil) return (nil, "mount: missing arg to -C option"); '9' => r.use9 = 1; * => return (nil, sys->sprint("mount: bad option -%c", opt)); } } if((r.flags & (Sys->MAFTER|Sys->MBEFORE)) == 0) r.flags |= Sys->MREPL; r.argv = arg->argv(); return (r, nil);} bind(argv: list of string): string{ (r, err) := mopt(argv); if(err != nil) return err; if(len r.argv < 2) return "bind: too few args"; from := hd r.argv; r.argv = tl r.argv; todir := hd r.argv; if(sys->bind(from, todir, r.flags) < 0) return ig(r, sys->sprint("bind %s %s: %r", from, todir)); return nil;} mount(argv: list of string, facfd: ref Sys->FD): string{ fd: ref Sys->FD; (r, err) := mopt(argv); if(err != nil) return err; if(len r.argv < 2) return ig(r, "mount: too few args"); addr := hd r.argv; r.argv = tl r.argv; dest := netmkaddr(addr, "net", "styx"); dir := hd r.argv; r.argv = tl r.argv; if(r.argv != nil) spec := hd r.argv; (ok, c) := sys->dial(dest, nil); if(ok < 0) return ig(r, sys->sprint("dial: %s: %r", dest)); if(r.use9){ factotum := load Factotum Factotum->PATH; if(factotum == nil) return ig(r, sys->sprint("cannot load %s: %r", Factotum->PATH)); factotum->init(); afd := sys->fauth(fd, spec); if(afd != nil) factotum->proxy(afd, facfd, "proto=p9any role=client"); # ignore result; if it fails, mount will fail if(sys->mount(fd, afd, dir, r.flags, spec) < 0) return ig(r, sys->sprint("mount %q %q: %r", addr, dir)); return nil; } user := user(); kd := "/usr/" + user + "/keyring/"; cert: string; if (r.keyfile != nil) { cert = r.keyfile; if (cert[0] != '/') cert = kd + cert; (ok, nil) = sys->stat(cert); if (ok<0) return ig(r, sys->sprint("cannot find certificate %q: %r", cert)); } else { cert = kd + addr; (ok, nil) = sys->stat(cert); if(ok < 0){ cert = kd + "default"; (ok, nil) = sys->stat(cert); } } ai := kr->readauthinfo(cert); if(ai == nil) return ig(r, sys->sprint("cannot read certificate from %q: %r", cert)); err = au->init(); if (err != nil) return ig(r, sys->sprint("auth->init: %r")); (fd, err) = au->client(r.alg, ai, c.dfd); if(fd == nil) return ig(r, sys->sprint("auth: %r")); if(sys->mount(fd, nil, dir, r.flags, spec) < 0) return ig(r, sys->sprint("mount %q %q: %r", addr, dir)); return nil;} import9(argv: list of string, facfd: ref Sys->FD): string{ (r, err) := mopt(argv); if(err != nil) return err; if(len r.argv < 2) return "import: too few args"; if(facfd == nil) return ig(r, "import: no factotum"); factotum := load Factotum Factotum->PATH; if(factotum == nil) return ig(r, sys->sprint("cannot load %s: %r", Factotum->PATH)); factotum->init(); addr := hd r.argv; r.argv = tl r.argv; rdir := hd r.argv; r.argv = tl r.argv; dir := rdir; if(r.argv != nil) dir = hd r.argv; dest := netmkaddr(addr, "net", "17007"); # exportfs; might not be in inferno's ndb yet (ok, c) := sys->dial(dest, nil); if(ok < 0) return ig(r, sys->sprint("import: %s: %r", dest)); fd := c.dfd; if(factotum->proxy(fd, facfd, "proto=p9any role=client") == nil) return ig(r, sys->sprint("import: %s: %r", dest)); if(sys->fprint(fd, "%s", rdir) < 0) return ig(r, sys->sprint("import: %s: %r", dest)); buf := array[256] of byte; if((n := sys->read(fd, buf, len buf)) != 2 || buf[0] != byte 'O' || buf[1] != byte 'K'){ if(n >= 4) sys->werrstr(string buf[0:n]); return ig(r, sys->sprint("import: %s: %r", dest)); } # TO DO: new style: impo aan|nofilter clear|ssl|tls\n afd := sys->fauth(fd, ""); if(afd != nil) factotum->proxy(afd, facfd, "proto=p9any role=client"); if(sys->mount(fd, afd, dir, r.flags, "") < 0) return ig(r, sys->sprint("import %q %q: %r", addr, dir)); return nil;} unmount(argv: list of string): string{ (r, err) := mopt(argv); if(err != nil) return err; from, tu: string; case len r.argv { * => return "unmount: takes 1 or 2 args"; 1 => from = nil; tu = hd r.argv; 2 => from = hd r.argv; tu = hd tl r.argv; } if(sys->unmount(from, tu) < 0) return ig(r, sys->sprint("unmount: %r")); return nil;} ig(r: ref Moptres, e: string): string{ if(r.ignore) return nil; return e;} user(): string{ sys = load Sys Sys->PATH; fd := sys->open("/dev/user", sys->OREAD); if(fd == nil) return ""; buf := array[Sys->NAMEMAX] of byte; n := sys->read(fd, buf, len buf); if(n < 0) return ""; return string buf[0:n]; } getenv(name: string): (int, string){ fd := sys->open("#e/"+name, Sys->OREAD); if(fd == nil) return (0, nil); b := array[256] of byte; n := sys->read(fd, b, len b); if(n <= 0) return (1, ""); for(i := 0; i < n; i++) if(b[i] == byte 0 || b[i] == byte '\n') break; return (1, string b[0:i]);} setenv(name: string, val: string){ fd := sys->create("#e/"+name, Sys->OWRITE, 8r664); if(fd != nil) sys->fprint(fd, "%s", val);} netmkaddr(addr, net, svc: string): string{ if(net == nil) net = "net"; (n, nil) := sys->tokenize(addr, "!"); if(n <= 1){ if(svc== nil) return sys->sprint("%s!%s", net, addr); return sys->sprint("%s!%s!%s", net, addr, svc); } if(svc == nil || n > 2) return addr; return sys->sprint("%s!%s", addr, svc);} newuser(user: string, cap: string, nsfile: string): string{ if(cap == nil) return "no capability"; sys = load Sys Sys->PATH; fd := sys->open("#¤/capuse", Sys->OWRITE); if(fd == nil) return sys->sprint("opening #¤/capuse: %r"); b := array of byte cap; if(sys->write(fd, b, len b) < 0) return sys->sprint("writing %s to #¤/capuse: %r", cap); # mount factotum as new user (probably unhelpful if not factotum owner) sys->unmount(nil, "/mnt/factotum"); sys->bind("#sfactotum", "/mnt/factotum", Sys->MREPL); return newns(user, nsfile);}
implement Newns;
#
# Build a new namespace from a file
#
# new create a new namespace from current directory (use cd)
# fork split the namespace before modification
# nodev disallow device attaches
# bind [-abrci] from to
# mount [-abrci9] [net!]machine[!svc] to [spec]
# import [-abrci9] [net!]machine[!svc] [remotedir] dir
# unmount [-i] [from] to
# cd directory
#
# -i to bind/mount/unmount means continue in the face of errors
#
include "sys.m";
sys: Sys;
FD, FileIO, Connection: import Sys;
stderr: ref FD;
include "draw.m";
include "bufio.m";
bio: Bufio;
Iobuf: import bio;
include "newns.m";
#include "sh.m";
include "keyring.m";
kr: Keyring;
include "security.m";
au: Auth;
include "factotum.m";
include "arg.m";
arg: Arg;
include "string.m";
str: String;
newns(user: string, file: string): string
{
sys = load Sys Sys->PATH;
kr = load Keyring Keyring->PATH;
stderr = sys->fildes(2);
# Could do some authentication here, and bail if no good FIXME
if(user == nil)
;
bio = load Bufio Bufio->PATH;
if(bio == nil)
return sys->sprint("cannot load %s: %r", Bufio->PATH);
arg = load Arg Arg->PATH;
if (arg == nil)
return sys->sprint("cannot load %s: %r", Arg->PATH);
au = load Auth Auth->PATH;
if(au == nil)
return sys->sprint("cannot load %s: %r", Auth->PATH);
err := au->init();
if(err != nil)
return "Auth->init: "+err;
str = load String String->PATH; # no check, because we'll live without it
if(file == nil){
file = "namespace";
if(sys->stat(file).t0 < 0)
file = "/lib/namespace";
}
mfp := bio->open(file, bio->OREAD);
if(mfp==nil)
return sys->sprint("cannot open %q: %r", file);
if(0 && user != nil){
sys->pctl(Sys->FORKENV, nil);
setenv("user", user);
setenv("home", "/usr/"+user);
}
facfd := sys->open("/mnt/factotum/rpc", Sys->ORDWR);
return nsfile(mfp, facfd);
}
nsfile(b: ref Iobuf, facfd: ref Sys->FD): string
{
e := "";
while((l := b.gets('\n')) != nil){
if(str != nil)
slist := str->unquoted(l);
else
(nil, slist) = sys->tokenize(l, " \t\n\r"); # old way, in absence of String
if(slist == nil)
continue;
e = nsop(expand(slist), facfd);
if(e != "")
break;
}
return e;
}
expand(l: list of string): list of string
{
nl: list of string;
for(; l != nil; l = tl l){
s := hd l;
for(i := 0; i < len s; i++)
if(s[i] == '$'){
for(j := i+1; j < len s; j++)
if((c := s[j]) == '.' || c == '/' || c == '$')
break;
if(j > i+1){
(ok, v) := getenv(s[i+1:j]);
if(!ok)
return nil;
s = s[0:i] + v + s[j:];
i = i + len v;
}
}
nl = s :: nl;
}
l = nil;
for(; nl != nil; nl = tl nl)
l = hd nl :: l;
return l;
}
nsop(argv: list of string, facfd: ref Sys->FD): string
{
# ignore comments
if(argv == nil || (hd argv)[0] == '#')
return nil;
e := "";
c := 0;
cmdstr := hd argv;
case cmdstr {
"." =>
if(tl argv == nil)
return ".: needs a filename";
nsf := hd tl argv;
mfp := bio->open(nsf, bio->OREAD);
if(mfp==nil)
return sys->sprint("can't open %q for read %r", nsf);
e = nsfile(mfp, facfd);
"new" =>
c = Sys->NEWNS | Sys->FORKENV;
"clear" =>
if(sys->pctl(Sys->FORKNS, nil) < 0 ||
sys->bind("#/", "/", Sys->MREPL) < 0 ||
sys->chdir("/") < 0 ||
sys->pctl(Sys->NEWNS, nil) < 0)
return sys->sprint("%r");
return nil;
"fork" =>
c = Sys->FORKNS;
"nodev" =>
c = Sys->NODEVS;
"bind" =>
e = bind(argv);
"mount" =>
e = mount(argv, facfd);
"unmount" =>
e = unmount(argv);
"import" =>
e = import9(argv, facfd);
"cd" =>
if(len argv != 2)
return "cd: must have one argument";
if(sys->chdir(hd tl argv) < 0)
return sys->sprint("%r");
* =>
e = "invalid namespace command";
}
if(c != 0) {
if(sys->pctl(c, nil) < 0)
return sys->sprint("%r");
}
return e;
}
Moptres: adt {
argv: list of string;
flags: int;
alg: string;
keyfile: string;
ignore: int;
use9: int;
};
mopt(argv: list of string): (ref Moptres, string)
{
r := ref Moptres(nil, 0, "none", nil, 0, 0);
arg->init(argv);
while ((opt := arg->opt()) != 0) {
case opt {
'i' => r.ignore = 1;
'a' => r.flags |= sys->MAFTER;
'b' => r.flags |= sys->MBEFORE;
'c' => r.flags |= sys->MCREATE;
'r' => r.flags |= sys->MREPL;
'k' =>
if((r.keyfile = arg->arg()) == nil)
return (nil, "mount: missing arg to -k option");
'C' =>
if((r.alg = arg->arg()) == nil)
return (nil, "mount: missing arg to -C option");
'9' =>
r.use9 = 1;
* =>
return (nil, sys->sprint("mount: bad option -%c", opt));
}
}
if((r.flags & (Sys->MAFTER|Sys->MBEFORE)) == 0)
r.flags |= Sys->MREPL;
r.argv = arg->argv();
return (r, nil);
}
bind(argv: list of string): string
{
(r, err) := mopt(argv);
if(err != nil)
return err;
if(len r.argv < 2)
return "bind: too few args";
from := hd r.argv;
r.argv = tl r.argv;
todir := hd r.argv;
if(sys->bind(from, todir, r.flags) < 0)
return ig(r, sys->sprint("bind %s %s: %r", from, todir));
return nil;
}
mount(argv: list of string, facfd: ref Sys->FD): string
{
fd: ref Sys->FD;
(r, err) := mopt(argv);
if(err != nil)
return err;
if(len r.argv < 2)
return ig(r, "mount: too few args");
addr := hd r.argv;
r.argv = tl r.argv;
dest := netmkaddr(addr, "net", "styx");
dir := hd r.argv;
r.argv = tl r.argv;
if(r.argv != nil)
spec := hd r.argv;
(ok, c) := sys->dial(dest, nil);
if(ok < 0)
return ig(r, sys->sprint("dial: %s: %r", dest));
if(r.use9){
factotum := load Factotum Factotum->PATH;
if(factotum == nil)
return ig(r, sys->sprint("cannot load %s: %r", Factotum->PATH));
factotum->init();
afd := sys->fauth(fd, spec);
if(afd != nil)
factotum->proxy(afd, facfd, "proto=p9any role=client"); # ignore result; if it fails, mount will fail
if(sys->mount(fd, afd, dir, r.flags, spec) < 0)
return ig(r, sys->sprint("mount %q %q: %r", addr, dir));
return nil;
}
user := user();
kd := "/usr/" + user + "/keyring/";
cert: string;
if (r.keyfile != nil) {
cert = r.keyfile;
if (cert[0] != '/')
cert = kd + cert;
(ok, nil) = sys->stat(cert);
if (ok<0)
return ig(r, sys->sprint("cannot find certificate %q: %r", cert));
} else {
cert = kd + addr;
(ok, nil) = sys->stat(cert);
if(ok < 0){
cert = kd + "default";
(ok, nil) = sys->stat(cert);
}
}
ai := kr->readauthinfo(cert);
if(ai == nil)
return ig(r, sys->sprint("cannot read certificate from %q: %r", cert));
err = au->init();
if (err != nil)
return ig(r, sys->sprint("auth->init: %r"));
(fd, err) = au->client(r.alg, ai, c.dfd);
if(fd == nil)
return ig(r, sys->sprint("auth: %r"));
if(sys->mount(fd, nil, dir, r.flags, spec) < 0)
return ig(r, sys->sprint("mount %q %q: %r", addr, dir));
return nil;
}
import9(argv: list of string, facfd: ref Sys->FD): string
{
(r, err) := mopt(argv);
if(err != nil)
return err;
if(len r.argv < 2)
return "import: too few args";
if(facfd == nil)
return ig(r, "import: no factotum");
factotum := load Factotum Factotum->PATH;
if(factotum == nil)
return ig(r, sys->sprint("cannot load %s: %r", Factotum->PATH));
factotum->init();
addr := hd r.argv;
r.argv = tl r.argv;
rdir := hd r.argv;
r.argv = tl r.argv;
dir := rdir;
if(r.argv != nil)
dir = hd r.argv;
dest := netmkaddr(addr, "net", "17007"); # exportfs; might not be in inferno's ndb yet
(ok, c) := sys->dial(dest, nil);
if(ok < 0)
return ig(r, sys->sprint("import: %s: %r", dest));
fd := c.dfd;
if(factotum->proxy(fd, facfd, "proto=p9any role=client") == nil)
return ig(r, sys->sprint("import: %s: %r", dest));
if(sys->fprint(fd, "%s", rdir) < 0)
return ig(r, sys->sprint("import: %s: %r", dest));
buf := array[256] of byte;
if((n := sys->read(fd, buf, len buf)) != 2 || buf[0] != byte 'O' || buf[1] != byte 'K'){
if(n >= 4)
sys->werrstr(string buf[0:n]);
return ig(r, sys->sprint("import: %s: %r", dest));
}
# TO DO: new style: impo aan|nofilter clear|ssl|tls\n
afd := sys->fauth(fd, "");
if(afd != nil)
factotum->proxy(afd, facfd, "proto=p9any role=client");
if(sys->mount(fd, afd, dir, r.flags, "") < 0)
return ig(r, sys->sprint("import %q %q: %r", addr, dir));
return nil;
}
unmount(argv: list of string): string
{
(r, err) := mopt(argv);
if(err != nil)
return err;
from, tu: string;
case len r.argv {
* =>
return "unmount: takes 1 or 2 args";
1 =>
from = nil;
tu = hd r.argv;
2 =>
from = hd r.argv;
tu = hd tl r.argv;
}
if(sys->unmount(from, tu) < 0)
return ig(r, sys->sprint("unmount: %r"));
return nil;
}
ig(r: ref Moptres, e: string): string
{
if(r.ignore)
return nil;
return e;
}
user(): string
{
sys = load Sys Sys->PATH;
fd := sys->open("/dev/user", sys->OREAD);
if(fd == nil)
return "";
buf := array[Sys->NAMEMAX] of byte;
n := sys->read(fd, buf, len buf);
if(n < 0)
return "";
return string buf[0:n];
}
getenv(name: string): (int, string)
{
fd := sys->open("#e/"+name, Sys->OREAD);
if(fd == nil)
return (0, nil);
b := array[256] of byte;
n := sys->read(fd, b, len b);
if(n <= 0)
return (1, "");
for(i := 0; i < n; i++)
if(b[i] == byte 0 || b[i] == byte '\n')
break;
return (1, string b[0:i]);
}
setenv(name: string, val: string)
{
fd := sys->create("#e/"+name, Sys->OWRITE, 8r664);
if(fd != nil)
sys->fprint(fd, "%s", val);
}
netmkaddr(addr, net, svc: string): string
{
if(net == nil)
net = "net";
(n, nil) := sys->tokenize(addr, "!");
if(n <= 1){
if(svc== nil)
return sys->sprint("%s!%s", net, addr);
return sys->sprint("%s!%s!%s", net, addr, svc);
}
if(svc == nil || n > 2)
return addr;
return sys->sprint("%s!%s", addr, svc);
}
newuser(user: string, cap: string, nsfile: string): string
{
if(cap == nil)
return "no capability";
sys = load Sys Sys->PATH;
fd := sys->open("#¤/capuse", Sys->OWRITE);
if(fd == nil)
return sys->sprint("opening #¤/capuse: %r");
b := array of byte cap;
if(sys->write(fd, b, len b) < 0)
return sys->sprint("writing %s to #¤/capuse: %r", cap);
# mount factotum as new user (probably unhelpful if not factotum owner)
sys->unmount(nil, "/mnt/factotum");
sys->bind("#sfactotum", "/mnt/factotum", Sys->MREPL);
return newns(user, nsfile);
}