| #include <u.h> |
| #include <libc.h> |
| #include <bio.h> |
| #include <auth.h> |
| #include <authsrv.h> |
| #include "authlocal.h" |
| |
| 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 *); |
| static int nsop(int, char*[], AuthRpc*); |
| static int callexport(char*, char*); |
| static int catch(void*, char*); |
| |
| static int |
| buildns(int newns, char *user, char *file) |
| { |
| Biobuf *b; |
| char home[4*ANAMELEN]; |
| int afd; |
| AuthRpc *rpc; |
| int cdroot; |
| char *path; |
| |
| rpc = nil; |
| /* try for factotum now because later is impossible */ |
| afd = open("/mnt/factotum/rpc", ORDWR); |
| if(afd >= 0){ |
| rpc = auth_allocrpc(afd); |
| if(rpc == nil){ |
| close(afd); |
| afd = -1; |
| } |
| } |
| 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); |
| 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) |
| { |
| 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); |
| } |
| atnotify(catch, 0); |
| return cdroot; |
| } |
| |
| int |
| newns(char *user, char *file) |
| { |
| return buildns(1, user, file); |
| } |
| |
| int |
| addns(char *user, char *file) |
| { |
| return buildns(0, user, file); |
| } |
| |
| static int |
| famount(int fd, AuthRpc *rpc, char *mntpt, int flags, char *aname) |
| { |
| int afd; |
| AuthInfo *ai; |
| |
| afd = fauth(fd, aname); |
| if(afd >= 0){ |
| ai = fauth_proxy(afd, rpc, amount_getkey, "proto=p9any role=client"); |
| if(ai != nil) |
| auth_freeAI(ai); |
| } |
| return mount(fd, afd, mntpt, flags, aname); |
| } |
| |
| static int |
| nsop(int argc, char *argv[], AuthRpc *rpc) |
| { |
| 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); |
| 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){ |
| fd = open(argv[0], ORDWR); |
| if(argc == 2) |
| famount(fd, rpc, argv[1], flags, ""); |
| else if(argc == 3) |
| famount(fd, rpc, argv[1], flags, argv[2]); |
| close(fd); |
| } else if(strcmp(argv0, "import") == 0){ |
| fd = callexport(argv[0], argv[1]); |
| if(argc == 2) |
| famount(fd, rpc, argv[1], flags, ""); |
| else if(argc == 3) |
| famount(fd, rpc, argv[2], flags, ""); |
| close(fd); |
| } 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, auth_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; |
| } |