/* * TO DO: * - gc of file systems (not going to do just yet?) * - statistics file * - configure on amsterdam */ #include #include #include #include #include #include #include #include #include #include #include "nfs3srv.h" #define trace if(!tracecalls){}else print typedef struct Ipokay Ipokay; typedef struct Config Config; typedef struct Ctree Ctree; typedef struct Cnode Cnode; struct Ipokay { int okay; uchar ip[IPaddrlen]; uchar mask[IPaddrlen]; }; struct Config { Ipokay *ok; uint nok; ulong mtime; Ctree *ctree; }; char *addr; int blocksize; int cachesize; Config config; char *configfile; int encryptedhandles = 1; Channel *nfschan; Channel *mountchan; Channel *timerchan; Nfs3Handle root; SunSrv *srv; int tracecalls; VtCache *vcache; VtConn *z; void cryptinit(void); void timerthread(void*); void timerproc(void*); extern void handleunparse(Fsys*, Nfs3Handle*, Nfs3Handle*, int); extern Nfs3Status handleparse(Nfs3Handle*, Fsys**, Nfs3Handle*, int); Nfs3Status logread(Cnode*, u32int, u64int, uchar**, u32int*, u1int*); Nfs3Status refreshdiskread(Cnode*, u32int, u64int, uchar**, u32int*, u1int*); Nfs3Status refreshconfigread(Cnode*, u32int, u64int, uchar**, u32int*, u1int*); int readconfigfile(Config *cp); void setrootfid(void); int ipokay(uchar *ip, ushort port); u64int unittoull(char*); void usage(void) { fprint(2, "usage: vnfs [-LLRVr] [-a addr] [-b blocksize] [-c cachesize] configfile\n"); threadexitsall("usage"); } void threadmain(int argc, char **argv) { fmtinstall('B', sunrpcfmt); fmtinstall('C', suncallfmt); fmtinstall('F', vtfcallfmt); fmtinstall('H', encodefmt); fmtinstall('I', eipfmt); fmtinstall('V', vtscorefmt); sunfmtinstall(&nfs3prog); sunfmtinstall(&nfsmount3prog); addr = "udp!*!2049"; blocksize = 8192; cachesize = 400; srv = sunsrv(); srv->ipokay = ipokay; cryptinit(); ARGBEGIN{ default: usage(); case 'E': encryptedhandles = 0; break; case 'L': if(srv->localonly == 0) srv->localonly = 1; else srv->localparanoia = 1; break; case 'R': srv->chatty++; break; case 'T': tracecalls = 1; break; case 'V': chattyventi = 1; break; case 'a': addr = EARGF(usage()); break; case 'b': blocksize = unittoull(EARGF(usage())); break; case 'c': cachesize = unittoull(EARGF(usage())); break; case 'r': srv->alwaysreject++; break; }ARGEND if(argc != 1) usage(); if((z = vtdial(nil)) == nil) sysfatal("vtdial: %r"); if(vtconnect(z) < 0) sysfatal("vtconnect: %r"); if((vcache = vtcachealloc(z, blocksize, cachesize)) == nil) sysfatal("vtcache: %r"); configfile = argv[0]; if(readconfigfile(&config) < 0) sysfatal("readConfig: %r"); setrootfid(); nfschan = chancreate(sizeof(SunMsg*), 0); mountchan = chancreate(sizeof(SunMsg*), 0); timerchan = chancreate(sizeof(void*), 0); if(sunsrvudp(srv, addr) < 0) sysfatal("starting server: %r"); sunsrvthreadcreate(srv, nfs3proc, nfschan); sunsrvthreadcreate(srv, mount3proc, mountchan); sunsrvthreadcreate(srv, timerthread, nil); proccreate(timerproc, nil, 32768); sunsrvprog(srv, &nfs3prog, nfschan); sunsrvprog(srv, &nfsmount3prog, mountchan); threadexits(nil); } #define TWID64 ((u64int)~(u64int)0) u64int unittoull(char *s) { char *es; u64int n; if(s == nil) return TWID64; n = strtoul(s, &es, 0); if(*es == 'k' || *es == 'K'){ n *= 1024; es++; }else if(*es == 'm' || *es == 'M'){ n *= 1024*1024; es++; }else if(*es == 'g' || *es == 'G'){ n *= 1024*1024*1024; es++; }else if(*es == 't' || *es == 'T'){ n *= 1024*1024; n *= 1024*1024; } if(*es != '\0') return TWID64; return n; } /* * Handles. * * We store all the state about which file a client is accessing in * the handle, so that we don't have to maintain any per-client state * ourselves. In order to avoid leaking handles or letting clients * create arbitrary handles, we sign and encrypt each handle with * AES using a key selected randomly when the server starts. * Thus, handles cannot be used across sessions. * * The decrypted handles begin with the following header: * * rand[12] random bytes used to make encryption non-deterministic * len[4] length of handle that follows * sessid[8] random session id chosen at start time * * If we're pressed for space in the rest of the handle, we could * probably reduce the amount of randomness. * * Security woes aside, the fact that we have to shove everything * into the handles is quite annoying. We have to encode, in 40 bytes: * * - position in the synthesized config tree * - enough of the path to do glob matching * - position in an archived file system image * * and the handles need to be stable across changes in the config file * (though not across server restarts since encryption screws * that up nicely). * * We encode each of the first two as a 10-byte hash that is * the first half of a SHA1 hash. */ enum { RandSize = 16, SessidSize = 8, HeaderSize = RandSize+SessidSize, MaxHandleSize = Nfs3MaxHandleSize - HeaderSize, }; AESstate aesstate; uchar sessid[SessidSize]; static void hencrypt(Nfs3Handle *h) { uchar *p; AESstate aes; /* * root handle has special encryption - a single 0 byte - so that it * never goes stale. */ if(h->len == root.len && memcmp(h->h, root.h, root.len) == 0){ h->h[0] = 0; h->len = 1; return; } if(!encryptedhandles) return; if(h->len > MaxHandleSize){ /* oops */ fprint(2, "handle too long: %.*lH\n", h->len, h->h); memset(h->h, 'X', Nfs3MaxHandleSize); h->len = Nfs3MaxHandleSize; return; } p = h->h; memmove(p+HeaderSize, p, h->len); *(u32int*)p = fastrand(); *(u32int*)(p+4) = fastrand(); *(u32int*)(p+8) = fastrand(); *(u32int*)(p+12) = h->len; memmove(p+16, sessid, SessidSize); h->len += HeaderSize; if(encryptedhandles){ while(h->len < MaxHandleSize) h->h[h->len++] = fastrand(); aes = aesstate; aesCBCencrypt(h->h, MaxHandleSize, &aes); } } static Nfs3Status hdecrypt(Nfs3Handle *h) { AESstate aes; if(h->len == 1 && h->h[0] == 0){ /* single 0 byte is root */ *h = root; return Nfs3Ok; } if(!encryptedhandles) return Nfs3Ok; if(h->len <= HeaderSize) return Nfs3ErrBadHandle; if(encryptedhandles){ if(h->len != MaxHandleSize) return Nfs3ErrBadHandle; aes = aesstate; aesCBCdecrypt(h->h, h->len, &aes); } if(memcmp(h->h+RandSize, sessid, sizeof sessid) != 0) return Nfs3ErrStale; /* give benefit of doubt */ h->len = *(u32int*)(h->h+12); /* XXX byte order */ memmove(h->h, h->h+HeaderSize, h->len); return Nfs3Ok; } void cryptinit(void) { uchar key[32], ivec[AESbsize]; int i; *(u32int*)sessid = truerand(); for(i=0; i>6); /* first 10 bits */ } static Cnode* mkcnode(Ctree *t, Cnode *parent, char *elem, uint elen, char *path, uint plen) { uint h; Cnode *n; n = emalloc(sizeof *n + elen+1); n->name = (char*)(n+1); memmove(n->name, elem, elen); n->name[elen] = 0; n->parent = parent; if(parent){ n->nextsib = parent->kidlist; parent->kidlist = n; } n->kidlist = nil; sha1((uchar*)path, plen, n->handle, nil); h = dumbhash(n->handle); n->nexthash = t->hash[h]; t->hash[h] = n; return n; } void markctree(Ctree *t) { int i; Cnode *n; for(i=0; ihash); i++) for(n=t->hash[i]; n; n=n->nexthash) if(n->name[0] != '+') n->mark = 1; } int refreshdisk(void) { int i; Cnode *n; Ctree *t; t = config.ctree; for(i=0; ihash); i++) for(n=t->hash[i]; n; n=n->nexthash){ if(n->mfsys) disksync(n->mfsys->disk); if(n->fsys) disksync(n->fsys->disk); } return 0; } void sweepctree(Ctree *t) { int i; Cnode *n; /* just zero all the garbage and leave it linked into the tree */ for(i=0; ihash); i++){ for(n=t->hash[i]; n; n=n->nexthash){ if(!n->mark) continue; n->fsys = nil; free(n->fsysimage); n->fsysimage = nil; memset(n->fsysscore, 0, sizeof n->fsysscore); n->mfsys = nil; n->ismtpt = 0; memset(&n->fsyshandle, 0, sizeof n->fsyshandle); memset(&n->mfsyshandle, 0, sizeof n->mfsyshandle); } } } static Cnode* cnodewalk(Cnode *n, char *name, uint len, int markokay) { Cnode *nn; for(nn=n->kidlist; nn; nn=nn->nextsib) if(strncmp(nn->name, name, len) == 0 && nn->name[len] == 0) if(!nn->mark || markokay) return nn; return nil; } Cnode* ctreewalkpath(Ctree *t, char *name, ulong createmtime) { Cnode *n, *nn; char *p, *nextp; n = t->root; p = name; for(; *p; p=nextp){ n->mark = 0; assert(*p == '/'); p++; nextp = strchr(p, '/'); if(nextp == nil) nextp = p+strlen(p); if((nn = cnodewalk(n, p, nextp-p, 1)) == nil){ if(createmtime == 0) return nil; nn = mkcnode(t, n, p, nextp-p, name, nextp-name); nn->mtime = createmtime; } if(nn->mark) nn->mark = 0; n = nn; } n->mark = 0; return n; } Ctree* mkctree(void) { Ctree *t; t = emalloc(sizeof *t); t->root = mkcnode(t, nil, "", 0, "", 0); ctreewalkpath(t, "/+log", time(0))->read = logread; ctreewalkpath(t, "/+refreshdisk", time(0))->read = refreshdiskread; ctreewalkpath(t, "/+refreshconfig", time(0))->read = refreshconfigread; return t; } Cnode* ctreemountfsys(Ctree *t, char *path, ulong time, uchar *score, char *file) { Cnode *n; if(time == 0) time = 1; n = ctreewalkpath(t, path, time); if(score){ if(n->ismtpt && (n->fsysimage || memcmp(n->fsysscore, score, VtScoreSize) != 0)){ free(n->fsysimage); n->fsysimage = nil; n->fsys = nil; /* leak (might be other refs) */ } memmove(n->fsysscore, score, VtScoreSize); }else{ if(n->ismtpt && (n->fsysimage==nil || strcmp(n->fsysimage, file) != 0)){ free(n->fsysimage); n->fsysimage = nil; n->fsys = nil; /* leak (might be other refs) */ } n->fsysimage = emalloc(strlen(file)+1); strcpy(n->fsysimage, file); } n->ismtpt = 1; return n; } Cnode* cnodebyhandle(Ctree *t, uchar *p) { int h; Cnode *n; h = dumbhash(p); for(n=t->hash[h]; n; n=n->nexthash) if(memcmp(n->handle, p, CnodeHandleSize) == 0) return n; return nil; } static int parseipandmask(char *s, uchar *ip, uchar *mask) { char *p, *q; p = strchr(s, '/'); if(p) *p++ = 0; if(parseip(ip, s) == ~0UL) return -1; if(p == nil) memset(mask, 0xFF, IPaddrlen); else{ if(isdigit((uchar)*p) && strtol(p, &q, 10)>=0 && *q==0) *--p = '/'; if(parseipmask(mask, p) == ~0UL) return -1; if(*p != '/') *--p = '/'; } //fprint(2, "parseipandmask %s => %I %I\n", s, ip, mask); return 0; } static int parsetime(char *s, ulong *time) { ulong x; char *p; int i; Tm tm; /* decimal integer is seconds since 1970 */ x = strtoul(s, &p, 10); if(x > 0 && *p == 0){ *time = x; return 0; } /* otherwise expect yyyy/mmdd/hhmm */ if(strlen(s) != 14 || s[4] != '/' || s[9] != '/') return -1; for(i=0; i<4; i++) if(!isdigit((uchar)s[i]) || !isdigit((uchar)s[i+5]) || !isdigit((uchar)s[i+10])) return -1; memset(&tm, 0, sizeof tm); tm.year = atoi(s)-1900; if(tm.year < 0 || tm.year > 200) return -1; tm.mon = (s[5]-'0')*10+s[6]-'0' - 1; if(tm.mon < 0 || tm.mon > 11) return -1; tm.mday = (s[7]-'0')*10+s[8]-'0'; if(tm.mday < 0 || tm.mday > 31) return -1; tm.hour = (s[10]-'0')*10+s[11]-'0'; if(tm.hour < 0 || tm.hour > 23) return -1; tm.min = (s[12]-'0')*10+s[13]-'0'; if(tm.min < 0 || tm.min > 59) return -1; strcpy(tm.zone, "XXX"); /* anything but GMT */ if(0){ print("tm2sec %d/%d/%d/%d/%d\n", tm.year, tm.mon, tm.mday, tm.hour, tm.min); } *time = tm2sec(&tm); if(0) print("time %lud\n", *time); return 0; } int readconfigfile(Config *cp) { char *f[10], *image, *p, *pref, *q, *name; int nf, line; uchar scorebuf[VtScoreSize], *score; ulong time; Biobuf *b; Config c; Dir *dir; name = configfile; c = *cp; if((dir = dirstat(name)) == nil) return -1; if(c.mtime == dir->mtime){ free(dir); return 0; } c.mtime = dir->mtime; free(dir); if((b = Bopen(name, OREAD)) == nil){ free(dir); return -1; } /* * Reuse old tree, garbage collecting entries that * are not mentioned in the new config file. */ if(c.ctree == nil) c.ctree = mkctree(); markctree(c.ctree); c.ok = nil; c.nok = 0; line = 0; for(; (p=Brdstr(b, '\n', 1)) != nil; free(p)){ line++; if((q = strchr(p, '#')) != nil) *q = 0; nf = tokenize(p, f, nelem(f)); if(nf == 0) continue; if(strcmp(f[0], "mount") == 0){ if(nf != 4){ werrstr("syntax error: mount /path /dev|score mtime"); goto badline; } if(f[1][0] != '/'){ werrstr("unrooted path %s", f[1]); goto badline; } score = nil; image = nil; if(f[2][0] == '/'){ if(access(f[2], AEXIST) < 0){ werrstr("image %s does not exist", f[2]); goto badline; } image = f[2]; }else{ if(vtparsescore(f[2], &pref, scorebuf) < 0){ werrstr("bad score %s", f[2]); goto badline; } score = scorebuf; } if(parsetime(f[3], &time) < 0){ fprint(2, "%s:%d: bad time %s\n", name, line, f[3]); time = 1; } ctreemountfsys(c.ctree, f[1], time, score, image); continue; } if(strcmp(f[0], "allow") == 0 || strcmp(f[0], "deny") == 0){ if(nf != 2){ werrstr("syntax error: allow|deny ip[/mask]"); goto badline; } c.ok = erealloc(c.ok, (c.nok+1)*sizeof(c.ok[0])); if(parseipandmask(f[1], c.ok[c.nok].ip, c.ok[c.nok].mask) < 0){ werrstr("bad ip[/mask]: %s", f[1]); goto badline; } c.ok[c.nok].okay = (strcmp(f[0], "allow") == 0); c.nok++; continue; } werrstr("unknown verb '%s'", f[0]); badline: fprint(2, "%s:%d: %r\n", name, line); } Bterm(b); sweepctree(c.ctree); free(cp->ok); *cp = c; return 0; } int ipokay(uchar *ip, ushort port) { int i; uchar ipx[IPaddrlen]; Ipokay *ok; for(i=0; imask, ipx); if(0) fprint(2, "%I & %I = %I (== %I?)\n", ip, ok->mask, ipx, ok->ip); if(memcmp(ipx, ok->ip, IPaddrlen) == 0) return ok->okay; } if(config.nok == 0) /* all is permitted */ return 1; /* otherwise default is none allowed */ return 0; } Nfs3Status cnodelookup(Ctree *t, Cnode **np, char *name) { Cnode *n, *nn; n = *np; if(n->isblackhole) return Nfs3Ok; if((nn = cnodewalk(n, name, strlen(name), 0)) == nil){ if(n->ismtpt || n->fsys){ if((nn = cnodewalk(n, "", 0, 1)) == nil){ nn = mkcnode(t, n, "", 0, (char*)n->handle, SHA1dlen); nn->isblackhole = 1; } nn->mark = 0; } } if(nn == nil) return Nfs3ErrNoEnt; *np = nn; return Nfs3Ok; } Nfs3Status cnodegetattr(Cnode *n, Nfs3Attr *attr) { memset(attr, 0, sizeof *attr); if(n->read){ attr->type = Nfs3FileReg; attr->mode = 0444; attr->size = 512; attr->nlink = 1; }else{ attr->type = Nfs3FileDir; attr->mode = 0555; attr->size = 1024; attr->nlink = 10; } attr->fileid = *(u64int*)n->handle; attr->atime.sec = n->mtime; attr->mtime.sec = n->mtime; attr->ctime.sec = n->mtime; return Nfs3Ok; } Nfs3Status cnodereaddir(Cnode *n, u32int count, u64int cookie, uchar **pdata, u32int *pcount, u1int *peof) { uchar *data, *p, *ep, *np; u64int c; Nfs3Entry ne; n = n->kidlist; c = cookie; for(; c && n; c--) n = n->nextsib; if(n == nil){ *pdata = 0; *pcount = 0; *peof = 1; return Nfs3Ok; } data = emalloc(count); p = data; ep = data+count; while(n && p < ep){ if(n->mark || n->name[0] == '+'){ n = n->nextsib; ++cookie; continue; } ne.name = n->name; ne.cookie = ++cookie; ne.fileid = *(u64int*)n->handle; if(nfs3entrypack(p, ep, &np, &ne) < 0) break; p = np; n = n->nextsib; } *pdata = data; *pcount = p - data; *peof = n==nil; return Nfs3Ok; } void timerproc(void *v) { for(;;){ sleep(60*1000); sendp(timerchan, 0); } } void timerthread(void *v) { for(;;){ recvp(timerchan); // refreshconfig(); } } /* * Actually serve the NFS requests. Called from nfs3srv.c. * Each request runs in its own thread (coroutine). * * Decrypted handles have the form: * * config[20] - SHA1 hash identifying a config tree node * glob[10] - SHA1 hash prefix identifying a glob state * fsyshandle[<=10] - disk file system handle (usually 4 bytes) */ /* * A fid represents a point in the file tree. * There are three components, all derived from the handle: * * - config tree position (also used to find fsys) * - glob state for exclusions * - file system position */ enum { HAccess, HAttr, HWalk, HDotdot, HRead }; typedef struct Fid Fid; struct Fid { Cnode *cnode; Fsys *fsys; Nfs3Handle fsyshandle; }; int handlecmp(Nfs3Handle *h, Nfs3Handle *h1) { if(h->len != h1->len) return h->len - h1->len; return memcmp(h->h, h1->h, h->len); } Nfs3Status handletofid(Nfs3Handle *eh, Fid *fid, int mode) { int domount; Cnode *n; Disk *disk, *cdisk; Fsys *fsys; Nfs3Status ok; Nfs3Handle h2, *h, *fh; memset(fid, 0, sizeof *fid); domount = 1; if(mode == HDotdot) domount = 0; /* * Not necessary, but speeds up ls -l /dump/2005 * HAttr and HAccess must be handled the same way * because both can be used to fetch attributes. * Acting differently yields inconsistencies at mount points, * and causes FreeBSD ls -l to fail. */ if(mode == HAttr || mode == HAccess) domount = 0; /* * Decrypt handle. */ h2 = *eh; h = &h2; if((ok = hdecrypt(h)) != Nfs3Ok) return ok; trace("handletofid: decrypted %.*lH\n", h->len, h->h); if(h->len < FsysHandleOffset) return Nfs3ErrBadHandle; /* * Find place in config tree. */ if((n = cnodebyhandle(config.ctree, h->h)) == nil) return Nfs3ErrStale; fid->cnode = n; if(n->ismtpt && domount){ /* * Open fsys for mount point if needed. */ if(n->mfsys == nil){ trace("handletofid: mounting %V/%s\n", n->fsysscore, n->fsysimage); if(n->fsysimage){ if(strcmp(n->fsysimage, "/dev/null") == 0) return Nfs3ErrAcces; if((disk = diskopenfile(n->fsysimage)) == nil){ fprint(2, "cannot open disk %s: %r\n", n->fsysimage); return Nfs3ErrIo; } if((cdisk = diskcache(disk, blocksize, 64)) == nil){ fprint(2, "cannot cache disk %s: %r\n", n->fsysimage); diskclose(disk); } disk = cdisk; }else{ if((disk = diskopenventi(vcache, n->fsysscore)) == nil){ fprint(2, "cannot open venti disk %V: %r\n", n->fsysscore); return Nfs3ErrIo; } } if((fsys = fsysopen(disk)) == nil){ fprint(2, "cannot open fsys on %V: %r\n", n->fsysscore); diskclose(disk); return Nfs3ErrIo; } n->mfsys = fsys; fsysroot(fsys, &n->mfsyshandle); } /* * Use inner handle. */ fid->fsys = n->mfsys; fid->fsyshandle = n->mfsyshandle; }else{ /* * Use fsys handle from tree or from handle. * This assumes that fsyshandle was set by fidtohandle * earlier, so it's not okay to reuse handles (except the root) * across sessions. The encryption above makes and * enforces the same restriction, so this is okay. */ fid->fsys = n->fsys; fh = &fid->fsyshandle; if(n->isblackhole){ fh->len = h->len-FsysHandleOffset; memmove(fh->h, h->h+FsysHandleOffset, fh->len); }else *fh = n->fsyshandle; trace("handletofid: fsyshandle %.*lH\n", fh->len, fh->h); } /* * TO DO (maybe): some sort of path restriction here. */ trace("handletofid: cnode %s fsys %p fsyshandle %.*lH\n", n->name, fid->fsys, fid->fsyshandle.len, fid->fsyshandle.h); return Nfs3Ok; } void _fidtohandle(Fid *fid, Nfs3Handle *h) { Cnode *n; n = fid->cnode; /* * Record fsys handle in n, don't bother sending it to client * for black holes. */ n->fsys = fid->fsys; if(!n->isblackhole){ n->fsyshandle = fid->fsyshandle; fid->fsyshandle.len = 0; } memmove(h->h, n->handle, CnodeHandleSize); memmove(h->h+FsysHandleOffset, fid->fsyshandle.h, fid->fsyshandle.len); h->len = FsysHandleOffset+fid->fsyshandle.len; } void fidtohandle(Fid *fid, Nfs3Handle *h) { _fidtohandle(fid, h); hencrypt(h); } void setrootfid(void) { Fid fid; memset(&fid, 0, sizeof fid); fid.cnode = config.ctree->root; _fidtohandle(&fid, &root); fprint(2, "handle %.*lH\n", root.len, root.h); } void fsgetroot(Nfs3Handle *h) { *h = root; hencrypt(h); } Nfs3Status fsgetattr(SunAuthUnix *au, Nfs3Handle *h, Nfs3Attr *attr) { Fid fid; Nfs3Status ok; trace("getattr %.*lH\n", h->len, h->h); if((ok = handletofid(h, &fid, HAttr)) != Nfs3Ok) return ok; if(fid.fsys) return fsysgetattr(fid.fsys, au, &fid.fsyshandle, attr); else return cnodegetattr(fid.cnode, attr); } /* * Lookup is always the hard part. */ Nfs3Status fslookup(SunAuthUnix *au, Nfs3Handle *h, char *name, Nfs3Handle *nh) { Fid fid; Cnode *n; Nfs3Status ok; Nfs3Handle xh; int mode; trace("lookup %.*lH %s\n", h->len, h->h, name); mode = HWalk; if(strcmp(name, "..") == 0 || strcmp(name, ".") == 0) mode = HDotdot; if((ok = handletofid(h, &fid, mode)) != Nfs3Ok){ nfs3errstr(ok); trace("lookup: handletofid %r\n"); return ok; } if(strcmp(name, ".") == 0){ fidtohandle(&fid, nh); return Nfs3Ok; } /* * Walk down file system and cnode simultaneously. * If dotdot and file system doesn't move, need to walk * up cnode. Save the corresponding fsys handles in * the cnode as we walk down so that we'll have them * for dotdotting back up. */ n = fid.cnode; if(mode == HWalk){ /* * Walk down config tree and file system simultaneously. */ if((ok = cnodelookup(config.ctree, &n, name)) != Nfs3Ok){ nfs3errstr(ok); trace("lookup: cnodelookup: %r\n"); return ok; } fid.cnode = n; if(fid.fsys){ if((ok = fsyslookup(fid.fsys, au, &fid.fsyshandle, name, &xh)) != Nfs3Ok){ nfs3errstr(ok); trace("lookup: fsyslookup: %r\n"); return ok; } fid.fsyshandle = xh; } }else{ /* * Walking dotdot. Ick. */ trace("lookup dotdot fsys=%p\n", fid.fsys); if(fid.fsys){ /* * Walk up file system, then try up config tree. */ if((ok = fsyslookup(fid.fsys, au, &fid.fsyshandle, "..", &xh)) != Nfs3Ok){ nfs3errstr(ok); trace("lookup fsyslookup: %r\n"); return ok; } fid.fsyshandle = xh; /* * Usually just go to n->parent. * * If we're in a subtree of the mounted file system that * isn't represented explicitly by the config tree (instead * the black hole node represents the entire file tree), * then we only go to n->parent when we've dotdotted back * to the right handle. */ if(n->parent == nil) trace("lookup dotdot: no parent\n"); else{ trace("lookup dotdot: parent %.*lH, have %.*lH\n", n->parent->fsyshandle.len, n->parent->fsyshandle.h, xh.len, xh.h); } if(n->isblackhole){ if(handlecmp(&n->parent->mfsyshandle, &xh) == 0) n = n->parent; }else{ if(n->parent) n = n->parent; } }else{ /* * No file system, just walk up. */ if(n->parent) n = n->parent; } fid.fsys = n->fsys; fid.fsyshandle = n->fsyshandle; fid.cnode = n; } fidtohandle(&fid, nh); return Nfs3Ok; } Nfs3Status fsaccess(SunAuthUnix *au, Nfs3Handle *h, u32int want, u32int *got, Nfs3Attr *attr) { Fid fid; Nfs3Status ok; trace("access %.*lH 0x%ux\n", h->len, h->h, want); if((ok = handletofid(h, &fid, HAccess)) != Nfs3Ok) return ok; if(fid.fsys) return fsysaccess(fid.fsys, au, &fid.fsyshandle, want, got, attr); *got = want & (Nfs3AccessRead|Nfs3AccessLookup|Nfs3AccessExecute); return cnodegetattr(fid.cnode, attr); } Nfs3Status fsreadlink(SunAuthUnix *au, Nfs3Handle *h, char **link) { Fid fid; Nfs3Status ok; trace("readlink %.*lH\n", h->len, h->h); if((ok = handletofid(h, &fid, HRead)) != Nfs3Ok) return ok; if(fid.fsys) return fsysreadlink(fid.fsys, au, &fid.fsyshandle, link); *link = 0; return Nfs3ErrNotSupp; } Nfs3Status fsreadfile(SunAuthUnix *au, Nfs3Handle *h, u32int count, u64int offset, uchar **data, u32int *pcount, u1int *peof) { Fid fid; Nfs3Status ok; trace("readfile %.*lH\n", h->len, h->h); if((ok = handletofid(h, &fid, HRead)) != Nfs3Ok) return ok; if(fid.cnode->read) return fid.cnode->read(fid.cnode, count, offset, data, pcount, peof); if(fid.fsys) return fsysreadfile(fid.fsys, au, &fid.fsyshandle, count, offset, data, pcount, peof); return Nfs3ErrNotSupp; } Nfs3Status fsreaddir(SunAuthUnix *au, Nfs3Handle *h, u32int len, u64int cookie, uchar **pdata, u32int *pcount, u1int *peof) { Fid fid; Nfs3Status ok; trace("readdir %.*lH\n", h->len, h->h); if((ok = handletofid(h, &fid, HRead)) != Nfs3Ok) return ok; if(fid.fsys) return fsysreaddir(fid.fsys, au, &fid.fsyshandle, len, cookie, pdata, pcount, peof); return cnodereaddir(fid.cnode, len, cookie, pdata, pcount, peof); } Nfs3Status logread(Cnode *n, u32int count, u64int offset, uchar **data, u32int *pcount, u1int *peof) { *pcount = 0; *peof = 1; return Nfs3Ok; } Nfs3Status refreshdiskread(Cnode *n, u32int count, u64int offset, uchar **data, u32int *pcount, u1int *peof) { char buf[128]; if(offset != 0){ *pcount = 0; *peof = 1; return Nfs3Ok; } if(refreshdisk() < 0) snprint(buf, sizeof buf, "refreshdisk: %r\n"); else strcpy(buf, "ok\n"); *data = emalloc(strlen(buf)); strcpy((char*)*data, buf); *pcount = strlen(buf); *peof = 1; return Nfs3Ok; } Nfs3Status refreshconfigread(Cnode *n, u32int count, u64int offset, uchar **data, u32int *pcount, u1int *peof) { char buf[128]; if(offset != 0){ *pcount = 0; *peof = 1; return Nfs3Ok; } if(readconfigfile(&config) < 0) snprint(buf, sizeof buf, "readconfig: %r\n"); else strcpy(buf, "ok\n"); *data = emalloc(strlen(buf)); strcpy((char*)*data, buf); *pcount = strlen(buf); *peof = 1; return Nfs3Ok; }