#include #include #include #include #include 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; }