Plan 9 from Bell Labs’s /usr/web/sources/contrib/arisawa/su/newns.c

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


#include <u.h>
#include <libc.h>
#include <bio.h>
#include <auth.h>
#include <authsrv.h>

extern getkey(char *params);

enum
{
	NARG	= 15,		/* max number of arguments */
	MAXARG	= 10*ANAMELEN,	/* max length of an argument */
};

static int	setenv(char*, char*);
static char	*expandarg(char*, char*);
static int	splitargs(char*, char*[], char*, int);
static int	nsfile(Biobuf *, AuthRpc *, char *user);
static int	nsop(int, char*[], AuthRpc*, char *user);
static int	callexport(char*, char*);
static int	catch(void*, char*);

extern int debug;

static int
buildns(int newns, char *user, char *file)
{
	Biobuf *b;
	char home[4*ANAMELEN];
	AuthRpc *rpc;
	int cdroot;
	char *path;
	int afd;

	rpc = nil;
	/* try for factotum now because later is impossible */
	afd = open("/mnt/factotum/rpc", ORDWR);
	if(afd >= 0){
		if(debug) fprint(2,"auth_allocrpc ...\n");
		rpc = auth_allocrpc(afd);
		if(rpc == nil){
			if(debug) fprint(2,"auth_allocrpc failed: %r\n");
			close(afd);
			afd = -1;
		}
	}
	else if(debug)
		fprint(2,"open: %r\n");
	if(file == nil){
		if(!newns){
			werrstr("no namespace file specified");
			return -1;
		}
		file = "/lib/namespace";
	}
	b = Bopen(file, OREAD);
	if(b == 0){
		werrstr("can't open %s: %r", file);
		close(afd);
		auth_freerpc(rpc);
		return -1;
	}
	if(newns){
		rfork(RFENVG|RFCNAMEG);
		setenv("user", user);
		snprint(home, 2*ANAMELEN, "/usr/%s", user);
		setenv("home", home);
	}
	cdroot = nsfile(b, rpc, user);
	Bterm(b);
	if(rpc){
		close(rpc->afd);
		auth_freerpc(rpc);
	}

	/* make sure we managed to cd into the new name space */
	if(newns && !cdroot){
		path = malloc(1024);
		if(path == nil || getwd(path, 1024) == 0 || chdir(path) < 0)
			chdir("/");
		if(path != nil)
			free(path);
	}

	return 0;
}

static int
nsfile(Biobuf *b, AuthRpc *rpc, char *user)
{
	int argc;
	char *cmd, *argv[NARG+1], argbuf[MAXARG*NARG];
	int cdroot = 0;

	atnotify(catch, 1);
	while(cmd = Brdline(b, '\n')){
		cmd[Blinelen(b)-1] = '\0';
		while(*cmd==' ' || *cmd=='\t')
			cmd++;
		if(*cmd == '#')
			continue;
		argc = splitargs(cmd, argv, argbuf, NARG);
		if(argc)
			cdroot |= nsop(argc, argv, rpc, user);
	}
	atnotify(catch, 0);
	return cdroot;
}

int
anewns(char *user, char *file)
{
	return buildns(1, user, file);
}

static int
famount(int fd, AuthRpc *rpc, char *mntpt, int flags, char *aname, char *user)
{
	int afd;
	AuthInfo *ai;
	char buf[256];

	/*	fauth() is a system call. 
	*	fauth(2):
         *	If fauth returns -1, the error case, that means the file
         *	server does not require authentication for the connection,
         *	and afd should be set to -1 in the call to mount.
	*	It is rare to use fauth directly; more commonly amount is used.	*/

	afd = fauth(fd, aname);
	if(afd >= 0){
		if(debug)
			fprint(2,"fauth_proxy ...\n");
		snprint(buf, sizeof buf, "proto=p9any role=client user=%s", user);
		ai = fauth_proxy(afd, rpc, getkey, buf);
		if(ai != nil)
			auth_freeAI(ai);
		else	fprint(2,"# fauth_proxy: %r\n");
	}
	else if(debug)
		fprint(2,"fauth: %r\n");
		/*	typical messages are:
		*		authentication not required
		*		authentication disabled
		*		authentication failed
		* 	these messages come from a server.
		*	see /sys/src/cmd/fossil/9p.c for example. */
	return mount(fd, afd, mntpt, flags, aname);
}

