Plan 9 from Bell Labs’s /usr/web/sources/contrib/nemo/octopus/port/lmount.b

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


implement Mount;

# Exactly like mount(1), but can insert delays to pretend the
# link to the file server has a bad latency, for testing other services.

include "sys.m";
	sys: Sys;
include "draw.m";
include "keyring.m";
include "security.m";
include "factotum.m";
include "styxconv.m";
include "styxpersist.m";
include "arg.m";
include "sh.m";

Mount: module
{
	init:	 fn(nil: ref Draw->Context, nil: list of string);
};

verbose := 0;
doauth := 1;
do9 := 0;
oldstyx := 0;
persist := 0;
showstyx := 0;
quiet := 0;

alg := "none";
keyfile: string;
spec: string;
addr: string;
latency := 0;

fail(status, msg: string)
{
	sys->fprint(sys->fildes(2), "mount: %s\n", msg);
	raise "fail:"+status;
}

nomod(mod: string)
{
	fail("load", sys->sprint("can't load %s: %r", mod));
}

init(ctxt: ref Draw->Context, args: list of string)
{
	sys = load Sys Sys->PATH;
	arg := load Arg Arg->PATH;
	if(arg == nil)
		nomod(Arg->PATH);

	arg->init(args);
	arg->setusage("o/lmount [-L lat] [-a|-b] [-coA9] [-C cryptoalg] [-k keyfile] [-q] net!addr|file|{command} mountpoint [spec]");
	flags := 0;
	while((o := arg->opt()) != 0){
		case o {
		'a' =>
			flags |= Sys->MAFTER;
		'b' =>
			flags |= Sys->MBEFORE;
		'c' =>
			flags |= Sys->MCREATE;
		'C' =>
			alg = arg->earg();
		'k' or
		'f' =>
			keyfile = arg->earg();
		'A' =>
			doauth = 0;
		'9' =>
			doauth = 0;
			do9 = 1;
		'o' =>
			oldstyx = 1;
		'v' =>
			verbose = 1;
		'P' =>
			persist = 1;
		'S' =>
			showstyx = 1;
		'q' =>
			quiet = 1;
		'L' =>
			latency = int arg->earg();
		*   =>
			arg->usage();
		}
	}
	args = arg->argv();
	if(len args != 2){
		if(len args != 3)
			arg->usage();
		spec = hd tl tl args;
	}
	arg = nil;
	addr = hd args;
	mountpoint := hd tl args;

	if(oldstyx && do9)
		fail("usage", "cannot combine -o and -9 options");

	fd := connect(ctxt, addr);
	ok: int;
	if(do9){
		fd = styxlog(fd);
		factotum := load Factotum Factotum->PATH;
		if(factotum == nil)
			nomod(Factotum->PATH);
		factotum->init();
		ok = factotum->mount(fd, mountpoint, flags, spec, nil).t0;
	}else{
		err: string;
		if(!persist){
			(fd, err) = authcvt(fd);
			if(fd == nil)
				fail("error", err);
		}
		fd = styxlog(fd);
		ok = sys->mount(fd, nil, mountpoint, flags, spec);
	}
	if(ok < 0 && !quiet)
		fail("mount failed", sys->sprint("mount failed: %r"));
}

connect(ctxt: ref Draw->Context, dest: string): ref Sys->FD
{
	if(dest != nil && dest[0] == '{' && dest[len dest - 1] == '}'){
		if(persist)
			fail("usage", "cannot persistently mount a command");
		doauth = 0;
		return popen(ctxt, dest :: nil);
	}
	(n, nil) := sys->tokenize(dest, "!");
	if(n == 1){
		fd := sys->open(dest, Sys->ORDWR);
		if(fd != nil){
			if(persist)
				fail("usage", "cannot persistently mount a file");
			return fd;
		}
		if(dest[0] == '/')
			fail("open failed", sys->sprint("can't open %s: %r", dest));
	}
	svc := "styx";
	if(do9)
		svc = "9fs";
	dest = netmkaddr(dest, "net", svc);
	if(persist){
		styxpersist := load Styxpersist Styxpersist->PATH;
		if(styxpersist == nil)
			fail("load", sys->sprint("cannot load %s: %r", Styxpersist->PATH));
		sys->pipe(p := array[2] of ref Sys->FD);
		(c, err) := styxpersist->init(p[0], do9, nil);
		if(c == nil)
			fail("error", "styxpersist: "+err);
		spawn dialler(c, dest);
		return p[1];
	}
	(ok, c) := sys->dial(dest, nil);
	if(ok < 0)
			fail("dial failed",  sys->sprint("can't dial %s: %r", dest));
	return c.dfd;
}

dialler(dialc: chan of chan of ref Sys->FD, dest: string)
{
	while((reply := <-dialc) != nil){
		if(verbose)
			sys->print("dialling %s\n", addr);
		(ok, c) := sys->dial(dest, nil);
		if(ok == -1){
			reply <-= nil;
			continue;
		}
		(fd, err) := authcvt(c.dfd);
		if(fd == nil && verbose)
			sys->print("%s\n", err);
		# XXX could check that user at the other end is still the same.
		reply <-= fd;
	}
}

