Blob


1 /*
2 * TO DO:
3 * - gc of file systems (not going to do just yet?)
4 * - statistics file
5 * - configure on amsterdam
6 */
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"
20 #define trace if(!tracecalls){}else print
22 typedef struct Ipokay Ipokay;
23 typedef struct Config Config;
24 typedef struct Ctree Ctree;
25 typedef struct Cnode Cnode;
27 struct Ipokay
28 {
29 int okay;
30 uchar ip[IPaddrlen];
31 uchar mask[IPaddrlen];
32 };
34 struct Config
35 {
36 Ipokay *ok;
37 uint nok;
38 ulong mtime;
39 Ctree *ctree;
40 };
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;
57 void cryptinit(void);
58 void timerthread(void*);
59 void timerproc(void*);
61 extern void handleunparse(Fsys*, Nfs3Handle*, Nfs3Handle*, int);
62 extern Nfs3Status handleparse(Nfs3Handle*, Fsys**, Nfs3Handle*, int);
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*);
68 int readconfigfile(Config *cp);
69 void setrootfid(void);
70 int ipokay(uchar *ip, ushort port);
72 u64int unittoull(char*);
74 void
75 usage(void)
76 {
77 fprint(2, "usage: vnfs [-LLRVr] [-a addr] [-b blocksize] [-c cachesize] configfile\n");
78 threadexitsall("usage");
79 }
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);
93 addr = "udp!*!2049";
94 blocksize = 8192;
95 cachesize = 400;
96 srv = sunsrv();
97 srv->ipokay = ipokay;
98 cryptinit();
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 chattyventi = 1;
120 break;
121 case 'a':
122 addr = EARGF(usage());
123 break;
124 case 'b':
125 blocksize = unittoull(EARGF(usage()));
126 break;
127 case 'c':
128 cachesize = unittoull(EARGF(usage()));
129 break;
130 case 'r':
131 srv->alwaysreject++;
132 break;
133 }ARGEND
135 if(argc != 1)
136 usage();
138 if((z = vtdial(nil)) == nil)
139 sysfatal("vtdial: %r");
140 if(vtconnect(z) < 0)
141 sysfatal("vtconnect: %r");
142 if((vcache = vtcachealloc(z, blocksize, cachesize)) == nil)
143 sysfatal("vtcache: %r");
145 configfile = argv[0];
146 if(readconfigfile(&config) < 0)
147 sysfatal("readConfig: %r");
148 setrootfid();
150 nfschan = chancreate(sizeof(SunMsg*), 0);
151 mountchan = chancreate(sizeof(SunMsg*), 0);
152 timerchan = chancreate(sizeof(void*), 0);
154 if(sunsrvudp(srv, addr) < 0)
155 sysfatal("starting server: %r");
157 sunsrvthreadcreate(srv, nfs3proc, nfschan);
158 sunsrvthreadcreate(srv, mount3proc, mountchan);
159 sunsrvthreadcreate(srv, timerthread, nil);
160 proccreate(timerproc, nil, 32768);
162 sunsrvprog(srv, &nfs3prog, nfschan);
163 sunsrvprog(srv, &nfsmount3prog, mountchan);
165 threadexits(nil);
168 #define TWID64 ((u64int)~(u64int)0)
170 u64int
171 unittoull(char *s)
173 char *es;
174 u64int n;
176 if(s == nil)
177 return TWID64;
178 n = strtoul(s, &es, 0);
179 if(*es == 'k' || *es == 'K'){
180 n *= 1024;
181 es++;
182 }else if(*es == 'm' || *es == 'M'){
183 n *= 1024*1024;
184 es++;
185 }else if(*es == 'g' || *es == 'G'){
186 n *= 1024*1024*1024;
187 es++;
188 }else if(*es == 't' || *es == 'T'){
189 n *= 1024*1024;
190 n *= 1024*1024;
192 if(*es != '\0')
193 return TWID64;
194 return n;
197 /*
198 * Handles.
200 * We store all the state about which file a client is accessing in
201 * the handle, so that we don't have to maintain any per-client state
202 * ourselves. In order to avoid leaking handles or letting clients
203 * create arbitrary handles, we sign and encrypt each handle with
204 * AES using a key selected randomly when the server starts.
205 * Thus, handles cannot be used across sessions.
207 * The decrypted handles begin with the following header:
209 * rand[12] random bytes used to make encryption non-deterministic
210 * len[4] length of handle that follows
211 * sessid[8] random session id chosen at start time
213 * If we're pressed for space in the rest of the handle, we could
214 * probably reduce the amount of randomness.
216 * Security woes aside, the fact that we have to shove everything
217 * into the handles is quite annoying. We have to encode, in 40 bytes:
219 * - position in the synthesized config tree
220 * - enough of the path to do glob matching
221 * - position in an archived file system image
223 * and the handles need to be stable across changes in the config file
224 * (though not across server restarts since encryption screws
225 * that up nicely).
227 * We encode each of the first two as a 10-byte hash that is
228 * the first half of a SHA1 hash.
229 */
231 enum
233 RandSize = 16,
234 SessidSize = 8,
235 HeaderSize = RandSize+SessidSize,
236 MaxHandleSize = Nfs3MaxHandleSize - HeaderSize,
237 };
239 AESstate aesstate;
240 uchar sessid[SessidSize];
242 static void
243 hencrypt(Nfs3Handle *h)
245 uchar *p;
246 AESstate aes;
248 /*
249 * root handle has special encryption - a single 0 byte - so that it
250 * never goes stale.
251 */
252 if(h->len == root.len && memcmp(h->h, root.h, root.len) == 0){
253 h->h[0] = 0;
254 h->len = 1;
255 return;
258 if(!encryptedhandles)
259 return;
261 if(h->len > MaxHandleSize){
262 /* oops */
263 fprint(2, "handle too long: %.*lH\n", h->len, h->h);
264 memset(h->h, 'X', Nfs3MaxHandleSize);
265 h->len = Nfs3MaxHandleSize;
266 return;
269 p = h->h;
270 memmove(p+HeaderSize, p, h->len);
271 *(u32int*)p = fastrand();
272 *(u32int*)(p+4) = fastrand();
273 *(u32int*)(p+8) = fastrand();
274 *(u32int*)(p+12) = h->len;
275 memmove(p+16, sessid, SessidSize);
276 h->len += HeaderSize;
278 if(encryptedhandles){
279 while(h->len < MaxHandleSize)
280 h->h[h->len++] = fastrand();
281 aes = aesstate;
282 aesCBCencrypt(h->h, MaxHandleSize, &aes);
286 static Nfs3Status
287 hdecrypt(Nfs3Handle *h)
289 AESstate aes;
291 if(h->len == 1 && h->h[0] == 0){ /* single 0 byte is root */
292 *h = root;
293 return Nfs3Ok;
296 if(!encryptedhandles)
297 return Nfs3Ok;
299 if(h->len <= HeaderSize)
300 return Nfs3ErrBadHandle;
301 if(encryptedhandles){
302 if(h->len != MaxHandleSize)
303 return Nfs3ErrBadHandle;
304 aes = aesstate;
305 aesCBCdecrypt(h->h, h->len, &aes);
307 if(memcmp(h->h+RandSize, sessid, sizeof sessid) != 0)
308 return Nfs3ErrStale; /* give benefit of doubt */
309 h->len = *(u32int*)(h->h+12); /* XXX byte order */
310 memmove(h->h, h->h+HeaderSize, h->len);
311 return Nfs3Ok;
314 void
315 cryptinit(void)
317 uchar key[32], ivec[AESbsize];
318 int i;
320 *(u32int*)sessid = truerand();
321 for(i=0; i<nelem(key); i+=4)
322 *(u32int*)&key[i] = truerand();
323 for(i=0; i<nelem(ivec); i++)
324 ivec[i] = fastrand();
325 setupAESstate(&aesstate, key, sizeof key, ivec);
328 /*
329 * Config file.
331 * The main purpose of the configuration file is to define a tree
332 * in which the archived file system images are mounted.
333 * The tree is stored as Entry structures, defined below.
335 * The configuration file also allows one to define shell-like
336 * glob expressions matching paths that are not to be displayed.
337 * The matched files or directories are shown in directory listings
338 * (could suppress these if we cared) but they cannot be opened,
339 * read, or written, and getattr returns zeroed data.
340 */
341 enum
343 /* sizes used in handles; see nfs server below */
344 CnodeHandleSize = 8,
345 FsysHandleOffset = CnodeHandleSize,
346 };
348 /*
349 * Config file tree.
350 */
351 struct Ctree
353 Cnode *root;
354 Cnode *hash[1024];
355 };
357 struct Cnode
359 char *name; /* path element */
360 Cnode *parent; /* in tree */
361 Cnode *nextsib; /* in tree */
362 Cnode *kidlist; /* in tree */
363 Cnode *nexthash; /* in hash list */
365 Nfs3Status (*read)(Cnode*, u32int, u64int, uchar**, u32int*, u1int*); /* synthesized read fn */
367 uchar handle[VtScoreSize]; /* sha1(path to here) */
368 ulong mtime; /* mtime for this directory entry */
370 /* fsys overlay on this node */
371 Fsys *fsys; /* cache of memory structure */
372 Nfs3Handle fsyshandle;
373 int isblackhole; /* walking down keeps you here */
375 /*
376 * mount point info.
377 * if a mount point is inside another file system,
378 * the fsys and fsyshandle above have the old fs info,
379 * the mfsys and mfsyshandle below have the new one.
380 * getattrs must use the old info for consistency.
381 */
382 int ismtpt; /* whether there is an fsys mounted here */
383 uchar fsysscore[VtScoreSize]; /* score of fsys image on venti */
384 char *fsysimage; /* raw disk image */
385 Fsys *mfsys; /* mounted file system (nil until walked) */
386 Nfs3Handle mfsyshandle; /* handle to root of mounted fsys */
388 int mark; /* gc */
389 };
391 static uint
392 dumbhash(uchar *s)
394 return (s[0]<<2)|(s[1]>>6); /* first 10 bits */
397 static Cnode*
398 mkcnode(Ctree *t, Cnode *parent, char *elem, uint elen, char *path, uint plen)
400 uint h;
401 Cnode *n;
403 n = emalloc(sizeof *n + elen+1);
404 n->name = (char*)(n+1);
405 memmove(n->name, elem, elen);
406 n->name[elen] = 0;
407 n->parent = parent;
408 if(parent){
409 n->nextsib = parent->kidlist;
410 parent->kidlist = n;
412 n->kidlist = nil;
413 sha1((uchar*)path, plen, n->handle, nil);
414 h = dumbhash(n->handle);
415 n->nexthash = t->hash[h];
416 t->hash[h] = n;
418 return n;
421 void
422 markctree(Ctree *t)
424 int i;
425 Cnode *n;
427 for(i=0; i<nelem(t->hash); i++)
428 for(n=t->hash[i]; n; n=n->nexthash)
429 if(n->name[0] != '+')
430 n->mark = 1;
433 int
434 refreshdisk(void)
436 int i;
437 Cnode *n;
438 Ctree *t;
440 t = config.ctree;
441 for(i=0; i<nelem(t->hash); i++)
442 for(n=t->hash[i]; n; n=n->nexthash){
443 if(n->mfsys)
444 disksync(n->mfsys->disk);
445 if(n->fsys)
446 disksync(n->fsys->disk);
448 return 0;
451 void
452 sweepctree(Ctree *t)
454 int i;
455 Cnode *n;
457 /* just zero all the garbage and leave it linked into the tree */
458 for(i=0; i<nelem(t->hash); i++){
459 for(n=t->hash[i]; n; n=n->nexthash){
460 if(!n->mark)
461 continue;
462 n->fsys = nil;
463 free(n->fsysimage);
464 n->fsysimage = nil;
465 memset(n->fsysscore, 0, sizeof n->fsysscore);
466 n->mfsys = nil;
467 n->ismtpt = 0;
468 memset(&n->fsyshandle, 0, sizeof n->fsyshandle);
469 memset(&n->mfsyshandle, 0, sizeof n->mfsyshandle);
474 static Cnode*
475 cnodewalk(Cnode *n, char *name, uint len, int markokay)
477 Cnode *nn;
479 for(nn=n->kidlist; nn; nn=nn->nextsib)
480 if(strncmp(nn->name, name, len) == 0 && nn->name[len] == 0)
481 if(!nn->mark || markokay)
482 return nn;
483 return nil;
486 Cnode*
487 ctreewalkpath(Ctree *t, char *name, ulong createmtime)
489 Cnode *n, *nn;
490 char *p, *nextp;
492 n = t->root;
493 p = name;
494 for(; *p; p=nextp){
495 n->mark = 0;
496 assert(*p == '/');
497 p++;
498 nextp = strchr(p, '/');
499 if(nextp == nil)
500 nextp = p+strlen(p);
501 if((nn = cnodewalk(n, p, nextp-p, 1)) == nil){
502 if(createmtime == 0)
503 return nil;
504 nn = mkcnode(t, n, p, nextp-p, name, nextp-name);
505 nn->mtime = createmtime;
507 if(nn->mark)
508 nn->mark = 0;
509 n = nn;
511 n->mark = 0;
512 return n;
515 Ctree*
516 mkctree(void)
518 Ctree *t;
520 t = emalloc(sizeof *t);
521 t->root = mkcnode(t, nil, "", 0, "", 0);
523 ctreewalkpath(t, "/+log", time(0))->read = logread;
524 ctreewalkpath(t, "/+refreshdisk", time(0))->read = refreshdiskread;
525 ctreewalkpath(t, "/+refreshconfig", time(0))->read = refreshconfigread;
527 return t;
530 Cnode*
531 ctreemountfsys(Ctree *t, char *path, ulong time, uchar *score, char *file)
533 Cnode *n;
535 if(time == 0)
536 time = 1;
537 n = ctreewalkpath(t, path, time);
538 if(score){
539 if(n->ismtpt && (n->fsysimage || memcmp(n->fsysscore, score, VtScoreSize) != 0)){
540 free(n->fsysimage);
541 n->fsysimage = nil;
542 n->fsys = nil; /* leak (might be other refs) */
544 memmove(n->fsysscore, score, VtScoreSize);
545 }else{
546 if(n->ismtpt && (n->fsysimage==nil || strcmp(n->fsysimage, file) != 0)){
547 free(n->fsysimage);
548 n->fsysimage = nil;
549 n->fsys = nil; /* leak (might be other refs) */
551 n->fsysimage = emalloc(strlen(file)+1);
552 strcpy(n->fsysimage, file);
554 n->ismtpt = 1;
555 return n;
558 Cnode*
559 cnodebyhandle(Ctree *t, uchar *p)
561 int h;
562 Cnode *n;
564 h = dumbhash(p);
565 for(n=t->hash[h]; n; n=n->nexthash)
566 if(memcmp(n->handle, p, CnodeHandleSize) == 0)
567 return n;
568 return nil;
571 static int
572 parseipandmask(char *s, uchar *ip, uchar *mask)
574 char *p, *q;
576 p = strchr(s, '/');
577 if(p)
578 *p++ = 0;
579 if(parseip(ip, s) == ~0UL)
580 return -1;
581 if(p == nil)
582 memset(mask, 0xFF, IPaddrlen);
583 else{
584 if(isdigit((uchar)*p) && strtol(p, &q, 10)>=0 && *q==0)
585 *--p = '/';
586 if(parseipmask(mask, p) == ~0UL)
587 return -1;
588 if(*p != '/')
589 *--p = '/';
591 //fprint(2, "parseipandmask %s => %I %I\n", s, ip, mask);
592 return 0;
595 static int
596 parsetime(char *s, ulong *time)
598 ulong x;
599 char *p;
600 int i;
601 Tm tm;
603 /* decimal integer is seconds since 1970 */
604 x = strtoul(s, &p, 10);
605 if(x > 0 && *p == 0){
606 *time = x;
607 return 0;
610 /* otherwise expect yyyy/mmdd/hhmm */
611 if(strlen(s) != 14 || s[4] != '/' || s[9] != '/')
612 return -1;
613 for(i=0; i<4; i++)
614 if(!isdigit((uchar)s[i]) || !isdigit((uchar)s[i+5]) || !isdigit((uchar)s[i+10]))
615 return -1;
616 memset(&tm, 0, sizeof tm);
617 tm.year = atoi(s)-1900;
618 if(tm.year < 0 || tm.year > 200)
619 return -1;
620 tm.mon = (s[5]-'0')*10+s[6]-'0' - 1;
621 if(tm.mon < 0 || tm.mon > 11)
622 return -1;
623 tm.mday = (s[7]-'0')*10+s[8]-'0';
624 if(tm.mday < 0 || tm.mday > 31)
625 return -1;
626 tm.hour = (s[10]-'0')*10+s[11]-'0';
627 if(tm.hour < 0 || tm.hour > 23)
628 return -1;
629 tm.min = (s[12]-'0')*10+s[13]-'0';
630 if(tm.min < 0 || tm.min > 59)
631 return -1;
632 strcpy(tm.zone, "XXX"); /* anything but GMT */
633 if(0){
634 print("tm2sec %d/%d/%d/%d/%d\n",
635 tm.year, tm.mon, tm.mday, tm.hour, tm.min);
637 *time = tm2sec(&tm);
638 if(0) print("time %lud\n", *time);
639 return 0;
643 int
644 readconfigfile(Config *cp)
646 char *f[10], *image, *p, *pref, *q, *name;
647 int nf, line;
648 uchar scorebuf[VtScoreSize], *score;
649 ulong time;
650 Biobuf *b;
651 Config c;
652 Dir *dir;
654 name = configfile;
655 c = *cp;
656 if((dir = dirstat(name)) == nil)
657 return -1;
658 if(c.mtime == dir->mtime){
659 free(dir);
660 return 0;
662 c.mtime = dir->mtime;
663 free(dir);
664 if((b = Bopen(name, OREAD)) == nil){
665 free(dir);
666 return -1;
669 /*
670 * Reuse old tree, garbage collecting entries that
671 * are not mentioned in the new config file.
672 */
673 if(c.ctree == nil)
674 c.ctree = mkctree();
676 markctree(c.ctree);
677 c.ok = nil;
678 c.nok = 0;
680 line = 0;
681 for(; (p=Brdstr(b, '\n', 1)) != nil; free(p)){
682 line++;
683 if((q = strchr(p, '#')) != nil)
684 *q = 0;
685 nf = tokenize(p, f, nelem(f));
686 if(nf == 0)
687 continue;
688 if(strcmp(f[0], "mount") == 0){
689 if(nf != 4){
690 werrstr("syntax error: mount /path /dev|score mtime");
691 goto badline;
693 if(f[1][0] != '/'){
694 werrstr("unrooted path %s", f[1]);
695 goto badline;
697 score = nil;
698 image = nil;
699 if(f[2][0] == '/'){
700 if(access(f[2], AEXIST) < 0){
701 werrstr("image %s does not exist", f[2]);
702 goto badline;
704 image = f[2];
705 }else{
706 if(vtparsescore(f[2], &pref, scorebuf) < 0){
707 werrstr("bad score %s", f[2]);
708 goto badline;
710 score = scorebuf;
712 if(parsetime(f[3], &time) < 0){
713 fprint(2, "%s:%d: bad time %s\n", name, line, f[3]);
714 time = 1;
716 ctreemountfsys(c.ctree, f[1], time, score, image);
717 continue;
719 if(strcmp(f[0], "allow") == 0 || strcmp(f[0], "deny") == 0){
720 if(nf != 2){
721 werrstr("syntax error: allow|deny ip[/mask]");
722 goto badline;
724 c.ok = erealloc(c.ok, (c.nok+1)*sizeof(c.ok[0]));
725 if(parseipandmask(f[1], c.ok[c.nok].ip, c.ok[c.nok].mask) < 0){
726 werrstr("bad ip[/mask]: %s", f[1]);
727 goto badline;
729 c.ok[c.nok].okay = (strcmp(f[0], "allow") == 0);
730 c.nok++;
731 continue;
733 werrstr("unknown verb '%s'", f[0]);
734 badline:
735 fprint(2, "%s:%d: %r\n", name, line);
737 Bterm(b);
739 sweepctree(c.ctree);
740 free(cp->ok);
741 *cp = c;
742 return 0;
745 int
746 ipokay(uchar *ip, ushort port)
748 int i;
749 uchar ipx[IPaddrlen];
750 Ipokay *ok;
752 for(i=0; i<config.nok; i++){
753 ok = &config.ok[i];
754 maskip(ip, ok->mask, ipx);
755 if(0) fprint(2, "%I & %I = %I (== %I?)\n",
756 ip, ok->mask, ipx, ok->ip);
757 if(memcmp(ipx, ok->ip, IPaddrlen) == 0)
758 return ok->okay;
760 if(config.nok == 0) /* all is permitted */
761 return 1;
762 /* otherwise default is none allowed */
763 return 0;
766 Nfs3Status
767 cnodelookup(Ctree *t, Cnode **np, char *name)
769 Cnode *n, *nn;
771 n = *np;
772 if(n->isblackhole)
773 return Nfs3Ok;
774 if((nn = cnodewalk(n, name, strlen(name), 0)) == nil){
775 if(n->ismtpt || n->fsys){
776 if((nn = cnodewalk(n, "", 0, 1)) == nil){
777 nn = mkcnode(t, n, "", 0, (char*)n->handle, SHA1dlen);
778 nn->isblackhole = 1;
780 nn->mark = 0;
783 if(nn == nil)
784 return Nfs3ErrNoEnt;
785 *np = nn;
786 return Nfs3Ok;
789 Nfs3Status
790 cnodegetattr(Cnode *n, Nfs3Attr *attr)
792 memset(attr, 0, sizeof *attr);
793 if(n->read){
794 attr->type = Nfs3FileReg;
795 attr->mode = 0444;
796 attr->size = 512;
797 attr->nlink = 1;
798 }else{
799 attr->type = Nfs3FileDir;
800 attr->mode = 0555;
801 attr->size = 1024;
802 attr->nlink = 10;
804 attr->fileid = *(u64int*)n->handle;
805 attr->atime.sec = n->mtime;
806 attr->mtime.sec = n->mtime;
807 attr->ctime.sec = n->mtime;
808 return Nfs3Ok;
811 Nfs3Status
812 cnodereaddir(Cnode *n, u32int count, u64int cookie, uchar **pdata, u32int *pcount, u1int *peof)
814 uchar *data, *p, *ep, *np;
815 u64int c;
816 Nfs3Entry ne;
818 n = n->kidlist;
819 c = cookie;
820 for(; c && n; c--)
821 n = n->nextsib;
822 if(n == nil){
823 *pdata = 0;
824 *pcount = 0;
825 *peof = 1;
826 return Nfs3Ok;
829 data = emalloc(count);
830 p = data;
831 ep = data+count;
832 while(n && p < ep){
833 if(n->mark || n->name[0] == '+'){
834 n = n->nextsib;
835 ++cookie;
836 continue;
838 ne.name = n->name;
839 ne.cookie = ++cookie;
840 ne.fileid = *(u64int*)n->handle;
841 if(nfs3entrypack(p, ep, &np, &ne) < 0)
842 break;
843 p = np;
844 n = n->nextsib;
846 *pdata = data;
847 *pcount = p - data;
848 *peof = n==nil;
849 return Nfs3Ok;
852 void
853 timerproc(void *v)
855 for(;;){
856 sleep(60*1000);
857 sendp(timerchan, 0);
861 void
862 timerthread(void *v)
864 for(;;){
865 recvp(timerchan);
866 // refreshconfig();
870 /*
871 * Actually serve the NFS requests. Called from nfs3srv.c.
872 * Each request runs in its own thread (coroutine).
874 * Decrypted handles have the form:
876 * config[20] - SHA1 hash identifying a config tree node
877 * glob[10] - SHA1 hash prefix identifying a glob state
878 * fsyshandle[<=10] - disk file system handle (usually 4 bytes)
879 */
881 /*
882 * A fid represents a point in the file tree.
883 * There are three components, all derived from the handle:
885 * - config tree position (also used to find fsys)
886 * - glob state for exclusions
887 * - file system position
888 */
889 enum
891 HAccess,
892 HAttr,
893 HWalk,
894 HDotdot,
895 HRead
896 };
897 typedef struct Fid Fid;
898 struct Fid
900 Cnode *cnode;
901 Fsys *fsys;
902 Nfs3Handle fsyshandle;
903 };
905 int
906 handlecmp(Nfs3Handle *h, Nfs3Handle *h1)
908 if(h->len != h1->len)
909 return h->len - h1->len;
910 return memcmp(h->h, h1->h, h->len);
913 Nfs3Status
914 handletofid(Nfs3Handle *eh, Fid *fid, int mode)
916 int domount;
917 Cnode *n;
918 Disk *disk, *cdisk;
919 Fsys *fsys;
920 Nfs3Status ok;
921 Nfs3Handle h2, *h, *fh;
923 memset(fid, 0, sizeof *fid);
925 domount = 1;
926 if(mode == HDotdot)
927 domount = 0;
928 /*
929 * Not necessary, but speeds up ls -l /dump/2005
930 * HAttr and HAccess must be handled the same way
931 * because both can be used to fetch attributes.
932 * Acting differently yields inconsistencies at mount points,
933 * and causes FreeBSD ls -l to fail.
934 */
935 if(mode == HAttr || mode == HAccess)
936 domount = 0;
938 /*
939 * Decrypt handle.
940 */
941 h2 = *eh;
942 h = &h2;
943 if((ok = hdecrypt(h)) != Nfs3Ok)
944 return ok;
945 trace("handletofid: decrypted %.*lH\n", h->len, h->h);
946 if(h->len < FsysHandleOffset)
947 return Nfs3ErrBadHandle;
949 /*
950 * Find place in config tree.
951 */
952 if((n = cnodebyhandle(config.ctree, h->h)) == nil)
953 return Nfs3ErrStale;
954 fid->cnode = n;
956 if(n->ismtpt && domount){
957 /*
958 * Open fsys for mount point if needed.
959 */
960 if(n->mfsys == nil){
961 trace("handletofid: mounting %V/%s\n", n->fsysscore, n->fsysimage);
962 if(n->fsysimage){
963 if(strcmp(n->fsysimage, "/dev/null") == 0)
964 return Nfs3ErrAcces;
965 if((disk = diskopenfile(n->fsysimage)) == nil){
966 fprint(2, "cannot open disk %s: %r\n", n->fsysimage);
967 return Nfs3ErrIo;
969 if((cdisk = diskcache(disk, blocksize, 64)) == nil){
970 fprint(2, "cannot cache disk %s: %r\n", n->fsysimage);
971 diskclose(disk);
973 disk = cdisk;
974 }else{
975 if((disk = diskopenventi(vcache, n->fsysscore)) == nil){
976 fprint(2, "cannot open venti disk %V: %r\n", n->fsysscore);
977 return Nfs3ErrIo;
980 if((fsys = fsysopen(disk)) == nil){
981 fprint(2, "cannot open fsys on %V: %r\n", n->fsysscore);
982 diskclose(disk);
983 return Nfs3ErrIo;
985 n->mfsys = fsys;
986 fsysroot(fsys, &n->mfsyshandle);
989 /*
990 * Use inner handle.
991 */
992 fid->fsys = n->mfsys;
993 fid->fsyshandle = n->mfsyshandle;
994 }else{
995 /*
996 * Use fsys handle from tree or from handle.
997 * This assumes that fsyshandle was set by fidtohandle
998 * earlier, so it's not okay to reuse handles (except the root)
999 * across sessions. The encryption above makes and
1000 * enforces the same restriction, so this is okay.
1002 fid->fsys = n->fsys;
1003 fh = &fid->fsyshandle;
1004 if(n->isblackhole){
1005 fh->len = h->len-FsysHandleOffset;
1006 memmove(fh->h, h->h+FsysHandleOffset, fh->len);
1007 }else
1008 *fh = n->fsyshandle;
1009 trace("handletofid: fsyshandle %.*lH\n", fh->len, fh->h);
1013 * TO DO (maybe): some sort of path restriction here.
1015 trace("handletofid: cnode %s fsys %p fsyshandle %.*lH\n",
1016 n->name, fid->fsys, fid->fsyshandle.len, fid->fsyshandle.h);
1017 return Nfs3Ok;
1020 void
1021 _fidtohandle(Fid *fid, Nfs3Handle *h)
1023 Cnode *n;
1025 n = fid->cnode;
1027 * Record fsys handle in n, don't bother sending it to client
1028 * for black holes.
1030 n->fsys = fid->fsys;
1031 if(!n->isblackhole){
1032 n->fsyshandle = fid->fsyshandle;
1033 fid->fsyshandle.len = 0;
1035 memmove(h->h, n->handle, CnodeHandleSize);
1036 memmove(h->h+FsysHandleOffset, fid->fsyshandle.h, fid->fsyshandle.len);
1037 h->len = FsysHandleOffset+fid->fsyshandle.len;
1040 void
1041 fidtohandle(Fid *fid, Nfs3Handle *h)
1043 _fidtohandle(fid, h);
1044 hencrypt(h);
1047 void
1048 setrootfid(void)
1050 Fid fid;
1052 memset(&fid, 0, sizeof fid);
1053 fid.cnode = config.ctree->root;
1054 _fidtohandle(&fid, &root);
1055 fprint(2, "handle %.*lH\n", root.len, root.h);
1058 void
1059 fsgetroot(Nfs3Handle *h)
1061 *h = root;
1062 hencrypt(h);
1065 Nfs3Status
1066 fsgetattr(SunAuthUnix *au, Nfs3Handle *h, Nfs3Attr *attr)
1068 Fid fid;
1069 Nfs3Status ok;
1071 trace("getattr %.*lH\n", h->len, h->h);
1072 if((ok = handletofid(h, &fid, HAttr)) != Nfs3Ok)
1073 return ok;
1074 if(fid.fsys)
1075 return fsysgetattr(fid.fsys, au, &fid.fsyshandle, attr);
1076 else
1077 return cnodegetattr(fid.cnode, attr);
1081 * Lookup is always the hard part.
1083 Nfs3Status
1084 fslookup(SunAuthUnix *au, Nfs3Handle *h, char *name, Nfs3Handle *nh)
1086 Fid fid;
1087 Cnode *n;
1088 Nfs3Status ok;
1089 Nfs3Handle xh;
1090 int mode;
1092 trace("lookup %.*lH %s\n", h->len, h->h, name);
1094 mode = HWalk;
1095 if(strcmp(name, "..") == 0 || strcmp(name, ".") == 0)
1096 mode = HDotdot;
1097 if((ok = handletofid(h, &fid, mode)) != Nfs3Ok){
1098 nfs3errstr(ok);
1099 trace("lookup: handletofid %r\n");
1100 return ok;
1103 if(strcmp(name, ".") == 0){
1104 fidtohandle(&fid, nh);
1105 return Nfs3Ok;
1109 * Walk down file system and cnode simultaneously.
1110 * If dotdot and file system doesn't move, need to walk
1111 * up cnode. Save the corresponding fsys handles in
1112 * the cnode as we walk down so that we'll have them
1113 * for dotdotting back up.
1115 n = fid.cnode;
1116 if(mode == HWalk){
1118 * Walk down config tree and file system simultaneously.
1120 if((ok = cnodelookup(config.ctree, &n, name)) != Nfs3Ok){
1121 nfs3errstr(ok);
1122 trace("lookup: cnodelookup: %r\n");
1123 return ok;
1125 fid.cnode = n;
1126 if(fid.fsys){
1127 if((ok = fsyslookup(fid.fsys, au, &fid.fsyshandle, name, &xh)) != Nfs3Ok){
1128 nfs3errstr(ok);
1129 trace("lookup: fsyslookup: %r\n");
1130 return ok;
1132 fid.fsyshandle = xh;
1134 }else{
1136 * Walking dotdot. Ick.
1138 trace("lookup dotdot fsys=%p\n", fid.fsys);
1139 if(fid.fsys){
1141 * Walk up file system, then try up config tree.
1143 if((ok = fsyslookup(fid.fsys, au, &fid.fsyshandle, "..", &xh)) != Nfs3Ok){
1144 nfs3errstr(ok);
1145 trace("lookup fsyslookup: %r\n");
1146 return ok;
1148 fid.fsyshandle = xh;
1151 * Usually just go to n->parent.
1153 * If we're in a subtree of the mounted file system that
1154 * isn't represented explicitly by the config tree (instead
1155 * the black hole node represents the entire file tree),
1156 * then we only go to n->parent when we've dotdotted back
1157 * to the right handle.
1159 if(n->parent == nil)
1160 trace("lookup dotdot: no parent\n");
1161 else{
1162 trace("lookup dotdot: parent %.*lH, have %.*lH\n",
1163 n->parent->fsyshandle.len, n->parent->fsyshandle.h,
1164 xh.len, xh.h);
1167 if(n->isblackhole){
1168 if(handlecmp(&n->parent->mfsyshandle, &xh) == 0)
1169 n = n->parent;
1170 }else{
1171 if(n->parent)
1172 n = n->parent;
1174 }else{
1176 * No file system, just walk up.
1178 if(n->parent)
1179 n = n->parent;
1181 fid.fsys = n->fsys;
1182 fid.fsyshandle = n->fsyshandle;
1183 fid.cnode = n;
1185 fidtohandle(&fid, nh);
1186 return Nfs3Ok;
1189 Nfs3Status
1190 fsaccess(SunAuthUnix *au, Nfs3Handle *h, u32int want, u32int *got, Nfs3Attr *attr)
1192 Fid fid;
1193 Nfs3Status ok;
1195 trace("access %.*lH 0x%ux\n", h->len, h->h, want);
1196 if((ok = handletofid(h, &fid, HAccess)) != Nfs3Ok)
1197 return ok;
1198 if(fid.fsys)
1199 return fsysaccess(fid.fsys, au, &fid.fsyshandle, want, got, attr);
1200 *got = want & (Nfs3AccessRead|Nfs3AccessLookup|Nfs3AccessExecute);
1201 return cnodegetattr(fid.cnode, attr);
1204 Nfs3Status
1205 fsreadlink(SunAuthUnix *au, Nfs3Handle *h, char **link)
1207 Fid fid;
1208 Nfs3Status ok;
1210 trace("readlink %.*lH\n", h->len, h->h);
1211 if((ok = handletofid(h, &fid, HRead)) != Nfs3Ok)
1212 return ok;
1213 if(fid.fsys)
1214 return fsysreadlink(fid.fsys, au, &fid.fsyshandle, link);
1215 *link = 0;
1216 return Nfs3ErrNotSupp;
1219 Nfs3Status
1220 fsreadfile(SunAuthUnix *au, Nfs3Handle *h, u32int count, u64int offset, uchar **data, u32int *pcount, u1int *peof)
1222 Fid fid;
1223 Nfs3Status ok;
1225 trace("readfile %.*lH\n", h->len, h->h);
1226 if((ok = handletofid(h, &fid, HRead)) != Nfs3Ok)
1227 return ok;
1228 if(fid.cnode->read)
1229 return fid.cnode->read(fid.cnode, count, offset, data, pcount, peof);
1230 if(fid.fsys)
1231 return fsysreadfile(fid.fsys, au, &fid.fsyshandle, count, offset, data, pcount, peof);
1232 return Nfs3ErrNotSupp;
1235 Nfs3Status
1236 fsreaddir(SunAuthUnix *au, Nfs3Handle *h, u32int len, u64int cookie, uchar **pdata, u32int *pcount, u1int *peof)
1238 Fid fid;
1239 Nfs3Status ok;
1241 trace("readdir %.*lH\n", h->len, h->h);
1242 if((ok = handletofid(h, &fid, HRead)) != Nfs3Ok)
1243 return ok;
1244 if(fid.fsys)
1245 return fsysreaddir(fid.fsys, au, &fid.fsyshandle, len, cookie, pdata, pcount, peof);
1246 return cnodereaddir(fid.cnode, len, cookie, pdata, pcount, peof);
1249 Nfs3Status
1250 logread(Cnode *n, u32int count, u64int offset, uchar **data, u32int *pcount, u1int *peof)
1252 *pcount = 0;
1253 *peof = 1;
1254 return Nfs3Ok;
1257 Nfs3Status
1258 refreshdiskread(Cnode *n, u32int count, u64int offset, uchar **data, u32int *pcount, u1int *peof)
1260 char buf[128];
1262 if(offset != 0){
1263 *pcount = 0;
1264 *peof = 1;
1265 return Nfs3Ok;
1267 if(refreshdisk() < 0)
1268 snprint(buf, sizeof buf, "refreshdisk: %r\n");
1269 else
1270 strcpy(buf, "ok\n");
1271 *data = emalloc(strlen(buf));
1272 strcpy((char*)*data, buf);
1273 *pcount = strlen(buf);
1274 *peof = 1;
1275 return Nfs3Ok;
1278 Nfs3Status
1279 refreshconfigread(Cnode *n, u32int count, u64int offset, uchar **data, u32int *pcount, u1int *peof)
1281 char buf[128];
1283 if(offset != 0){
1284 *pcount = 0;
1285 *peof = 1;
1286 return Nfs3Ok;
1288 if(readconfigfile(&config) < 0)
1289 snprint(buf, sizeof buf, "readconfig: %r\n");
1290 else
1291 strcpy(buf, "ok\n");
1292 *data = emalloc(strlen(buf));
1293 strcpy((char*)*data, buf);
1294 *pcount = strlen(buf);
1295 *peof = 1;
1296 return Nfs3Ok;