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