plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

vnfs.c (27973B)


      1 /*
      2  * TO DO:
      3  *	- gc of file systems (not going to do just yet?)
      4  *	- statistics file
      5  *	- configure on amsterdam
      6  */
      7 
      8 #include <u.h>
      9 #include <libc.h>
     10 #include <bio.h>
     11 #include <ip.h>
     12 #include <thread.h>
     13 #include <libsec.h>
     14 #include <sunrpc.h>
     15 #include <nfs3.h>
     16 #include <diskfs.h>
     17 #include <venti.h>
     18 #include "nfs3srv.h"
     19 
     20 #define trace if(!tracecalls){}else print
     21 
     22 typedef struct Ipokay Ipokay;
     23 typedef struct Config Config;
     24 typedef struct Ctree Ctree;
     25 typedef struct Cnode Cnode;
     26 
     27 struct Ipokay
     28 {
     29 	int okay;
     30 	uchar ip[IPaddrlen];
     31 	uchar mask[IPaddrlen];
     32 };
     33 
     34 struct Config
     35 {
     36 	Ipokay *ok;
     37 	uint nok;
     38 	ulong mtime;
     39 	Ctree *ctree;
     40 };
     41 
     42 char 		*addr;
     43 int			blocksize;
     44 int			cachesize;
     45 Config		config;
     46 char			*configfile;
     47 int			encryptedhandles = 1;
     48 Channel		*nfschan;
     49 Channel		*mountchan;
     50 Channel		*timerchan;
     51 Nfs3Handle	root;
     52 SunSrv		*srv;
     53 int			tracecalls;
     54 VtCache		*vcache;
     55 VtConn		*z;
     56 
     57 void			cryptinit(void);
     58 void			timerthread(void*);
     59 void			timerproc(void*);
     60 
     61 extern	void			handleunparse(Fsys*, Nfs3Handle*, Nfs3Handle*, int);
     62 extern	Nfs3Status	handleparse(Nfs3Handle*, Fsys**, Nfs3Handle*, int);
     63 
     64 Nfs3Status	logread(Cnode*, u32int, u64int, uchar**, u32int*, u1int*);
     65 Nfs3Status	refreshdiskread(Cnode*, u32int, u64int, uchar**, u32int*, u1int*);
     66 Nfs3Status	refreshconfigread(Cnode*, u32int, u64int, uchar**, u32int*, u1int*);
     67 
     68 int readconfigfile(Config *cp);
     69 void setrootfid(void);
     70 int ipokay(uchar *ip, ushort port);
     71 
     72 u64int	unittoull(char*);
     73 
     74 void
     75 usage(void)
     76 {
     77 	fprint(2, "usage: vnfs [-LLRVir] [-a addr] [-b blocksize] [-c cachesize] configfile\n");
     78 	threadexitsall("usage");
     79 }
     80 
     81 void
     82 threadmain(int argc, char **argv)
     83 {
     84 	fmtinstall('B', sunrpcfmt);
     85 	fmtinstall('C', suncallfmt);
     86 	fmtinstall('F', vtfcallfmt);
     87 	fmtinstall('H', encodefmt);
     88 	fmtinstall('I', eipfmt);
     89 	fmtinstall('V', vtscorefmt);
     90 	sunfmtinstall(&nfs3prog);
     91 	sunfmtinstall(&nfsmount3prog);
     92 
     93 	addr = "udp!*!2049";
     94 	blocksize = 8192;
     95 	cachesize = 400;
     96 	srv = sunsrv();
     97 	srv->ipokay = ipokay;
     98 	cryptinit();
     99 
    100 	ARGBEGIN{
    101 	default:
    102 		usage();
    103 	case 'E':
    104 		encryptedhandles = 0;
    105 		break;
    106 	case 'L':
    107 		if(srv->localonly == 0)
    108 			srv->localonly = 1;
    109 		else
    110 			srv->localparanoia = 1;
    111 		break;
    112 	case 'R':
    113 		srv->chatty++;
    114 		break;
    115 	case 'T':
    116 		tracecalls = 1;
    117 		break;
    118 	case 'V':
    119 		if(chattyventi++)
    120 			vttracelevel++;
    121 		break;
    122 	case 'a':
    123 		addr = EARGF(usage());
    124 		break;
    125 	case 'b':
    126 		blocksize = unittoull(EARGF(usage()));
    127 		break;
    128 	case 'c':
    129 		cachesize = unittoull(EARGF(usage()));
    130 		break;
    131 	case 'i':
    132 		insecure = 1;
    133 		break;
    134 	case 'r':
    135 		srv->alwaysreject++;
    136 		break;
    137 	}ARGEND
    138 
    139 	if(argc != 1)
    140 		usage();
    141 
    142 	if((z = vtdial(nil)) == nil)
    143 		sysfatal("vtdial: %r");
    144 	if(vtconnect(z) < 0)
    145 		sysfatal("vtconnect: %r");
    146 	if((vcache = vtcachealloc(z, blocksize*cachesize)) == nil)
    147 		sysfatal("vtcache: %r");
    148 
    149 	configfile = argv[0];
    150 	if(readconfigfile(&config) < 0)
    151 		sysfatal("readConfig: %r");
    152 	setrootfid();
    153 
    154 	nfschan = chancreate(sizeof(SunMsg*), 0);
    155 	mountchan = chancreate(sizeof(SunMsg*), 0);
    156 	timerchan = chancreate(sizeof(void*), 0);
    157 
    158 	if(sunsrvudp(srv, addr) < 0)
    159 		sysfatal("starting server: %r");
    160 
    161 	sunsrvthreadcreate(srv, nfs3proc, nfschan);
    162 	sunsrvthreadcreate(srv, mount3proc, mountchan);
    163 	sunsrvthreadcreate(srv, timerthread, nil);
    164 	proccreate(timerproc, nil, 32768);
    165 
    166 	sunsrvprog(srv, &nfs3prog, nfschan);
    167 	sunsrvprog(srv, &nfsmount3prog, mountchan);
    168 
    169 	threadexits(nil);
    170 }
    171 
    172 #define TWID64	((u64int)~(u64int)0)
    173 
    174 u64int
    175 unittoull(char *s)
    176 {
    177 	char *es;
    178 	u64int n;
    179 
    180 	if(s == nil)
    181 		return TWID64;
    182 	n = strtoul(s, &es, 0);
    183 	if(*es == 'k' || *es == 'K'){
    184 		n *= 1024;
    185 		es++;
    186 	}else if(*es == 'm' || *es == 'M'){
    187 		n *= 1024*1024;
    188 		es++;
    189 	}else if(*es == 'g' || *es == 'G'){
    190 		n *= 1024*1024*1024;
    191 		es++;
    192 	}else if(*es == 't' || *es == 'T'){
    193 		n *= 1024*1024;
    194 		n *= 1024*1024;
    195 	}
    196 	if(*es != '\0')
    197 		return TWID64;
    198 	return n;
    199 }
    200 
    201 /*
    202  * Handles.
    203  *
    204  * We store all the state about which file a client is accessing in
    205  * the handle, so that we don't have to maintain any per-client state
    206  * ourselves.  In order to avoid leaking handles or letting clients
    207  * create arbitrary handles, we sign and encrypt each handle with
    208  * AES using a key selected randomly when the server starts.
    209  * Thus, handles cannot be used across sessions.
    210  *
    211  * The decrypted handles begin with the following header:
    212  *
    213  *	sessid[8]		random session id chosen at start time
    214  *	len[4]		length of handle that follows
    215  *
    216  * If we're pressed for space in the rest of the handle, we could
    217  * probably reduce the amount of sessid bytes.  Note that the sessid
    218  * bytes must be consistent during a run of vnfs, or else some
    219  * clients (e.g., Linux 2.4) eventually notice that successive TLookups
    220  * return different handles, and they return "Stale NFS file handle"
    221  * errors to system calls in response (even though we never sent
    222  * that error!).
    223  *
    224  * Security woes aside, the fact that we have to shove everything
    225  * into the handles is quite annoying.  We have to encode, in 40 bytes:
    226  *
    227  *	- position in the synthesized config tree
    228  *	- enough of the path to do glob matching
    229  *	- position in an archived file system image
    230  *
    231  * and the handles need to be stable across changes in the config file
    232  * (though not across server restarts since encryption screws
    233  * that up nicely).
    234  *
    235  * We encode each of the first two as a 10-byte hash that is
    236  * the first half of a SHA1 hash.
    237  */
    238 
    239 enum
    240 {
    241 	SessidSize = 8,
    242 	HeaderSize = SessidSize+4,
    243 	MaxHandleSize = Nfs3MaxHandleSize - HeaderSize
    244 };
    245 
    246 AESstate		aesstate;
    247 uchar		sessid[SessidSize];
    248 
    249 static void
    250 hencrypt(Nfs3Handle *h)
    251 {
    252 	uchar *p;
    253 	AESstate aes;
    254 
    255 	/*
    256 	 * root handle has special encryption - a single 0 byte - so that it
    257 	 * never goes stale.
    258 	 */
    259 	if(h->len == root.len && memcmp(h->h, root.h, root.len) == 0){
    260 		h->h[0] = 0;
    261 		h->len = 1;
    262 		return;
    263 	}
    264 
    265 	if(!encryptedhandles)
    266 		return;
    267 
    268 	if(h->len > MaxHandleSize){
    269 		/* oops */
    270 		fprint(2, "handle too long: %.*lH\n", h->len, h->h);
    271 		memset(h->h, 'X', Nfs3MaxHandleSize);
    272 		h->len = Nfs3MaxHandleSize;
    273 		return;
    274 	}
    275 
    276 	p = h->h;
    277 	memmove(p+HeaderSize, p, h->len);
    278 	memmove(p, sessid, SessidSize);
    279 	*(u32int*)(p+SessidSize) = h->len;
    280 	h->len += HeaderSize;
    281 
    282 	if(encryptedhandles){
    283 		while(h->len < MaxHandleSize)
    284 			h->h[h->len++] = 0;
    285 		aes = aesstate;
    286 		aesCBCencrypt(h->h, MaxHandleSize, &aes);
    287 	}
    288 }
    289 
    290 static Nfs3Status
    291 hdecrypt(Nfs3Handle *h)
    292 {
    293 	AESstate aes;
    294 
    295 	if(h->len == 1 && h->h[0] == 0){	/* single 0 byte is root */
    296 		*h = root;
    297 		return Nfs3Ok;
    298 	}
    299 
    300 	if(!encryptedhandles)
    301 		return Nfs3Ok;
    302 
    303 	if(h->len <= HeaderSize)
    304 		return Nfs3ErrBadHandle;
    305 	if(encryptedhandles){
    306 		if(h->len != MaxHandleSize)
    307 			return Nfs3ErrBadHandle;
    308 		aes = aesstate;
    309 		aesCBCdecrypt(h->h, h->len, &aes);
    310 	}
    311 	if(memcmp(h->h, sessid, SessidSize) != 0)
    312 		return Nfs3ErrStale;	/* give benefit of doubt */
    313 	h->len = *(u32int*)(h->h+SessidSize);
    314 	if(h->len >= MaxHandleSize-HeaderSize)
    315 		return Nfs3ErrBadHandle;
    316 	memmove(h->h, h->h+HeaderSize, h->len);
    317 	return Nfs3Ok;
    318 }
    319 
    320 void
    321 cryptinit(void)
    322 {
    323 	uchar key[32], ivec[AESbsize];
    324 	int i;
    325 	u32int u32;
    326 
    327 	u32 = truerand();
    328 	memmove(sessid, &u32, 4);
    329 	for(i=0; i<nelem(key); i+=4) {
    330 		u32 = truerand();
    331 		memmove(key+i, &u32, 4);
    332 	}
    333 	for(i=0; i<nelem(ivec); i++)
    334 		ivec[i] = fastrand();
    335 	setupAESstate(&aesstate, key, sizeof key, ivec);
    336 }
    337 
    338 /*
    339  * Config file.
    340  *
    341  * The main purpose of the configuration file is to define a tree
    342  * in which the archived file system images are mounted.
    343  * The tree is stored as Entry structures, defined below.
    344  *
    345  * The configuration file also allows one to define shell-like
    346  * glob expressions matching paths that are not to be displayed.
    347  * The matched files or directories are shown in directory listings
    348  * (could suppress these if we cared) but they cannot be opened,
    349  * read, or written, and getattr returns zeroed data.
    350  */
    351 enum
    352 {
    353 	/* sizes used in handles; see nfs server below */
    354 	CnodeHandleSize = 8,
    355 	FsysHandleOffset = CnodeHandleSize
    356 };
    357 
    358 /*
    359  * Config file tree.
    360  */
    361 struct Ctree
    362 {
    363 	Cnode *root;
    364 	Cnode *hash[1024];
    365 };
    366 
    367 struct Cnode
    368 {
    369 	char *name;	/* path element */
    370 	Cnode *parent;	/* in tree */
    371 	Cnode *nextsib;	/* in tree */
    372 	Cnode *kidlist;	/* in tree */
    373 	Cnode *nexthash;	/* in hash list */
    374 
    375 	Nfs3Status (*read)(Cnode*, u32int, u64int, uchar**, u32int*, u1int*);	/* synthesized read fn */
    376 
    377 	uchar handle[VtScoreSize];	/* sha1(path to here) */
    378 	ulong mtime;	/* mtime for this directory entry */
    379 
    380 	/* fsys overlay on this node */
    381 	Fsys *fsys;	/* cache of memory structure */
    382 	Nfs3Handle fsyshandle;
    383 	int isblackhole;	/* walking down keeps you here */
    384 
    385 	/*
    386 	 * mount point info.
    387 	 * if a mount point is inside another file system,
    388 	 * the fsys and fsyshandle above have the old fs info,
    389 	 * the mfsys and mfsyshandle below have the new one.
    390 	 * getattrs must use the old info for consistency.
    391 	 */
    392 	int ismtpt;	/* whether there is an fsys mounted here */
    393 	uchar fsysscore[VtScoreSize];	/* score of fsys image on venti */
    394 	char *fsysimage;	/* raw disk image */
    395 	Fsys *mfsys;	/* mounted file system (nil until walked) */
    396 	Nfs3Handle mfsyshandle;	/* handle to root of mounted fsys */
    397 
    398 	int mark;	/* gc */
    399 };
    400 
    401 static uint
    402 dumbhash(uchar *s)
    403 {
    404 	return (s[0]<<2)|(s[1]>>6);	/* first 10 bits */
    405 }
    406 
    407 static Cnode*
    408 mkcnode(Ctree *t, Cnode *parent, char *elem, uint elen, char *path, uint plen)
    409 {
    410 	uint h;
    411 	Cnode *n;
    412 
    413 	n = emalloc(sizeof *n + elen+1);
    414 	n->name = (char*)(n+1);
    415 	memmove(n->name, elem, elen);
    416 	n->name[elen] = 0;
    417 	n->parent = parent;
    418 	if(parent){
    419 		n->nextsib = parent->kidlist;
    420 		parent->kidlist = n;
    421 	}
    422 	n->kidlist = nil;
    423 	sha1((uchar*)path, plen, n->handle, nil);
    424 	h = dumbhash(n->handle);
    425 	n->nexthash = t->hash[h];
    426 	t->hash[h] = n;
    427 
    428 	return n;
    429 }
    430 
    431 void
    432 markctree(Ctree *t)
    433 {
    434 	int i;
    435 	Cnode *n;
    436 
    437 	for(i=0; i<nelem(t->hash); i++)
    438 		for(n=t->hash[i]; n; n=n->nexthash)
    439 			if(n->name[0] != '+')
    440 				n->mark = 1;
    441 }
    442 
    443 int
    444 refreshdisk(void)
    445 {
    446 	int i;
    447 	Cnode *n;
    448 	Ctree *t;
    449 
    450 	t = config.ctree;
    451 	for(i=0; i<nelem(t->hash); i++)
    452 		for(n=t->hash[i]; n; n=n->nexthash){
    453 			if(n->mfsys)
    454 				disksync(n->mfsys->disk);
    455 			if(n->fsys)
    456 				disksync(n->fsys->disk);
    457 		}
    458 	return 0;
    459 }
    460 
    461 void
    462 sweepctree(Ctree *t)
    463 {
    464 	int i;
    465 	Cnode *n;
    466 
    467 	/* just zero all the garbage and leave it linked into the tree */
    468 	for(i=0; i<nelem(t->hash); i++){
    469 		for(n=t->hash[i]; n; n=n->nexthash){
    470 			if(!n->mark)
    471 				continue;
    472 			n->fsys = nil;
    473 			free(n->fsysimage);
    474 			n->fsysimage = nil;
    475 			memset(n->fsysscore, 0, sizeof n->fsysscore);
    476 			n->mfsys = nil;
    477 			n->ismtpt = 0;
    478 			memset(&n->fsyshandle, 0, sizeof n->fsyshandle);
    479 			memset(&n->mfsyshandle, 0, sizeof n->mfsyshandle);
    480 		}
    481 	}
    482 }
    483 
    484 static Cnode*
    485 cnodewalk(Cnode *n, char *name, uint len, int markokay)
    486 {
    487 	Cnode *nn;
    488 
    489 	for(nn=n->kidlist; nn; nn=nn->nextsib)
    490 		if(strncmp(nn->name, name, len) == 0 && nn->name[len] == 0)
    491 		if(!nn->mark || markokay)
    492 			return nn;
    493 	return nil;
    494 }
    495 
    496 Cnode*
    497 ctreewalkpath(Ctree *t, char *name, ulong createmtime)
    498 {
    499 	Cnode *n, *nn;
    500 	char *p, *nextp;
    501 
    502 	n = t->root;
    503 	p = name;
    504 	for(; *p; p=nextp){
    505 		n->mark = 0;
    506 		assert(*p == '/');
    507 		p++;
    508 		nextp = strchr(p, '/');
    509 		if(nextp == nil)
    510 			nextp = p+strlen(p);
    511 		if((nn = cnodewalk(n, p, nextp-p, 1)) == nil){
    512 			if(createmtime == 0)
    513 				return nil;
    514 			nn = mkcnode(t, n, p, nextp-p, name, nextp-name);
    515 			nn->mtime = createmtime;
    516 		}
    517 		if(nn->mark)
    518 			nn->mark = 0;
    519 		n = nn;
    520 	}
    521 	n->mark = 0;
    522 	return n;
    523 }
    524 
    525 Ctree*
    526 mkctree(void)
    527 {
    528 	Ctree *t;
    529 
    530 	t = emalloc(sizeof *t);
    531 	t->root = mkcnode(t, nil, "", 0, "", 0);
    532 
    533 	ctreewalkpath(t, "/+log", time(0))->read = logread;
    534 	ctreewalkpath(t, "/+refreshdisk", time(0))->read = refreshdiskread;
    535 	ctreewalkpath(t, "/+refreshconfig", time(0))->read = refreshconfigread;
    536 
    537 	return t;
    538 }
    539 
    540 Cnode*
    541 ctreemountfsys(Ctree *t, char *path, ulong time, uchar *score, char *file)
    542 {
    543 	Cnode *n;
    544 
    545 	if(time == 0)
    546 		time = 1;
    547 	n = ctreewalkpath(t, path, time);
    548 	if(score){
    549 		if(n->ismtpt && (n->fsysimage || memcmp(n->fsysscore, score, VtScoreSize) != 0)){
    550 			free(n->fsysimage);
    551 			n->fsysimage = nil;
    552 			n->fsys = nil;	/* leak (might be other refs) */
    553 		}
    554 		memmove(n->fsysscore, score, VtScoreSize);
    555 	}else{
    556 		if(n->ismtpt && (n->fsysimage==nil || strcmp(n->fsysimage, file) != 0)){
    557 			free(n->fsysimage);
    558 			n->fsysimage = nil;
    559 			n->fsys = nil;	/* leak (might be other refs) */
    560 		}
    561 		n->fsysimage = emalloc(strlen(file)+1);
    562 		strcpy(n->fsysimage, file);
    563 	}
    564 	n->ismtpt = 1;
    565 	return n;
    566 }
    567 
    568 Cnode*
    569 cnodebyhandle(Ctree *t, uchar *p)
    570 {
    571 	int h;
    572 	Cnode *n;
    573 
    574 	h = dumbhash(p);
    575 	for(n=t->hash[h]; n; n=n->nexthash)
    576 		if(memcmp(n->handle, p, CnodeHandleSize) == 0)
    577 			return n;
    578 	return nil;
    579 }
    580 
    581 static int
    582 parseipandmask(char *s, uchar *ip, uchar *mask)
    583 {
    584 	char *p, *q;
    585 
    586 	p = strchr(s, '/');
    587 	if(p)
    588 		*p++ = 0;
    589 	if(parseip(ip, s) == ~0UL)
    590 		return -1;
    591 	if(p == nil)
    592 		memset(mask, 0xFF, IPaddrlen);
    593 	else{
    594 		if(isdigit((uchar)*p) && strtol(p, &q, 10)>=0 && *q==0)
    595 			*--p = '/';
    596 		if(parseipmask(mask, p) == ~0UL)
    597 			return -1;
    598 		if(*p != '/')
    599 			*--p = '/';
    600 	}
    601 /*fprint(2, "parseipandmask %s => %I %I\n", s, ip, mask); */
    602 	return 0;
    603 }
    604 
    605 static int
    606 parsetime(char *s, ulong *time)
    607 {
    608 	ulong x;
    609 	char *p;
    610 	int i;
    611 	Tm tm;
    612 
    613 	/* decimal integer is seconds since 1970 */
    614 	x = strtoul(s, &p, 10);
    615 	if(x > 0 && *p == 0){
    616 		*time = x;
    617 		return 0;
    618 	}
    619 
    620 	/* otherwise expect yyyy/mmdd/hhmm */
    621 	if(strlen(s) != 14 || s[4] != '/' || s[9] != '/')
    622 		return -1;
    623 	for(i=0; i<4; i++)
    624 		if(!isdigit((uchar)s[i]) || !isdigit((uchar)s[i+5]) || !isdigit((uchar)s[i+10]))
    625 			return -1;
    626 	memset(&tm, 0, sizeof tm);
    627 	tm.year = atoi(s)-1900;
    628 	if(tm.year < 0 || tm.year > 200)
    629 		return -1;
    630 	tm.mon = (s[5]-'0')*10+s[6]-'0' - 1;
    631 	if(tm.mon < 0 || tm.mon > 11)
    632 		return -1;
    633 	tm.mday = (s[7]-'0')*10+s[8]-'0';
    634 	if(tm.mday < 0 || tm.mday > 31)
    635 		return -1;
    636 	tm.hour = (s[10]-'0')*10+s[11]-'0';
    637 	if(tm.hour < 0 || tm.hour > 23)
    638 		return -1;
    639 	tm.min = (s[12]-'0')*10+s[13]-'0';
    640 	if(tm.min < 0 || tm.min > 59)
    641 		return -1;
    642 	strcpy(tm.zone, "XXX");	/* anything but GMT */
    643 if(0){
    644 print("tm2sec %d/%d/%d/%d/%d\n",
    645 	tm.year, tm.mon, tm.mday, tm.hour, tm.min);
    646 }
    647 	*time = tm2sec(&tm);
    648 if(0) print("time %lud\n", *time);
    649 	return 0;
    650 }
    651 
    652 
    653 int
    654 readconfigfile(Config *cp)
    655 {
    656 	char *f[10], *image, *p, *pref, *q, *name;
    657 	int nf, line;
    658 	uchar scorebuf[VtScoreSize], *score;
    659 	ulong time;
    660 	Biobuf *b;
    661 	Config c;
    662 	Dir *dir;
    663 
    664 	name = configfile;
    665 	c = *cp;
    666 	if((dir = dirstat(name)) == nil)
    667 		return -1;
    668 	if(c.mtime == dir->mtime){
    669 		free(dir);
    670 		return 0;
    671 	}
    672 	c.mtime = dir->mtime;
    673 	free(dir);
    674 	if((b = Bopen(name, OREAD)) == nil)
    675 		return -1;
    676 
    677 	/*
    678 	 * Reuse old tree, garbage collecting entries that
    679 	 * are not mentioned in the new config file.
    680 	 */
    681 	if(c.ctree == nil)
    682 		c.ctree = mkctree();
    683 
    684 	markctree(c.ctree);
    685 	c.ok = nil;
    686 	c.nok = 0;
    687 
    688 	line = 0;
    689 	for(; (p=Brdstr(b, '\n', 1)) != nil; free(p)){
    690 		line++;
    691 		if((q = strchr(p, '#')) != nil)
    692 			*q = 0;
    693 		nf = tokenize(p, f, nelem(f));
    694 		if(nf == 0)
    695 			continue;
    696 		if(strcmp(f[0], "mount") == 0){
    697 			if(nf != 4){
    698 				werrstr("syntax error: mount /path /dev|score mtime");
    699 				goto badline;
    700 			}
    701 			if(f[1][0] != '/'){
    702 				werrstr("unrooted path %s", f[1]);
    703 				goto badline;
    704 			}
    705 			score = nil;
    706 			image = nil;
    707 			if(f[2][0] == '/'){
    708 				if(access(f[2], AEXIST) < 0){
    709 					werrstr("image %s does not exist", f[2]);
    710 					goto badline;
    711 				}
    712 				image = f[2];
    713 			}else{
    714 				if(vtparsescore(f[2], &pref, scorebuf) < 0){
    715 					werrstr("bad score %s", f[2]);
    716 					goto badline;
    717 				}
    718 				score = scorebuf;
    719 			}
    720 			if(parsetime(f[3], &time) < 0){
    721 				fprint(2, "%s:%d: bad time %s\n", name, line, f[3]);
    722 				time = 1;
    723 			}
    724 			ctreemountfsys(c.ctree, f[1], time, score, image);
    725 			continue;
    726 		}
    727 		if(strcmp(f[0], "allow") == 0 || strcmp(f[0], "deny") == 0){
    728 			if(nf != 2){
    729 				werrstr("syntax error: allow|deny ip[/mask]");
    730 				goto badline;
    731 			}
    732 			c.ok = erealloc(c.ok, (c.nok+1)*sizeof(c.ok[0]));
    733 			if(parseipandmask(f[1], c.ok[c.nok].ip, c.ok[c.nok].mask) < 0){
    734 				werrstr("bad ip[/mask]: %s", f[1]);
    735 				goto badline;
    736 			}
    737 			c.ok[c.nok].okay = (strcmp(f[0], "allow") == 0);
    738 			c.nok++;
    739 			continue;
    740 		}
    741 		werrstr("unknown verb '%s'", f[0]);
    742 	badline:
    743 		fprint(2, "%s:%d: %r\n", name, line);
    744 	}
    745 	Bterm(b);
    746 
    747 	sweepctree(c.ctree);
    748 	free(cp->ok);
    749 	*cp = c;
    750 	return 0;
    751 }
    752 
    753 int
    754 ipokay(uchar *ip, ushort port)
    755 {
    756 	int i;
    757 	uchar ipx[IPaddrlen];
    758 	Ipokay *ok;
    759 
    760 	for(i=0; i<config.nok; i++){
    761 		ok = &config.ok[i];
    762 		maskip(ip, ok->mask, ipx);
    763 if(0) fprint(2, "%I & %I = %I (== %I?)\n",
    764 	ip, ok->mask, ipx, ok->ip);
    765 		if(memcmp(ipx, ok->ip, IPaddrlen) == 0)
    766 			return ok->okay;
    767 	}
    768 	if(config.nok == 0)	/* all is permitted */
    769 		return 1;
    770 	/* otherwise default is none allowed */
    771 	return 0;
    772 }
    773 
    774 Nfs3Status
    775 cnodelookup(Ctree *t, Cnode **np, char *name)
    776 {
    777 	Cnode *n, *nn;
    778 
    779 	n = *np;
    780 	if(n->isblackhole)
    781 		return Nfs3Ok;
    782 	if((nn = cnodewalk(n, name, strlen(name), 0)) == nil){
    783 		if(n->ismtpt || n->fsys){
    784 			if((nn = cnodewalk(n, "", 0, 1)) == nil){
    785 				nn = mkcnode(t, n, "", 0, (char*)n->handle, SHA1dlen);
    786 				nn->isblackhole = 1;
    787 			}
    788 			nn->mark = 0;
    789 		}
    790 	}
    791 	if(nn == nil)
    792 		return Nfs3ErrNoEnt;
    793 	*np = nn;
    794 	return Nfs3Ok;
    795 }
    796 
    797 Nfs3Status
    798 cnodegetattr(Cnode *n, Nfs3Attr *attr)
    799 {
    800 	uint64 u64;
    801 
    802 	memset(attr, 0, sizeof *attr);
    803 	if(n->read){
    804 		attr->type = Nfs3FileReg;
    805 		attr->mode = 0444;
    806 		attr->size = 512;
    807 		attr->nlink = 1;
    808 	}else{
    809 		attr->type = Nfs3FileDir;
    810 		attr->mode = 0555;
    811 		attr->size = 1024;
    812 		attr->nlink = 10;
    813 	}
    814 	memmove(&u64, n->handle, 8);
    815 	attr->fileid = u64;
    816 	attr->atime.sec = n->mtime;
    817 	attr->mtime.sec = n->mtime;
    818 	attr->ctime.sec = n->mtime;
    819 	return Nfs3Ok;
    820 }
    821 
    822 Nfs3Status
    823 cnodereaddir(Cnode *n, u32int count, u64int cookie, uchar **pdata, u32int *pcount, u1int *peof)
    824 {
    825 	uchar *data, *p, *ep, *np;
    826 	u64int c;
    827 	u64int u64;
    828 	Nfs3Entry ne;
    829 
    830 	n = n->kidlist;
    831 	c = cookie;
    832 	for(; c && n; c--)
    833 		n = n->nextsib;
    834 	if(n == nil){
    835 		*pdata = 0;
    836 		*pcount = 0;
    837 		*peof = 1;
    838 		return Nfs3Ok;
    839 	}
    840 
    841 	data = emalloc(count);
    842 	p = data;
    843 	ep = data+count;
    844 	while(n && p < ep){
    845 		if(n->mark || n->name[0] == '+'){
    846 			n = n->nextsib;
    847 			++cookie;
    848 			continue;
    849 		}
    850 		ne.name = n->name;
    851 		ne.namelen = strlen(n->name);
    852 		ne.cookie = ++cookie;
    853 		memmove(&u64, n->handle, 8);
    854 		ne.fileid = u64;
    855 		if(nfs3entrypack(p, ep, &np, &ne) < 0)
    856 			break;
    857 		p = np;
    858 		n = n->nextsib;
    859 	}
    860 	*pdata = data;
    861 	*pcount = p - data;
    862 	*peof = n==nil;
    863 	return Nfs3Ok;
    864 }
    865 
    866 void
    867 timerproc(void *v)
    868 {
    869 	for(;;){
    870 		sleep(60*1000);
    871 		sendp(timerchan, 0);
    872 	}
    873 }
    874 
    875 void
    876 timerthread(void *v)
    877 {
    878 	for(;;){
    879 		recvp(timerchan);
    880 	/*	refreshconfig(); */
    881 	}
    882 }
    883 
    884 /*
    885  * Actually serve the NFS requests.  Called from nfs3srv.c.
    886  * Each request runs in its own thread (coroutine).
    887  *
    888  * Decrypted handles have the form:
    889  *
    890  *	config[20] - SHA1 hash identifying a config tree node
    891  *	glob[10] - SHA1 hash prefix identifying a glob state
    892  *	fsyshandle[<=10] - disk file system handle (usually 4 bytes)
    893  */
    894 
    895 /*
    896  * A fid represents a point in the file tree.
    897  * There are three components, all derived from the handle:
    898  *
    899  *	- config tree position (also used to find fsys)
    900  *	- glob state for exclusions
    901  *	- file system position
    902  */
    903 enum
    904 {
    905 	HAccess,
    906 	HAttr,
    907 	HWalk,
    908 	HDotdot,
    909 	HRead
    910 };
    911 typedef struct Fid Fid;
    912 struct Fid
    913 {
    914 	Cnode *cnode;
    915 	Fsys *fsys;
    916 	Nfs3Handle fsyshandle;
    917 };
    918 
    919 int
    920 handlecmp(Nfs3Handle *h, Nfs3Handle *h1)
    921 {
    922 	if(h->len != h1->len)
    923 		return h->len - h1->len;
    924 	return memcmp(h->h, h1->h, h->len);
    925 }
    926 
    927 Nfs3Status
    928 handletofid(Nfs3Handle *eh, Fid *fid, int mode)
    929 {
    930 	int domount;
    931 	Cnode *n;
    932 	Disk *disk, *cdisk;
    933 	Fsys *fsys;
    934 	Nfs3Status ok;
    935 	Nfs3Handle h2, *h, *fh;
    936 
    937 	memset(fid, 0, sizeof *fid);
    938 
    939 	domount = 1;
    940 	if(mode == HDotdot)
    941 		domount = 0;
    942 	/*
    943 	 * Not necessary, but speeds up ls -l /dump/2005
    944 	 * HAttr and HAccess must be handled the same way
    945 	 * because both can be used to fetch attributes.
    946 	 * Acting differently yields inconsistencies at mount points,
    947 	 * and causes FreeBSD ls -l to fail.
    948 	 */
    949 	if(mode == HAttr || mode == HAccess)
    950 		domount = 0;
    951 
    952 	/*
    953 	 * Decrypt handle.
    954 	 */
    955 	h2 = *eh;
    956 	h = &h2;
    957 	if((ok = hdecrypt(h)) != Nfs3Ok)
    958 		return ok;
    959 	trace("handletofid: decrypted %.*lH\n", h->len, h->h);
    960 	if(h->len < FsysHandleOffset)
    961 		return Nfs3ErrBadHandle;
    962 
    963 	/*
    964 	 * Find place in config tree.
    965 	 */
    966 	if((n = cnodebyhandle(config.ctree, h->h)) == nil)
    967 		return Nfs3ErrStale;
    968 	fid->cnode = n;
    969 
    970 	if(n->ismtpt && domount){
    971 		/*
    972 		 * Open fsys for mount point if needed.
    973 		 */
    974 		if(n->mfsys == nil){
    975 			trace("handletofid: mounting %V/%s\n", n->fsysscore, n->fsysimage);
    976 			if(n->fsysimage){
    977 				if(strcmp(n->fsysimage, "/dev/null") == 0)
    978 					return Nfs3ErrAcces;
    979 				if((disk = diskopenfile(n->fsysimage)) == nil){
    980 					fprint(2, "cannot open disk %s: %r\n", n->fsysimage);
    981 					return Nfs3ErrIo;
    982 				}
    983 				if((cdisk = diskcache(disk, blocksize, 64)) == nil){
    984 					fprint(2, "cannot cache disk %s: %r\n", n->fsysimage);
    985 					diskclose(disk);
    986 				}
    987 				disk = cdisk;
    988 			}else{
    989 				if((disk = diskopenventi(vcache, n->fsysscore)) == nil){
    990 					fprint(2, "cannot open venti disk %V: %r\n", n->fsysscore);
    991 					return Nfs3ErrIo;
    992 				}
    993 			}
    994 			if((fsys = fsysopen(disk)) == nil){
    995 				fprint(2, "cannot open fsys on %V: %r\n", n->fsysscore);
    996 				diskclose(disk);
    997 				return Nfs3ErrIo;
    998 			}
    999 			n->mfsys = fsys;
   1000 			fsysroot(fsys, &n->mfsyshandle);
   1001 		}
   1002 
   1003 		/*
   1004 		 * Use inner handle.
   1005 		 */
   1006 		fid->fsys = n->mfsys;
   1007 		fid->fsyshandle = n->mfsyshandle;
   1008 	}else{
   1009 		/*
   1010 		 * Use fsys handle from tree or from handle.
   1011 		 * This assumes that fsyshandle was set by fidtohandle
   1012 		 * earlier, so it's not okay to reuse handles (except the root)
   1013 		 * across sessions.  The encryption above makes and
   1014 		 * enforces the same restriction, so this is okay.
   1015 		 */
   1016 		fid->fsys = n->fsys;
   1017 		fh = &fid->fsyshandle;
   1018 		if(n->isblackhole){
   1019 			fh->len = h->len-FsysHandleOffset;
   1020 			memmove(fh->h, h->h+FsysHandleOffset, fh->len);
   1021 		}else
   1022 			*fh = n->fsyshandle;
   1023 		trace("handletofid: fsyshandle %.*lH\n", fh->len, fh->h);
   1024 	}
   1025 
   1026 	/*
   1027 	 * TO DO (maybe): some sort of path restriction here.
   1028 	 */
   1029 	trace("handletofid: cnode %s fsys %p fsyshandle %.*lH\n",
   1030 		n->name, fid->fsys, fid->fsyshandle.len, fid->fsyshandle.h);
   1031 	return Nfs3Ok;
   1032 }
   1033 
   1034 void
   1035 _fidtohandle(Fid *fid, Nfs3Handle *h)
   1036 {
   1037 	Cnode *n;
   1038 
   1039 	n = fid->cnode;
   1040 	/*
   1041 	 * Record fsys handle in n, don't bother sending it to client
   1042 	 * for black holes.
   1043 	 */
   1044 	n->fsys = fid->fsys;
   1045 	if(!n->isblackhole){
   1046 		n->fsyshandle = fid->fsyshandle;
   1047 		fid->fsyshandle.len = 0;
   1048 	}
   1049 	memmove(h->h, n->handle, CnodeHandleSize);
   1050 	memmove(h->h+FsysHandleOffset, fid->fsyshandle.h, fid->fsyshandle.len);
   1051 	h->len = FsysHandleOffset+fid->fsyshandle.len;
   1052 }
   1053 
   1054 void
   1055 fidtohandle(Fid *fid, Nfs3Handle *h)
   1056 {
   1057 	_fidtohandle(fid, h);
   1058 	hencrypt(h);
   1059 }
   1060 
   1061 void
   1062 setrootfid(void)
   1063 {
   1064 	Fid fid;
   1065 
   1066 	memset(&fid, 0, sizeof fid);
   1067 	fid.cnode = config.ctree->root;
   1068 	_fidtohandle(&fid, &root);
   1069 }
   1070 
   1071 void
   1072 fsgetroot(Nfs3Handle *h)
   1073 {
   1074 	*h = root;
   1075 	hencrypt(h);
   1076 }
   1077 
   1078 Nfs3Status
   1079 fsgetattr(SunAuthUnix *au, Nfs3Handle *h, Nfs3Attr *attr)
   1080 {
   1081 	Fid fid;
   1082 	Nfs3Status ok;
   1083 
   1084 	trace("getattr %.*lH\n", h->len, h->h);
   1085 	if((ok = handletofid(h, &fid, HAttr)) != Nfs3Ok)
   1086 		return ok;
   1087 	if(fid.fsys)
   1088 		return fsysgetattr(fid.fsys, au, &fid.fsyshandle, attr);
   1089 	else
   1090 		return cnodegetattr(fid.cnode, attr);
   1091 }
   1092 
   1093 /*
   1094  * Lookup is always the hard part.
   1095  */
   1096 Nfs3Status
   1097 fslookup(SunAuthUnix *au, Nfs3Handle *h, char *name, Nfs3Handle *nh)
   1098 {
   1099 	Fid fid;
   1100 	Cnode *n;
   1101 	Nfs3Status ok;
   1102 	Nfs3Handle xh;
   1103 	int mode;
   1104 
   1105 	trace("lookup %.*lH %s\n", h->len, h->h, name);
   1106 
   1107 	mode = HWalk;
   1108 	if(strcmp(name, "..") == 0 || strcmp(name, ".") == 0)
   1109 		mode = HDotdot;
   1110 	if((ok = handletofid(h, &fid, mode)) != Nfs3Ok){
   1111 		nfs3errstr(ok);
   1112 		trace("lookup: handletofid %r\n");
   1113 		return ok;
   1114 	}
   1115 
   1116 	if(strcmp(name, ".") == 0){
   1117 		fidtohandle(&fid, nh);
   1118 		return Nfs3Ok;
   1119 	}
   1120 
   1121 	/*
   1122 	 * Walk down file system and cnode simultaneously.
   1123 	 * If dotdot and file system doesn't move, need to walk
   1124 	 * up cnode.  Save the corresponding fsys handles in
   1125 	 * the cnode as we walk down so that we'll have them
   1126 	 * for dotdotting back up.
   1127 	 */
   1128 	n = fid.cnode;
   1129 	if(mode == HWalk){
   1130 		/*
   1131 		 * Walk down config tree and file system simultaneously.
   1132 		 */
   1133 		if((ok = cnodelookup(config.ctree, &n, name)) != Nfs3Ok){
   1134 			nfs3errstr(ok);
   1135 			trace("lookup: cnodelookup: %r\n");
   1136 			return ok;
   1137 		}
   1138 		fid.cnode = n;
   1139 		if(fid.fsys){
   1140 			if((ok = fsyslookup(fid.fsys, au, &fid.fsyshandle, name, &xh)) != Nfs3Ok){
   1141 				nfs3errstr(ok);
   1142 				trace("lookup: fsyslookup: %r\n");
   1143 				return ok;
   1144 			}
   1145 			fid.fsyshandle = xh;
   1146 		}
   1147 	}else{
   1148 		/*
   1149 		 * Walking dotdot.  Ick.
   1150 		 */
   1151 		trace("lookup dotdot fsys=%p\n", fid.fsys);
   1152 		if(fid.fsys){
   1153 			/*
   1154 			 * Walk up file system, then try up config tree.
   1155 			 */
   1156 			if((ok = fsyslookup(fid.fsys, au, &fid.fsyshandle, "..", &xh)) != Nfs3Ok){
   1157 				nfs3errstr(ok);
   1158 				trace("lookup fsyslookup: %r\n");
   1159 				return ok;
   1160 			}
   1161 			fid.fsyshandle = xh;
   1162 
   1163 			/*
   1164 			 * Usually just go to n->parent.
   1165 			 *
   1166 			 * If we're in a subtree of the mounted file system that
   1167 			 * isn't represented explicitly by the config tree (instead
   1168 			 * the black hole node represents the entire file tree),
   1169 			 * then we only go to n->parent when we've dotdotted back
   1170 			 * to the right handle.
   1171 			 */
   1172 			if(n->parent == nil)
   1173 				trace("lookup dotdot: no parent\n");
   1174 			else{
   1175 				trace("lookup dotdot: parent %.*lH, have %.*lH\n",
   1176 					n->parent->fsyshandle.len, n->parent->fsyshandle.h,
   1177 					xh.len, xh.h);
   1178 			}
   1179 
   1180 			if(n->isblackhole){
   1181 				if(handlecmp(&n->parent->mfsyshandle, &xh) == 0)
   1182 					n = n->parent;
   1183 			}else{
   1184 				if(n->parent)
   1185 					n = n->parent;
   1186 			}
   1187 		}else{
   1188 			/*
   1189 			 * No file system, just walk up.
   1190 			 */
   1191 			if(n->parent)
   1192 				n = n->parent;
   1193 		}
   1194 		fid.fsys = n->fsys;
   1195 		if(!n->isblackhole)
   1196 			fid.fsyshandle = n->fsyshandle;
   1197 		fid.cnode = n;
   1198 	}
   1199 	fidtohandle(&fid, nh);
   1200 	return Nfs3Ok;
   1201 }
   1202 
   1203 Nfs3Status
   1204 fsaccess(SunAuthUnix *au, Nfs3Handle *h, u32int want, u32int *got, Nfs3Attr *attr)
   1205 {
   1206 	Fid fid;
   1207 	Nfs3Status ok;
   1208 
   1209 	trace("access %.*lH 0x%ux\n", h->len, h->h, want);
   1210 	if((ok = handletofid(h, &fid, HAccess)) != Nfs3Ok)
   1211 		return ok;
   1212 	if(fid.fsys)
   1213 		return fsysaccess(fid.fsys, au, &fid.fsyshandle, want, got, attr);
   1214 	*got = want & (Nfs3AccessRead|Nfs3AccessLookup|Nfs3AccessExecute);
   1215 	return cnodegetattr(fid.cnode, attr);
   1216 }
   1217 
   1218 Nfs3Status
   1219 fsreadlink(SunAuthUnix *au, Nfs3Handle *h, char **link)
   1220 {
   1221 	Fid fid;
   1222 	Nfs3Status ok;
   1223 
   1224 	trace("readlink %.*lH\n", h->len, h->h);
   1225 	if((ok = handletofid(h, &fid, HRead)) != Nfs3Ok)
   1226 		return ok;
   1227 	if(fid.fsys)
   1228 		return fsysreadlink(fid.fsys, au, &fid.fsyshandle, link);
   1229 	*link = 0;
   1230 	return Nfs3ErrNotSupp;
   1231 }
   1232 
   1233 Nfs3Status
   1234 fsreadfile(SunAuthUnix *au, Nfs3Handle *h, u32int count, u64int offset, uchar **data, u32int *pcount, u1int *peof)
   1235 {
   1236 	Fid fid;
   1237 	Nfs3Status ok;
   1238 
   1239 	trace("readfile %.*lH\n", h->len, h->h);
   1240 	if((ok = handletofid(h, &fid, HRead)) != Nfs3Ok)
   1241 		return ok;
   1242 	if(fid.cnode->read)
   1243 		return fid.cnode->read(fid.cnode, count, offset, data, pcount, peof);
   1244 	if(fid.fsys)
   1245 		return fsysreadfile(fid.fsys, au, &fid.fsyshandle, count, offset, data, pcount, peof);
   1246 	return Nfs3ErrNotSupp;
   1247 }
   1248 
   1249 Nfs3Status
   1250 fsreaddir(SunAuthUnix *au, Nfs3Handle *h, u32int len, u64int cookie, uchar **pdata, u32int *pcount, u1int *peof)
   1251 {
   1252 	Fid fid;
   1253 	Nfs3Status ok;
   1254 
   1255 	trace("readdir %.*lH\n", h->len, h->h);
   1256 	if((ok = handletofid(h, &fid, HRead)) != Nfs3Ok)
   1257 		return ok;
   1258 	if(fid.fsys)
   1259 		return fsysreaddir(fid.fsys, au, &fid.fsyshandle, len, cookie, pdata, pcount, peof);
   1260 	return cnodereaddir(fid.cnode, len, cookie, pdata, pcount, peof);
   1261 }
   1262 
   1263 Nfs3Status
   1264 logread(Cnode *n, u32int count, u64int offset, uchar **data, u32int *pcount, u1int *peof)
   1265 {
   1266 	*pcount = 0;
   1267 	*peof = 1;
   1268 	return Nfs3Ok;
   1269 }
   1270 
   1271 Nfs3Status
   1272 refreshdiskread(Cnode *n, u32int count, u64int offset, uchar **data, u32int *pcount, u1int *peof)
   1273 {
   1274 	char buf[128];
   1275 
   1276 	if(offset != 0){
   1277 		*pcount = 0;
   1278 		*peof = 1;
   1279 		return Nfs3Ok;
   1280 	}
   1281 	if(refreshdisk() < 0)
   1282 		snprint(buf, sizeof buf, "refreshdisk: %r\n");
   1283 	else
   1284 		strcpy(buf, "ok\n");
   1285 	*data = emalloc(strlen(buf));
   1286 	strcpy((char*)*data, buf);
   1287 	*pcount = strlen(buf);
   1288 	*peof = 1;
   1289 	return Nfs3Ok;
   1290 }
   1291 
   1292 Nfs3Status
   1293 refreshconfigread(Cnode *n, u32int count, u64int offset, uchar **data, u32int *pcount, u1int *peof)
   1294 {
   1295 	char buf[128];
   1296 
   1297 	if(offset != 0){
   1298 		*pcount = 0;
   1299 		*peof = 1;
   1300 		return Nfs3Ok;
   1301 	}
   1302 	if(readconfigfile(&config) < 0)
   1303 		snprint(buf, sizeof buf, "readconfig: %r\n");
   1304 	else
   1305 		strcpy(buf, "ok\n");
   1306 	*data = emalloc(strlen(buf));
   1307 	strcpy((char*)*data, buf);
   1308 	*pcount = strlen(buf);
   1309 	*peof = 1;
   1310 	return Nfs3Ok;
   1311 }