authcvt(fd: ref Sys->FD): (ref Sys->FD, string)
{
	err: string;
	if(doauth){
		(fd, err) = authenticate(keyfile, alg, fd, addr);
		if(fd == nil)
			return (nil, err);
		if(verbose)
			sys->print("remote username is %s\n", err);
	}
	if(oldstyx)
		return cvstyx(fd);
	return (fd, nil);
}

popen(ctxt: ref Draw->Context, argv: list of string): ref Sys->FD
{
	sh := load Sh Sh->PATH;
	if(sh == nil)
		nomod(Sh->PATH);
	sync := chan of int;
	fds := array[2] of ref Sys->FD;
	sys->pipe(fds);
	spawn runcmd(sh, ctxt, argv, fds[0], sync);
	<-sync;
	return fds[1];
}

runcmd(sh: Sh, ctxt: ref Draw->Context, argv: list of string, stdin: ref Sys->FD, sync: chan of int)
{
	sys->pctl(Sys->FORKFD, nil);
	sys->dup(stdin.fd, 0);
	stdin = nil;
	sync <-= 0;
	sh->run(ctxt, argv);
}

cvstyx(fd: ref Sys->FD): (ref Sys->FD, string)
{
	styxconv := load Styxconv Styxconv->PATH;
	if(styxconv == nil)
		return (nil, sys->sprint("cannot load %s: %r", Styxconv->PATH));
	styxconv->init();
	p := array[2] of ref Sys->FD;
	if(sys->pipe(p) < 0)
		return (nil, sys->sprint("can't create pipe: %r"));
	pidc := chan of int;
	spawn styxconv->styxconv(p[1], fd, pidc);
	p[1] = nil;
	<-pidc;
	return (p[0], nil);
}

authenticate(keyfile, alg: string, dfd: ref Sys->FD, addr: string): (ref Sys->FD, string)
{
	cert : string;

	kr := load Keyring Keyring->PATH;
	if(kr == nil)
		return (nil, sys->sprint("cannot load %s: %r", Keyring->PATH));

	kd := "/usr/" + user() + "/keyring/";
	if(keyfile == nil) {
		cert = kd + netmkaddr(addr, "tcp", "");
		(ok, nil) := sys->stat(cert);
		if (ok < 0)
			cert = kd + "default";
	}
	else if(len keyfile > 0 && keyfile[0] != '/')
		cert = kd + keyfile;
	else
		cert = keyfile;
	ai := kr->readauthinfo(cert);
	if(ai == nil)
		return (nil, sys->sprint("cannot read %s: %r", cert));

	auth := load Auth Auth->PATH;
	if(auth == nil)
		nomod(Auth->PATH);

	err := auth->init();
	if(err != nil)
		return (nil, "cannot init auth: "+err);

	fd: ref Sys->FD;
	(fd, err) = auth->client(alg, ai, dfd);
	if(fd == nil)
		return (nil, "authentication failed: "+err);
	return (fd, err);
}

user(): string
{
	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]; 
}

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);
}

kill(pid: int)
{
	if ((fd := sys->open("#p/" + string pid + "/ctl", Sys->OWRITE)) != nil)
		sys->fprint(fd, "kill");
}

include "styx.m";
	styx: Styx;
	Rmsg, Tmsg: import styx;

styxlog(fd: ref Sys->FD): ref Sys->FD
{
	sys->pipe(p := array[2] of ref Sys->FD);
	styx = load Styx Styx->PATH;
	styx->init();
	spawn tmsgreader(p[0], fd, p1 := chan[1] of int, p2 := chan[1] of int);
	spawn rmsgreader(fd, p[0], p2, p1);
	fd = p[1];
	return fd;
}

tmsgwriter(sfd: ref Sys->FD, tc: chan of array of byte)
{
	while((a := <-tc) != nil)
		sys->write(sfd, a, len a);	
}

tmsgdelay(d: array of byte, l: int, tc: chan of array of byte)
{
	sys->sleep(l);
	tc <-= d;
}

tmsgreader(cfd, sfd: ref Sys->FD, p1, p2: chan of int)
{
	p1 <-= sys->pctl(0, nil);
	m: ref Tmsg;
	tc:= chan of array of byte;
	spawn tmsgwriter(sfd, tc);
	do{
		m = Tmsg.read(cfd, 9000);
		if (showstyx)
			sys->print("%s\n", m.text());
		pick mm := m {
		Create =>
			l := len "!!LAT=";
			if (len mm.name >= l && mm.name[0:l] == "!!LAT="){
				latency = int mm.name[l:];
				sys->fprint(sys->fildes(2), "o/lmount: latency %d ms\n", latency);
			}
		}
		d := m.pack();
		if (latency > 0)
			spawn tmsgdelay(d, latency, tc);
		else
			tc <-= d;
	} while(m != nil && tagof(m) != tagof(Tmsg.Readerror));
	tc <-= nil;
	kill(<-p2);
}

rmsgreader(sfd, cfd: ref Sys->FD, p1, p2: chan of int)
{
	p1 <-= sys->pctl(0, nil);
	m: ref Rmsg;
	do{
		m = Rmsg.read(sfd, 9000);
		if (showstyx)
			sys->print("%s\n", m.text());
		d := m.pack();
		if(sys->write(cfd, d, len d) != len d)
			sys->print("rmsg write error: %r\n");
	} while(m != nil && tagof(m) != tagof(Tmsg.Readerror));
	kill(<-p2);
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.