|  | #include <u.h> | 
|  | #include <libc.h> | 
|  | #include <bio.h> | 
|  | #include <thread.h> | 
|  | #include <sunrpc.h> | 
|  | #include <nfs3.h> | 
|  | #include <diskfs.h> | 
|  | #include <venti.h> | 
|  | #include <libsec.h> | 
|  |  | 
|  | #undef stime | 
|  | #define stime configstime	/* sometimes in <time.h> */ | 
|  | typedef struct Entry Entry; | 
|  | struct Entry | 
|  | { | 
|  | Entry *parent; | 
|  | Entry *nextdir; | 
|  | Entry *nexthash; | 
|  | Entry *kids; | 
|  | int isfsys; | 
|  | Fsys *fsys; | 
|  | uchar score[VtScoreSize];	/* of fsys */ | 
|  | char *name; | 
|  | uchar sha1[VtScoreSize];	/* of path to this entry */ | 
|  | ulong time; | 
|  | }; | 
|  |  | 
|  | typedef struct Config Config; | 
|  | struct Config | 
|  | { | 
|  | VtCache *vcache; | 
|  | Entry *root; | 
|  | Entry *hash[1024]; | 
|  | Qid qid; | 
|  | }; | 
|  |  | 
|  | Config *config; | 
|  | static	ulong 	mtime;	/* mod time */ | 
|  | static	ulong 	stime;	/* sync time */ | 
|  | static	char*	configfile; | 
|  |  | 
|  | static int addpath(Config*, char*, uchar[VtScoreSize], ulong); | 
|  | Fsys fsysconfig; | 
|  |  | 
|  | static void | 
|  | freeconfig(Config *c) | 
|  | { | 
|  | Entry *next, *e; | 
|  | int i; | 
|  |  | 
|  | for(i=0; i<nelem(c->hash); i++){ | 
|  | for(e=c->hash[i]; e; e=next){ | 
|  | next = e->nexthash; | 
|  | free(e); | 
|  | } | 
|  | } | 
|  | free(c); | 
|  | } | 
|  |  | 
|  | static int | 
|  | namehash(uchar *s) | 
|  | { | 
|  | return (s[0]<<2)|(s[1]>>6); | 
|  | } | 
|  |  | 
|  | static Entry* | 
|  | entrybyhandle(Nfs3Handle *h) | 
|  | { | 
|  | int hh; | 
|  | Entry *e; | 
|  |  | 
|  | hh = namehash(h->h); | 
|  | for(e=config->hash[hh]; e; e=e->nexthash) | 
|  | if(memcmp(e->sha1, h->h, VtScoreSize) == 0) | 
|  | return e; | 
|  | return nil; | 
|  | } | 
|  |  | 
|  | static Config* | 
|  | readconfigfile(char *name, VtCache *vcache) | 
|  | { | 
|  | char *p, *pref, *f[10]; | 
|  | int ok; | 
|  | Config *c; | 
|  | uchar score[VtScoreSize]; | 
|  | int h, nf, line; | 
|  | Biobuf *b; | 
|  | Dir *dir; | 
|  |  | 
|  | configfile = vtstrdup(name); | 
|  |  | 
|  | if((dir = dirstat(name)) == nil) | 
|  | return nil; | 
|  |  | 
|  | if((b = Bopen(name, OREAD)) == nil){ | 
|  | free(dir); | 
|  | return nil; | 
|  | } | 
|  |  | 
|  | line = 0; | 
|  | ok = 1; | 
|  | c = emalloc(sizeof(Config)); | 
|  | c->vcache = vcache; | 
|  | c->qid = dir->qid; | 
|  | free(dir); | 
|  | c->root = emalloc(sizeof(Entry)); | 
|  | c->root->name = "/"; | 
|  | c->root->parent = c->root; | 
|  | sha1((uchar*)"/", 1, c->root->sha1, nil); | 
|  | h = namehash(c->root->sha1); | 
|  | c->hash[h] = c->root; | 
|  |  | 
|  | for(; (p = Brdstr(b, '\n', 1)) != nil; free(p)){ | 
|  | line++; | 
|  | if(p[0] == '#') | 
|  | continue; | 
|  | nf = tokenize(p, f, nelem(f)); | 
|  | if(nf != 3){ | 
|  | fprint(2, "%s:%d: syntax error\n", name, line); | 
|  | /* ok = 0; */ | 
|  | continue; | 
|  | } | 
|  | if(vtparsescore(f[1], &pref, score) < 0){ | 
|  | fprint(2, "%s:%d: bad score '%s'\n", name, line, f[1]); | 
|  | /* ok = 0; */ | 
|  | continue; | 
|  | } | 
|  | if(f[0][0] != '/'){ | 
|  | fprint(2, "%s:%d: unrooted path '%s'\n", name, line, f[0]); | 
|  | /* ok = 0; */ | 
|  | continue; | 
|  | } | 
|  | if(addpath(c, f[0], score, strtoul(f[2], 0, 0)) < 0){ | 
|  | fprint(2, "%s:%d: %s: %r\n", name, line, f[0]); | 
|  | /* ok = 0; */ | 
|  | continue; | 
|  | } | 
|  | } | 
|  | Bterm(b); | 
|  |  | 
|  | if(!ok){ | 
|  | freeconfig(c); | 
|  | return nil; | 
|  | } | 
|  |  | 
|  | return c; | 
|  | } | 
|  |  | 
|  | static void | 
|  | refreshconfig(void) | 
|  | { | 
|  | ulong now; | 
|  | Config *c, *old; | 
|  | Dir *d; | 
|  |  | 
|  | now = time(0); | 
|  | if(now - stime < 60) | 
|  | return; | 
|  | if((d = dirstat(configfile)) == nil) | 
|  | return; | 
|  | if(d->mtime == mtime){ | 
|  | free(d); | 
|  | stime = now; | 
|  | return; | 
|  | } | 
|  |  | 
|  | c = readconfigfile(configfile, config->vcache); | 
|  | if(c == nil){ | 
|  | free(d); | 
|  | return; | 
|  | } | 
|  |  | 
|  | old = config; | 
|  | config = c; | 
|  | stime = now; | 
|  | mtime = d->mtime; | 
|  | free(d); | 
|  | freeconfig(old); | 
|  | } | 
|  |  | 
|  | static Entry* | 
|  | entrylookup(Entry *e, char *p, int np) | 
|  | { | 
|  | for(e=e->kids; e; e=e->nextdir) | 
|  | if(strlen(e->name) == np && memcmp(e->name, p, np) == 0) | 
|  | return e; | 
|  | return nil; | 
|  | } | 
|  |  | 
|  | static Entry* | 
|  | walkpath(Config *c, char *name) | 
|  | { | 
|  | Entry *e, *ee; | 
|  | char *p, *nextp; | 
|  | int h; | 
|  |  | 
|  | e = c->root; | 
|  | p = name; | 
|  | for(; *p; p=nextp){ | 
|  | assert(*p == '/'); | 
|  | p++; | 
|  | nextp = strchr(p, '/'); | 
|  | if(nextp == nil) | 
|  | nextp = p+strlen(p); | 
|  | if(e->fsys){ | 
|  | werrstr("%.*s is already a mount point", utfnlen(name, nextp-name), name); | 
|  | return nil; | 
|  | } | 
|  | if((ee = entrylookup(e, p, nextp-p)) == nil){ | 
|  | ee = emalloc(sizeof(Entry)+(nextp-p)+1); | 
|  | ee->parent = e; | 
|  | ee->nextdir = e->kids; | 
|  | e->kids = ee; | 
|  | ee->name = (char*)&ee[1]; | 
|  | memmove(ee->name, p, nextp-p); | 
|  | ee->name[nextp-p] = 0; | 
|  | sha1((uchar*)name, nextp-name, ee->sha1, nil); | 
|  | h = namehash(ee->sha1); | 
|  | ee->nexthash = c->hash[h]; | 
|  | c->hash[h] = ee; | 
|  | } | 
|  | e = ee; | 
|  | } | 
|  | if(e->kids){ | 
|  | werrstr("%s already has children; cannot be mount point", name); | 
|  | return nil; | 
|  | } | 
|  | return e; | 
|  | } | 
|  |  | 
|  | static int | 
|  | addpath(Config *c, char *name, uchar score[VtScoreSize], ulong time) | 
|  | { | 
|  | Entry *e; | 
|  |  | 
|  | e = walkpath(c, name); | 
|  | if(e == nil) | 
|  | return -1; | 
|  | e->isfsys = 1; | 
|  | e->time = time; | 
|  | memmove(e->score, score, VtScoreSize); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void | 
|  | mkhandle(Nfs3Handle *h, Entry *e) | 
|  | { | 
|  | memmove(h->h, e->sha1, VtScoreSize); | 
|  | h->len = VtScoreSize; | 
|  | } | 
|  |  | 
|  | Nfs3Status | 
|  | handleparse(Nfs3Handle *h, Fsys **pfsys, Nfs3Handle *nh, int isgetattr) | 
|  | { | 
|  | int hh; | 
|  | Entry *e; | 
|  | Disk *disk; | 
|  | Fsys *fsys; | 
|  |  | 
|  | refreshconfig(); | 
|  |  | 
|  | if(h->len < VtScoreSize) | 
|  | return Nfs3ErrBadHandle; | 
|  |  | 
|  | hh = namehash(h->h); | 
|  | for(e=config->hash[hh]; e; e=e->nexthash) | 
|  | if(memcmp(e->sha1, h->h, VtScoreSize) == 0) | 
|  | break; | 
|  | if(e == nil) | 
|  | return Nfs3ErrBadHandle; | 
|  |  | 
|  | if(e->isfsys == 1 && e->fsys == nil && (h->len != VtScoreSize || !isgetattr)){ | 
|  | if((disk = diskopenventi(config->vcache, e->score)) == nil){ | 
|  | fprint(2, "cannot open disk %V: %r\n", e->score); | 
|  | return Nfs3ErrIo; | 
|  | } | 
|  | if((fsys = fsysopen(disk)) == nil){ | 
|  | fprint(2, "cannot open fsys on %V: %r\n", e->score); | 
|  | diskclose(disk); | 
|  | return Nfs3ErrIo; | 
|  | } | 
|  | e->fsys = fsys; | 
|  | } | 
|  |  | 
|  | if(e->fsys == nil || (isgetattr && h->len == VtScoreSize)){ | 
|  | if(h->len != VtScoreSize) | 
|  | return Nfs3ErrBadHandle; | 
|  | *pfsys = &fsysconfig; | 
|  | *nh = *h; | 
|  | return Nfs3Ok; | 
|  | } | 
|  | *pfsys = e->fsys; | 
|  | if(h->len == VtScoreSize) | 
|  | return fsysroot(*pfsys, nh); | 
|  | nh->len = h->len - VtScoreSize; | 
|  | memmove(nh->h, h->h+VtScoreSize, nh->len); | 
|  | return Nfs3Ok; | 
|  | } | 
|  |  | 
|  | void | 
|  | handleunparse(Fsys *fsys, Nfs3Handle *h, Nfs3Handle *nh, int dotdot) | 
|  | { | 
|  | Entry *e; | 
|  | int hh; | 
|  |  | 
|  | refreshconfig(); | 
|  |  | 
|  | if(fsys == &fsysconfig) | 
|  | return; | 
|  |  | 
|  | if(dotdot && nh->len == h->len - VtScoreSize | 
|  | && memcmp(h->h+VtScoreSize, nh->h, nh->len) == 0){ | 
|  | /* walked .. but didn't go anywhere: must be at root */ | 
|  | hh = namehash(h->h); | 
|  | for(e=config->hash[hh]; e; e=e->nexthash) | 
|  | if(memcmp(e->sha1, h->h, VtScoreSize) == 0) | 
|  | break; | 
|  | if(e == nil) | 
|  | return;	/* cannot happen */ | 
|  |  | 
|  | /* walk .. */ | 
|  | e = e->parent; | 
|  | nh->len = VtScoreSize; | 
|  | memmove(nh->h, e->sha1, VtScoreSize); | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* otherwise just insert the same prefix */ | 
|  | memmove(nh->h+VtScoreSize, nh->h, VtScoreSize); | 
|  | nh->len += VtScoreSize; | 
|  | memmove(nh->h, h->h, VtScoreSize); | 
|  | } | 
|  |  | 
|  | Nfs3Status | 
|  | fsysconfigroot(Fsys *fsys, Nfs3Handle *h) | 
|  | { | 
|  | USED(fsys); | 
|  |  | 
|  | mkhandle(h, config->root); | 
|  | return Nfs3Ok; | 
|  | } | 
|  |  | 
|  | Nfs3Status | 
|  | fsysconfiggetattr(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, Nfs3Attr *attr) | 
|  | { | 
|  | Entry *e; | 
|  |  | 
|  | USED(fsys); | 
|  | USED(au); | 
|  |  | 
|  | if(h->len != VtScoreSize) | 
|  | return Nfs3ErrBadHandle; | 
|  |  | 
|  | e = entrybyhandle(h); | 
|  | if(e == nil) | 
|  | return Nfs3ErrNoEnt; | 
|  |  | 
|  | memset(attr, 0, sizeof *attr); | 
|  | attr->type = Nfs3FileDir; | 
|  | attr->mode = 0555; | 
|  | attr->nlink = 2; | 
|  | attr->size = 1024; | 
|  | attr->fileid = *(u64int*)h->h; | 
|  | attr->atime.sec = e->time; | 
|  | attr->mtime.sec = e->time; | 
|  | attr->ctime.sec = e->time; | 
|  | return Nfs3Ok; | 
|  | } | 
|  |  | 
|  | Nfs3Status | 
|  | fsysconfigaccess(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int want, u32int *got, Nfs3Attr *attr) | 
|  | { | 
|  | want &= Nfs3AccessRead|Nfs3AccessLookup|Nfs3AccessExecute; | 
|  | *got = want; | 
|  | return fsysconfiggetattr(fsys, au, h, attr); | 
|  | } | 
|  |  | 
|  | Nfs3Status | 
|  | fsysconfiglookup(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char *name, Nfs3Handle *nh) | 
|  | { | 
|  | Entry *e; | 
|  |  | 
|  | USED(fsys); | 
|  | USED(au); | 
|  |  | 
|  | if(h->len != VtScoreSize) | 
|  | return Nfs3ErrBadHandle; | 
|  |  | 
|  | e = entrybyhandle(h); | 
|  | if(e == nil) | 
|  | return Nfs3ErrNoEnt; | 
|  |  | 
|  | if(strcmp(name, "..") == 0) | 
|  | e = e->parent; | 
|  | else if(strcmp(name, ".") == 0){ | 
|  | /* nothing */ | 
|  | }else{ | 
|  | if((e = entrylookup(e, name, strlen(name))) == nil) | 
|  | return Nfs3ErrNoEnt; | 
|  | } | 
|  |  | 
|  | mkhandle(nh, e); | 
|  | return Nfs3Ok; | 
|  | } | 
|  |  | 
|  | Nfs3Status | 
|  | fsysconfigreadlink(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char **link) | 
|  | { | 
|  | USED(h); | 
|  | USED(fsys); | 
|  | USED(au); | 
|  |  | 
|  | *link = 0; | 
|  | return Nfs3ErrNotSupp; | 
|  | } | 
|  |  | 
|  | Nfs3Status | 
|  | fsysconfigreadfile(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int count, u64int offset, uchar **pdata, u32int *pcount, u1int *peof) | 
|  | { | 
|  | USED(fsys); | 
|  | USED(h); | 
|  | USED(count); | 
|  | USED(offset); | 
|  | USED(pdata); | 
|  | USED(pcount); | 
|  | USED(peof); | 
|  | USED(au); | 
|  |  | 
|  | return Nfs3ErrNotSupp; | 
|  | } | 
|  |  | 
|  | Nfs3Status | 
|  | fsysconfigreaddir(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int count, u64int cookie, uchar **pdata, u32int *pcount, u1int *peof) | 
|  | { | 
|  | uchar *data, *p, *ep, *np; | 
|  | u64int c; | 
|  | Entry *e; | 
|  | Nfs3Entry ne; | 
|  |  | 
|  | USED(fsys); | 
|  | USED(au); | 
|  |  | 
|  | if(h->len != VtScoreSize) | 
|  | return Nfs3ErrBadHandle; | 
|  |  | 
|  | e = entrybyhandle(h); | 
|  | if(e == nil) | 
|  | return Nfs3ErrNoEnt; | 
|  |  | 
|  | e = e->kids; | 
|  | c = cookie; | 
|  | for(; c && e; c--) | 
|  | e = e->nextdir; | 
|  | if(e == nil){ | 
|  | *pdata = 0; | 
|  | *pcount = 0; | 
|  | *peof = 1; | 
|  | return Nfs3Ok; | 
|  | } | 
|  |  | 
|  | data = emalloc(count); | 
|  | p = data; | 
|  | ep = data+count; | 
|  | while(e && p < ep){ | 
|  | ne.name = e->name; | 
|  | ne.namelen = strlen(e->name); | 
|  | ne.cookie = ++cookie; | 
|  | ne.fileid = *(u64int*)e->sha1; | 
|  | if(nfs3entrypack(p, ep, &np, &ne) < 0) | 
|  | break; | 
|  | p = np; | 
|  | e = e->nextdir; | 
|  | } | 
|  | *pdata = data; | 
|  | *pcount = p - data; | 
|  | *peof = 0; | 
|  | return Nfs3Ok; | 
|  | } | 
|  |  | 
|  | void | 
|  | fsysconfigclose(Fsys *fsys) | 
|  | { | 
|  | USED(fsys); | 
|  | } | 
|  |  | 
|  | int | 
|  | readconfig(char *name, VtCache *vcache, Nfs3Handle *h) | 
|  | { | 
|  | Config *c; | 
|  | Dir *d; | 
|  |  | 
|  | if((d = dirstat(name)) == nil) | 
|  | return -1; | 
|  |  | 
|  | c = readconfigfile(name, vcache); | 
|  | if(c == nil){ | 
|  | free(d); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | config = c; | 
|  | mtime = d->mtime; | 
|  | stime = time(0); | 
|  | free(d); | 
|  |  | 
|  | mkhandle(h, c->root); | 
|  | fsysconfig._lookup = fsysconfiglookup; | 
|  | fsysconfig._access = fsysconfigaccess; | 
|  | fsysconfig._getattr = fsysconfiggetattr; | 
|  | fsysconfig._readdir = fsysconfigreaddir; | 
|  | fsysconfig._readfile = fsysconfigreadfile; | 
|  | fsysconfig._readlink = fsysconfigreadlink; | 
|  | fsysconfig._root = fsysconfigroot; | 
|  | fsysconfig._close = fsysconfigclose; | 
|  | return 0; | 
|  | } |