static int
nsop(int argc, char *argv[], AuthRpc *rpc, char *user)
{
	char *argv0;
	ulong flags;
	int fd;
	Biobuf *b;
	int cdroot = 0;

	flags = 0;
	argv0 = 0;
	ARGBEGIN{
	case 'a':
		flags |= MAFTER;
		break;
	case 'b':
		flags |= MBEFORE;
		break;
	case 'c':
		flags |= MCREATE;
		break;
	case 'C':
		flags |= MCACHE;
		break;
	}ARGEND

	if(!(flags & (MAFTER|MBEFORE)))
		flags |= MREPL;

	if(strcmp(argv0, ".") == 0 && argc == 1){
		b = Bopen(argv[0], OREAD);
		if(b == nil)
			return 0;
		cdroot |= nsfile(b, rpc, user);
		Bterm(b);
	} else if(strcmp(argv0, "clear") == 0 && argc == 0)
		rfork(RFCNAMEG);
	else if(strcmp(argv0, "bind") == 0 && argc == 2)
		bind(argv[0], argv[1], flags);
	else if(strcmp(argv0, "unmount") == 0){
		if(argc == 1)
			unmount(nil, argv[0]);
		else if(argc == 2)
			unmount(argv[0], argv[1]);
	} else if(strcmp(argv0, "mount") == 0){
		int status = -1;
		fd = open(argv[0], ORDWR);
		if(fd >= 0){
			if(argc == 2)
				status = famount(fd, rpc, argv[1], flags, "", user);
			else if(argc == 3)
				status = famount(fd, rpc, argv[1], flags, argv[2], user);
			close(fd);
			if(debug && status < 0)
				fprint(2,"mount %s %s: %r\n", argv[1], argv[2]);
		} else if(debug)
			fprint(2,"open: %r\n");
	} else if(strcmp(argv0, "import") == 0){
		int status = -1;
		fd = callexport(argv[0], argv[1]);
		if(fd >= 0){
			if(argc == 2)
				status = famount(fd, rpc, argv[1], flags, "", user);
			else if(argc == 3)
				status = famount(fd, rpc, argv[2], flags, "", user);
			close(fd);
			if(debug && status < 0)
				fprint(2,"famount %s: %r\n", argc==2?argv[1]:argv[2]);
		} else if(debug)
			fprint(2,"open: %r\n");
	} else if(strcmp(argv0, "cd") == 0 && argc == 1)
		if(chdir(argv[0]) == 0 && *argv[0] == '/')
			cdroot = 1;
	return cdroot;
}

static char *wocp = "sys: write on closed pipe";

static int
catch(void *x, char *m)
{
	USED(x);
	return strncmp(m, wocp, strlen(wocp)) == 0;
}

static int
callexport(char *sys, char *tree)
{
	char *na, buf[3];
	int fd;
	AuthInfo *ai;

	na = netmkaddr(sys, 0, "exportfs");
	if((fd = dial(na, 0, 0, 0)) < 0)
		return -1;
	if((ai = auth_proxy(fd, getkey, "proto=p9any role=client")) == nil
	|| write(fd, tree, strlen(tree)) < 0
	|| read(fd, buf, 3) != 2 || buf[0]!='O' || buf[1]!= 'K'){
		close(fd);
		auth_freeAI(ai);
		return -1;
	}
	auth_freeAI(ai);
	return fd;
}

static int
splitargs(char *p, char *argv[], char *argbuf, int nargv)
{
	char *q;
	int i, n;

	n = gettokens(p, argv, nargv, " \t'\r");
	if(n == nargv)
		return 0;
	for(i = 0; i < n; i++){
		q = argv[i];
		argv[i] = argbuf;
		argbuf = expandarg(q, argbuf);
		if(!argbuf)
			return 0;
	}
	return n;
}

/*
 * copy the arg into the buffer,
 * expanding any environment variables.
 * environment variables are assumed to be
 * names (ie. < ANAMELEN long)
 * the entire argument is expanded to be at
 * most MAXARG long and null terminated
 * the address of the byte after the terminating null is returned
 * any problems cause a 0 return;
 */
static char *
expandarg(char *arg, char *buf)
{
	char env[3+ANAMELEN], *p, *q, *x;
	int fd, n, len;

	n = 0;
	while(p = utfrune(arg, L'$')){
		len = p - arg;
		if(n + len + ANAMELEN >= MAXARG-1)
			return 0;
		memmove(&buf[n], arg, len);
		n += len;
		p++;
		arg = utfrune(p, L'\0');
		q = utfrune(p, L'/');
		if(q && q < arg)
			arg = q;
		q = utfrune(p, L'.');
		if(q && q < arg)
			arg = q;
		q = utfrune(p, L'$');
		if(q && q < arg)
			arg = q;
		len = arg - p;
		if(len >= ANAMELEN)
			continue;
		strcpy(env, "#e/");
		strncpy(env+3, p, len);
		env[3+len] = '\0';
		fd = open(env, OREAD);
		if(fd >= 0){
			len = read(fd, &buf[n], ANAMELEN - 1);
			/* some singleton environment variables have trailing NULs */
			/* lists separate entries with NULs; we arbitrarily take the first element */
			if(len > 0){
				x = memchr(&buf[n], 0, len);
				if(x != nil)
					len = x - &buf[n];
				n += len;
			}
			close(fd);
		}
	}
	len = strlen(arg);
	if(n + len >= MAXARG - 1)
		return 0;
	strcpy(&buf[n], arg);
	return &buf[n+len+1];
}

static int
setenv(char *name, char *val)
{
	int f;
	char ename[ANAMELEN+6];
	long s;

	sprint(ename, "#e/%s", name);
	f = create(ename, OWRITE, 0664);
	if(f < 0)
		return -1;
	s = strlen(val);
	if(write(f, val, s) != s){
		close(f);
		return -1;
	}
	close(f);
	return 0;
}

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.