blob: 1b5b404a55f7455e763afae78b872f6ae5938faa [file] [log] [blame]
#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;
}