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 void
73 usage(void)
74 {
75 fprint(2, "usage: vnfs [-LLRVr] [-a addr] [-b blocksize] [-c cachesize] configfile\n");
76 threadexitsall("usage");
77 }
79 void
80 threadmain(int argc, char **argv)
81 {
82 fmtinstall('B', sunrpcfmt);
83 fmtinstall('C', suncallfmt);
84 fmtinstall('F', vtfcallfmt);
85 fmtinstall('H', encodefmt);
86 fmtinstall('I', eipfmt);
87 fmtinstall('V', vtscorefmt);
88 sunfmtinstall(&nfs3prog);
89 sunfmtinstall(&nfsmount3prog);
91 addr = "udp!*!2049";
92 blocksize = 8192;
93 cachesize = 400;
94 srv = sunsrv();
95 srv->ipokay = ipokay;
96 cryptinit();
98 ARGBEGIN{
99 default:
100 usage();
101 case 'E':
102 encryptedhandles = 0;
103 break;
104 case 'L':
105 if(srv->localonly == 0)
106 srv->localonly = 1;
107 else
108 srv->localparanoia = 1;
109 break;
110 case 'R':
111 srv->chatty++;
112 break;
113 case 'T':
114 tracecalls = 1;
115 break;
116 case 'V':
117 chattyventi = 1;
118 break;
119 case 'a':
120 addr = EARGF(usage());
121 break;
122 case 'b':
123 blocksize = strtoull(EARGF(usage()), 0, 0);
124 break;
125 case 'c':
126 cachesize = strtoull(EARGF(usage()), 0, 0);
127 break;
128 case 'r':
129 srv->alwaysreject++;
130 break;
131 }ARGEND
133 if(argc != 1)
134 usage();
136 if((z = vtdial(nil)) == nil)
137 sysfatal("vtdial: %r");
138 if(vtconnect(z) < 0)
139 sysfatal("vtconnect: %r");
140 if((vcache = vtcachealloc(z, blocksize, cachesize)) == nil)
141 sysfatal("vtcache: %r");
143 configfile = argv[0];
144 if(readconfigfile(&config) < 0)
145 sysfatal("readConfig: %r");
146 setrootfid();
148 nfschan = chancreate(sizeof(SunMsg*), 0);
149 mountchan = chancreate(sizeof(SunMsg*), 0);
150 timerchan = chancreate(sizeof(void*), 0);
152 if(sunsrvudp(srv, addr) < 0)
153 sysfatal("starting server: %r");
155 sunsrvthreadcreate(srv, nfs3proc, nfschan);
156 sunsrvthreadcreate(srv, mount3proc, mountchan);
157 sunsrvthreadcreate(srv, timerthread, nil);
158 proccreate(timerproc, nil, 32768);
160 sunsrvprog(srv, &nfs3prog, nfschan);
161 sunsrvprog(srv, &nfsmount3prog, mountchan);
163 threadexits(nil);
166 /*
167 * Handles.
169 * We store all the state about which file a client is accessing in
170 * the handle, so that we don't have to maintain any per-client state
171 * ourselves. In order to avoid leaking handles or letting clients
172 * create arbitrary handles, we sign and encrypt each handle with
173 * AES using a key selected randomly when the server starts.
174 * Thus, handles cannot be used across sessions.
176 * The decrypted handles begin with the following header:
178 * rand[12] random bytes used to make encryption non-deterministic
179 * len[4] length of handle that follows
180 * sessid[8] random session id chosen at start time
182 * If we're pressed for space in the rest of the handle, we could
183 * probably reduce the amount of randomness.
185 * Security woes aside, the fact that we have to shove everything
186 * into the handles is quite annoying. We have to encode, in 40 bytes:
188 * - position in the synthesized config tree
189 * - enough of the path to do glob matching
190 * - position in an archived file system image
192 * and the handles need to be stable across changes in the config file
193 * (though not across server restarts since encryption screws
194 * that up nicely).
196 * We encode each of the first two as a 10-byte hash that is
197 * the first half of a SHA1 hash.
198 */
200 enum
202 RandSize = 16,
203 SessidSize = 8,
204 HeaderSize = RandSize+SessidSize,
205 MaxHandleSize = Nfs3MaxHandleSize - HeaderSize,
206 };
208 AESstate aesstate;
209 uchar sessid[SessidSize];
211 static void
212 hencrypt(Nfs3Handle *h)
214 uchar *p;
215 AESstate aes;
217 /*
218 * root handle has special encryption - a single 0 byte - so that it
219 * never goes stale.
220 */
221 if(h->len == root.len && memcmp(h->h, root.h, root.len) == 0){
222 h->h[0] = 0;
223 h->len = 1;
224 return;
227 if(!encryptedhandles)
228 return;
230 if(h->len > MaxHandleSize){
231 /* oops */
232 fprint(2, "handle too long: %.*lH\n", h->len, h->h);
233 memset(h->h, 'X', Nfs3MaxHandleSize);
234 h->len = Nfs3MaxHandleSize;
235 return;
238 p = h->h;
239 memmove(p+HeaderSize, p, h->len);
240 *(u32int*)p = fastrand();
241 *(u32int*)(p+4) = fastrand();
242 *(u32int*)(p+8) = fastrand();
243 *(u32int*)(p+12) = h->len;
244 memmove(p+16, sessid, SessidSize);
245 h->len += HeaderSize;
247 if(encryptedhandles){
248 while(h->len < MaxHandleSize)
249 h->h[h->len++] = fastrand();
250 aes = aesstate;
251 aesCBCencrypt(h->h, MaxHandleSize, &aes);
255 static Nfs3Status
256 hdecrypt(Nfs3Handle *h)
258 AESstate aes;
260 if(h->len == 1 && h->h[0] == 0){ /* single 0 byte is root */
261 *h = root;
262 return Nfs3Ok;
265 if(!encryptedhandles)
266 return Nfs3Ok;
268 if(h->len <= HeaderSize)
269 return Nfs3ErrBadHandle;
270 if(encryptedhandles){
271 if(h->len != MaxHandleSize)
272 return Nfs3ErrBadHandle;
273 aes = aesstate;
274 aesCBCdecrypt(h->h, h->len, &aes);
276 if(memcmp(h->h+RandSize, sessid, sizeof sessid) != 0)
277 return Nfs3ErrStale; /* give benefit of doubt */
278 h->len = *(u32int*)(h->h+12); /* XXX byte order */
279 memmove(h->h, h->h+HeaderSize, h->len);
280 return Nfs3Ok;
283 void
284 cryptinit(void)
286 uchar key[32], ivec[AESbsize];
287 int i;
289 *(u32int*)sessid = truerand();
290 for(i=0; i<nelem(key); i+=4)
291 *(u32int*)&key[i] = truerand();
292 for(i=0; i<nelem(ivec); i++)
293 ivec[i] = fastrand();
294 setupAESstate(&aesstate, key, sizeof key, ivec);
297 /*
298 * Config file.
300 * The main purpose of the configuration file is to define a tree
301 * in which the archived file system images are mounted.
302 * The tree is stored as Entry structures, defined below.
304 * The configuration file also allows one to define shell-like
305 * glob expressions matching paths that are not to be displayed.
306 * The matched files or directories are shown in directory listings
307 * (could suppress these if we cared) but they cannot be opened,
308 * read, or written, and getattr returns zeroed data.
309 */
310 enum
312 /* sizes used in handles; see nfs server below */
313 CnodeHandleSize = 8,
314 FsysHandleOffset = CnodeHandleSize,
315 };
317 /*
318 * Config file tree.
319 */
320 struct Ctree
322 Cnode *root;
323 Cnode *hash[1024];
324 };
326 struct Cnode
328 char *name; /* path element */
329 Cnode *parent; /* in tree */
330 Cnode *nextsib; /* in tree */
331 Cnode *kidlist; /* in tree */
332 Cnode *nexthash; /* in hash list */
334 Nfs3Status (*read)(Cnode*, u32int, u64int, uchar**, u32int*, u1int*); /* synthesized read fn */
336 uchar handle[VtScoreSize]; /* sha1(path to here) */
337 ulong mtime; /* mtime for this directory entry */
339 /* fsys overlay on this node */
340 Fsys *fsys; /* cache of memory structure */
341 Nfs3Handle fsyshandle;
342 int isblackhole; /* walking down keeps you here */
344 /*
345 * mount point info.
346 * if a mount point is inside another file system,
347 * the fsys and fsyshandle above have the old fs info,
348 * the mfsys and mfsyshandle below have the new one.
349 * getattrs must use the old info for consistency.
350 */
351 int ismtpt; /* whether there is an fsys mounted here */
352 uchar fsysscore[VtScoreSize]; /* score of fsys image on venti */
353 char *fsysimage; /* raw disk image */
354 Fsys *mfsys; /* mounted file system (nil until walked) */
355 Nfs3Handle mfsyshandle; /* handle to root of mounted fsys */
357 int mark; /* gc */
358 };
360 static uint
361 dumbhash(uchar *s)
363 return (s[0]<<2)|(s[1]>>6); /* first 10 bits */
366 static Cnode*
367 mkcnode(Ctree *t, Cnode *parent, char *elem, uint elen, char *path, uint plen)
369 uint h;
370 Cnode *n;
372 n = emalloc(sizeof *n + elen+1);
373 n->name = (char*)(n+1);
374 memmove(n->name, elem, elen);
375 n->name[elen] = 0;
376 n->parent = parent;
377 if(parent){
378 n->nextsib = parent->kidlist;
379 parent->kidlist = n;
381 n->kidlist = nil;
382 sha1((uchar*)path, plen, n->handle, nil);
383 h = dumbhash(n->handle);
384 n->nexthash = t->hash[h];
385 t->hash[h] = n;
387 return n;
390 void
391 markctree(Ctree *t)
393 int i;
394 Cnode *n;
396 for(i=0; i<nelem(t->hash); i++)
397 for(n=t->hash[i]; n; n=n->nexthash)
398 if(n->name[0] != '+')
399 n->mark = 1;
402 int
403 refreshdisk(void)
405 int i;
406 Cnode *n;
407 Ctree *t;
409 t = config.ctree;
410 for(i=0; i<nelem(t->hash); i++)
411 for(n=t->hash[i]; n; n=n->nexthash){
412 if(n->mfsys)
413 disksync(n->mfsys->disk);
414 if(n->fsys)
415 disksync(n->fsys->disk);
417 return 0;
420 void
421 sweepctree(Ctree *t)
423 int i;
424 Cnode *n;
426 /* just zero all the garbage and leave it linked into the tree */
427 for(i=0; i<nelem(t->hash); i++){
428 for(n=t->hash[i]; n; n=n->nexthash){
429 if(!n->mark)
430 continue;
431 n->fsys = nil;
432 free(n->fsysimage);
433 n->fsysimage = nil;
434 memset(n->fsysscore, 0, sizeof n->fsysscore);
435 n->mfsys = nil;
436 n->ismtpt = 0;
437 memset(&n->fsyshandle, 0, sizeof n->fsyshandle);
438 memset(&n->mfsyshandle, 0, sizeof n->mfsyshandle);
443 static Cnode*
444 cnodewalk(Cnode *n, char *name, uint len, int markokay)
446 Cnode *nn;
448 for(nn=n->kidlist; nn; nn=nn->nextsib)
449 if(strncmp(nn->name, name, len) == 0 && nn->name[len] == 0)
450 if(!nn->mark || markokay)
451 return nn;
452 return nil;
455 Cnode*
456 ctreewalkpath(Ctree *t, char *name, ulong createmtime)
458 Cnode *n, *nn;
459 char *p, *nextp;
461 n = t->root;
462 p = name;
463 for(; *p; p=nextp){
464 n->mark = 0;
465 assert(*p == '/');
466 p++;
467 nextp = strchr(p, '/');
468 if(nextp == nil)
469 nextp = p+strlen(p);
470 if((nn = cnodewalk(n, p, nextp-p, 1)) == nil){
471 if(createmtime == 0)
472 return nil;
473 nn = mkcnode(t, n, p, nextp-p, name, nextp-name);
474 nn->mtime = createmtime;
476 if(nn->mark)
477 nn->mark = 0;
478 n = nn;
480 n->mark = 0;
481 return n;
484 Ctree*
485 mkctree(void)
487 Ctree *t;
489 t = emalloc(sizeof *t);
490 t->root = mkcnode(t, nil, "", 0, "", 0);
492 ctreewalkpath(t, "/+log", time(0))->read = logread;
493 ctreewalkpath(t, "/+refreshdisk", time(0))->read = refreshdiskread;
494 ctreewalkpath(t, "/+refreshconfig", time(0))->read = refreshconfigread;
496 return t;
499 Cnode*
500 ctreemountfsys(Ctree *t, char *path, ulong time, uchar *score, char *file)
502 Cnode *n;
504 if(time == 0)
505 time = 1;
506 n = ctreewalkpath(t, path, time);
507 if(score){
508 if(n->ismtpt && (n->fsysimage || memcmp(n->fsysscore, score, VtScoreSize) != 0)){
509 free(n->fsysimage);
510 n->fsysimage = nil;
511 n->fsys = nil; /* leak (might be other refs) */
513 memmove(n->fsysscore, score, VtScoreSize);
514 }else{
515 if(n->ismtpt && (n->fsysimage==nil || strcmp(n->fsysimage, file) != 0)){
516 free(n->fsysimage);
517 n->fsysimage = nil;
518 n->fsys = nil; /* leak (might be other refs) */
520 n->fsysimage = emalloc(strlen(file)+1);
521 strcpy(n->fsysimage, file);
523 n->ismtpt = 1;
524 return n;
527 Cnode*
528 cnodebyhandle(Ctree *t, uchar *p)
530 int h;
531 Cnode *n;
533 h = dumbhash(p);
534 for(n=t->hash[h]; n; n=n->nexthash)
535 if(memcmp(n->handle, p, CnodeHandleSize) == 0)
536 return n;
537 return nil;
540 static int
541 parseipandmask(char *s, uchar *ip, uchar *mask)
543 char *p, *q;
545 p = strchr(s, '/');
546 if(p)
547 *p++ = 0;
548 if(parseip(ip, s) == ~0UL)
549 return -1;
550 if(p == nil)
551 memset(mask, 0xFF, IPaddrlen);
552 else{
553 if(isdigit(*p) && strtol(p, &q, 10)>=0 && *q==0)
554 *--p = '/';
555 if(parseipmask(mask, p) == ~0UL)
556 return -1;
557 if(*p != '/')
558 *--p = '/';
560 //fprint(2, "parseipandmask %s => %I %I\n", s, ip, mask);
561 return 0;
564 static int
565 parsetime(char *s, ulong *time)
567 ulong x;
568 char *p;
569 int i;
570 Tm tm;
572 /* decimal integer is seconds since 1970 */
573 x = strtoul(s, &p, 10);
574 if(x > 0 && *p == 0){
575 *time = x;
576 return 0;
579 /* otherwise expect yyyy/mmdd/hhmm */
580 if(strlen(s) != 14 || s[4] != '/' || s[9] != '/')
581 return -1;
582 for(i=0; i<4; i++)
583 if(!isdigit(s[i]) || !isdigit(s[i+5]) || !isdigit(s[i+10]))
584 return -1;
585 memset(&tm, 0, sizeof tm);
586 tm.year = atoi(s)-1900;
587 if(tm.year < 0 || tm.year > 200)
588 return -1;
589 tm.mon = (s[5]-'0')*10+s[6]-'0' - 1;
590 if(tm.mon < 0 || tm.mon > 11)
591 return -1;
592 tm.mday = (s[7]-'0')*10+s[8]-'0';
593 if(tm.mday < 0 || tm.mday > 31)
594 return -1;
595 tm.hour = (s[10]-'0')*10+s[11]-'0';
596 if(tm.hour < 0 || tm.hour > 23)
597 return -1;
598 tm.min = (s[12]-'0')*10+s[13]-'0';
599 if(tm.min < 0 || tm.min > 59)
600 return -1;
601 strcpy(tm.zone, "XXX"); /* anything but GMT */
602 if(0){
603 print("tm2sec %d/%d/%d/%d/%d\n",
604 tm.year, tm.mon, tm.mday, tm.hour, tm.min);
606 *time = tm2sec(&tm);
607 if(0) print("time %lud\n", *time);
608 return 0;
612 int
613 readconfigfile(Config *cp)
615 char *f[10], *image, *p, *pref, *q, *name;
616 int nf, line;
617 uchar scorebuf[VtScoreSize], *score;
618 ulong time;
619 Biobuf *b;
620 Config c;
621 Dir *dir;
623 name = configfile;
624 c = *cp;
625 if((dir = dirstat(name)) == nil)
626 return -1;
627 if(c.mtime == dir->mtime){
628 free(dir);
629 return 0;
631 c.mtime = dir->mtime;
632 free(dir);
633 if((b = Bopen(name, OREAD)) == nil){
634 free(dir);
635 return -1;
638 /*
639 * Reuse old tree, garbage collecting entries that
640 * are not mentioned in the new config file.
641 */
642 if(c.ctree == nil)
643 c.ctree = mkctree();
645 markctree(c.ctree);
646 c.ok = nil;
647 c.nok = 0;
649 line = 0;
650 for(; (p=Brdstr(b, '\n', 1)) != nil; free(p)){
651 line++;
652 if((q = strchr(p, '#')) != nil)
653 *q = 0;
654 nf = tokenize(p, f, nelem(f));
655 if(nf == 0)
656 continue;
657 if(strcmp(f[0], "mount") == 0){
658 if(nf != 4){
659 werrstr("syntax error: mount /path /dev|score mtime");
660 goto badline;
662 if(f[1][0] != '/'){
663 werrstr("unrooted path %s", f[1]);
664 goto badline;
666 score = nil;
667 image = nil;
668 if(f[2][0] == '/'){
669 if(access(f[2], AEXIST) < 0){
670 werrstr("image %s does not exist", f[2]);
671 goto badline;
673 image = f[2];
674 }else{
675 if(vtparsescore(f[2], &pref, scorebuf) < 0){
676 werrstr("bad score %s", f[2]);
677 goto badline;
679 score = scorebuf;
681 if(parsetime(f[3], &time) < 0){
682 fprint(2, "%s:%d: bad time %s\n", name, line, f[3]);
683 time = 1;
685 ctreemountfsys(c.ctree, f[1], time, score, image);
686 continue;
688 if(strcmp(f[0], "allow") == 0 || strcmp(f[0], "deny") == 0){
689 if(nf != 2){
690 werrstr("syntax error: allow|deny ip[/mask]");
691 goto badline;
693 c.ok = erealloc(c.ok, (c.nok+1)*sizeof(c.ok[0]));
694 if(parseipandmask(f[1], c.ok[c.nok].ip, c.ok[c.nok].mask) < 0){
695 werrstr("bad ip[/mask]: %s", f[1]);
696 goto badline;
698 c.ok[c.nok].okay = (strcmp(f[0], "allow") == 0);
699 c.nok++;
700 continue;
702 werrstr("unknown verb '%s'", f[0]);
703 badline:
704 fprint(2, "%s:%d: %r\n", name, line);
706 Bterm(b);
708 sweepctree(c.ctree);
709 free(cp->ok);
710 *cp = c;
711 return 0;
714 int
715 ipokay(uchar *ip, ushort port)
717 int i;
718 uchar ipx[IPaddrlen];
719 Ipokay *ok;
721 for(i=0; i<config.nok; i++){
722 ok = &config.ok[i];
723 maskip(ip, ok->mask, ipx);
724 if(0) fprint(2, "%I & %I = %I (== %I?)\n",
725 ip, ok->mask, ipx, ok->ip);
726 if(memcmp(ipx, ok->ip, IPaddrlen) == 0)
727 return ok->okay;
729 if(config.nok == 0) /* all is permitted */
730 return 1;
731 /* otherwise default is none allowed */
732 return 0;
735 Nfs3Status
736 cnodelookup(Ctree *t, Cnode **np, char *name)
738 Cnode *n, *nn;
740 n = *np;
741 if(n->isblackhole)
742 return Nfs3Ok;
743 if((nn = cnodewalk(n, name, strlen(name), 0)) == nil){
744 if(n->ismtpt || n->fsys){
745 if((nn = cnodewalk(n, "", 0, 1)) == nil){
746 nn = mkcnode(t, n, "", 0, (char*)n->handle, SHA1dlen);
747 nn->isblackhole = 1;
749 nn->mark = 0;
752 if(nn == nil)
753 return Nfs3ErrNoEnt;
754 *np = nn;
755 return Nfs3Ok;
758 Nfs3Status
759 cnodegetattr(Cnode *n, Nfs3Attr *attr)
761 memset(attr, 0, sizeof *attr);
762 if(n->read){
763 attr->type = Nfs3FileReg;
764 attr->mode = 0444;
765 attr->size = 512;
766 attr->nlink = 1;
767 }else{
768 attr->type = Nfs3FileDir;
769 attr->mode = 0555;
770 attr->size = 1024;
771 attr->nlink = 10;
773 attr->fileid = *(u64int*)n->handle;
774 attr->atime.sec = n->mtime;
775 attr->mtime.sec = n->mtime;
776 attr->ctime.sec = n->mtime;
777 return Nfs3Ok;
780 Nfs3Status
781 cnodereaddir(Cnode *n, u32int count, u64int cookie, uchar **pdata, u32int *pcount, u1int *peof)
783 uchar *data, *p, *ep, *np;
784 u64int c;
785 Nfs3Entry ne;
787 n = n->kidlist;
788 c = cookie;
789 for(; c && n; c--)
790 n = n->nextsib;
791 if(n == nil){
792 *pdata = 0;
793 *pcount = 0;
794 *peof = 1;
795 return Nfs3Ok;
798 data = emalloc(count);
799 p = data;
800 ep = data+count;
801 while(n && p < ep){
802 if(n->mark || n->name[0] == '+'){
803 n = n->nextsib;
804 ++cookie;
805 continue;
807 ne.name = n->name;
808 ne.cookie = ++cookie;
809 ne.fileid = *(u64int*)n->handle;
810 if(nfs3entrypack(p, ep, &np, &ne) < 0)
811 break;
812 p = np;
813 n = n->nextsib;
815 *pdata = data;
816 *pcount = p - data;
817 *peof = n==nil;
818 return Nfs3Ok;
821 void
822 timerproc(void *v)
824 for(;;){
825 sleep(60*1000);
826 sendp(timerchan, 0);
830 void
831 timerthread(void *v)
833 for(;;){
834 recvp(timerchan);
835 // refreshconfig();
839 /*
840 * Actually serve the NFS requests. Called from nfs3srv.c.
841 * Each request runs in its own thread (coroutine).
843 * Decrypted handles have the form:
845 * config[20] - SHA1 hash identifying a config tree node
846 * glob[10] - SHA1 hash prefix identifying a glob state
847 * fsyshandle[<=10] - disk file system handle (usually 4 bytes)
848 */
850 /*
851 * A fid represents a point in the file tree.
852 * There are three components, all derived from the handle:
854 * - config tree position (also used to find fsys)
855 * - glob state for exclusions
856 * - file system position
857 */
858 enum
860 HAccess,
861 HAttr,
862 HWalk,
863 HDotdot,
864 HRead
865 };
866 typedef struct Fid Fid;
867 struct Fid
869 Cnode *cnode;
870 Fsys *fsys;
871 Nfs3Handle fsyshandle;
872 };
874 int
875 handlecmp(Nfs3Handle *h, Nfs3Handle *h1)
877 if(h->len != h1->len)
878 return h->len - h1->len;
879 return memcmp(h->h, h1->h, h->len);
882 Nfs3Status
883 handletofid(Nfs3Handle *eh, Fid *fid, int mode)
885 int domount;
886 Cnode *n;
887 Disk *disk, *cdisk;
888 Fsys *fsys;
889 Nfs3Status ok;
890 Nfs3Handle h2, *h, *fh;
892 memset(fid, 0, sizeof *fid);
894 domount = 1;
895 if(mode == HDotdot)
896 domount = 0;
897 /*
898 * Not necessary, but speeds up ls -l /dump/2005
899 * HAttr and HAccess must be handled the same way
900 * because both can be used to fetch attributes.
901 * Acting differently yields inconsistencies at mount points,
902 * and causes FreeBSD ls -l to fail.
903 */
904 if(mode == HAttr || mode == HAccess)
905 domount = 0;
907 /*
908 * Decrypt handle.
909 */
910 h2 = *eh;
911 h = &h2;
912 if((ok = hdecrypt(h)) != Nfs3Ok)
913 return ok;
914 trace("handletofid: decrypted %.*lH\n", h->len, h->h);
915 if(h->len < FsysHandleOffset)
916 return Nfs3ErrBadHandle;
918 /*
919 * Find place in config tree.
920 */
921 if((n = cnodebyhandle(config.ctree, h->h)) == nil)
922 return Nfs3ErrStale;
923 fid->cnode = n;
925 if(n->ismtpt && domount){
926 /*
927 * Open fsys for mount point if needed.
928 */
929 if(n->mfsys == nil){
930 trace("handletofid: mounting %V/%s\n", n->fsysscore, n->fsysimage);
931 if(n->fsysimage){
932 if(strcmp(n->fsysimage, "/dev/null") == 0)
933 return Nfs3ErrAcces;
934 if((disk = diskopenfile(n->fsysimage)) == nil){
935 fprint(2, "cannot open disk %s: %r\n", n->fsysimage);
936 return Nfs3ErrIo;
938 if((cdisk = diskcache(disk, blocksize, 64)) == nil){
939 fprint(2, "cannot cache disk %s: %r\n", n->fsysimage);
940 diskclose(disk);
942 disk = cdisk;
943 }else{
944 if((disk = diskopenventi(vcache, n->fsysscore)) == nil){
945 fprint(2, "cannot open venti disk %V: %r\n", n->fsysscore);
946 return Nfs3ErrIo;
949 if((fsys = fsysopen(disk)) == nil){
950 fprint(2, "cannot open fsys on %V: %r\n", n->fsysscore);
951 diskclose(disk);
952 return Nfs3ErrIo;
954 n->mfsys = fsys;
955 fsysroot(fsys, &n->mfsyshandle);
958 /*
959 * Use inner handle.
960 */
961 fid->fsys = n->mfsys;
962 fid->fsyshandle = n->mfsyshandle;
963 }else{
964 /*
965 * Use fsys handle from tree or from handle.
966 * This assumes that fsyshandle was set by fidtohandle
967 * earlier, so it's not okay to reuse handles (except the root)
968 * across sessions. The encryption above makes and
969 * enforces the same restriction, so this is okay.
970 */
971 fid->fsys = n->fsys;
972 fh = &fid->fsyshandle;
973 if(n->isblackhole){
974 fh->len = h->len-FsysHandleOffset;
975 memmove(fh->h, h->h+FsysHandleOffset, fh->len);
976 }else
977 *fh = n->fsyshandle;
978 trace("handletofid: fsyshandle %.*lH\n", fh->len, fh->h);
981 /*
982 * TO DO (maybe): some sort of path restriction here.
983 */
984 trace("handletofid: cnode %s fsys %p fsyshandle %.*lH\n",
985 n->name, fid->fsys, fid->fsyshandle.len, fid->fsyshandle.h);
986 return Nfs3Ok;
989 void
990 _fidtohandle(Fid *fid, Nfs3Handle *h)
992 Cnode *n;
994 n = fid->cnode;
995 /*
996 * Record fsys handle in n, don't bother sending it to client
997 * for black holes.
998 */
999 n->fsys = fid->fsys;
1000 if(!n->isblackhole){
1001 n->fsyshandle = fid->fsyshandle;
1002 fid->fsyshandle.len = 0;
1004 memmove(h->h, n->handle, CnodeHandleSize);
1005 memmove(h->h+FsysHandleOffset, fid->fsyshandle.h, fid->fsyshandle.len);
1006 h->len = FsysHandleOffset+fid->fsyshandle.len;
1009 void
1010 fidtohandle(Fid *fid, Nfs3Handle *h)
1012 _fidtohandle(fid, h);
1013 hencrypt(h);
1016 void
1017 setrootfid(void)
1019 Fid fid;
1021 memset(&fid, 0, sizeof fid);
1022 fid.cnode = config.ctree->root;
1023 _fidtohandle(&fid, &root);
1024 fprint(2, "handle %.*lH\n", root.len, root.h);
1027 void
1028 fsgetroot(Nfs3Handle *h)
1030 *h = root;
1031 hencrypt(h);
1034 Nfs3Status
1035 fsgetattr(SunAuthUnix *au, Nfs3Handle *h, Nfs3Attr *attr)
1037 Fid fid;
1038 Nfs3Status ok;
1040 trace("getattr %.*lH\n", h->len, h->h);
1041 if((ok = handletofid(h, &fid, HAttr)) != Nfs3Ok)
1042 return ok;
1043 if(fid.fsys)
1044 return fsysgetattr(fid.fsys, au, &fid.fsyshandle, attr);
1045 else
1046 return cnodegetattr(fid.cnode, attr);
1050 * Lookup is always the hard part.
1052 Nfs3Status
1053 fslookup(SunAuthUnix *au, Nfs3Handle *h, char *name, Nfs3Handle *nh)
1055 Fid fid;
1056 Cnode *n;
1057 Nfs3Status ok;
1058 Nfs3Handle xh;
1059 int mode;
1061 trace("lookup %.*lH %s\n", h->len, h->h, name);
1063 mode = HWalk;
1064 if(strcmp(name, "..") == 0 || strcmp(name, ".") == 0)
1065 mode = HDotdot;
1066 if((ok = handletofid(h, &fid, mode)) != Nfs3Ok){
1067 nfs3errstr(ok);
1068 trace("lookup: handletofid %r\n");
1069 return ok;
1072 if(strcmp(name, ".") == 0){
1073 fidtohandle(&fid, nh);
1074 return Nfs3Ok;
1078 * Walk down file system and cnode simultaneously.
1079 * If dotdot and file system doesn't move, need to walk
1080 * up cnode. Save the corresponding fsys handles in
1081 * the cnode as we walk down so that we'll have them
1082 * for dotdotting back up.
1084 n = fid.cnode;
1085 if(mode == HWalk){
1087 * Walk down config tree and file system simultaneously.
1089 if((ok = cnodelookup(config.ctree, &n, name)) != Nfs3Ok){
1090 nfs3errstr(ok);
1091 trace("lookup: cnodelookup: %r\n");
1092 return ok;
1094 fid.cnode = n;
1095 if(fid.fsys){
1096 if((ok = fsyslookup(fid.fsys, au, &fid.fsyshandle, name, &xh)) != Nfs3Ok){
1097 nfs3errstr(ok);
1098 trace("lookup: fsyslookup: %r\n");
1099 return ok;
1101 fid.fsyshandle = xh;
1103 }else{
1105 * Walking dotdot. Ick.
1107 trace("lookup dotdot fsys=%p\n", fid.fsys);
1108 if(fid.fsys){
1110 * Walk up file system, then try up config tree.
1112 if((ok = fsyslookup(fid.fsys, au, &fid.fsyshandle, "..", &xh)) != Nfs3Ok){
1113 nfs3errstr(ok);
1114 trace("lookup fsyslookup: %r\n");
1115 return ok;
1117 fid.fsyshandle = xh;
1120 * Usually just go to n->parent.
1122 * If we're in a subtree of the mounted file system that
1123 * isn't represented explicitly by the config tree (instead
1124 * the black hole node represents the entire file tree),
1125 * then we only go to n->parent when we've dotdotted back
1126 * to the right handle.
1128 if(n->parent == nil)
1129 trace("lookup dotdot: no parent\n");
1130 else{
1131 trace("lookup dotdot: parent %.*lH, have %.*lH\n",
1132 n->parent->fsyshandle.len, n->parent->fsyshandle.h,
1133 xh.len, xh.h);
1136 if(n->isblackhole){
1137 if(handlecmp(&n->parent->mfsyshandle, &xh) == 0)
1138 n = n->parent;
1139 }else{
1140 if(n->parent)
1141 n = n->parent;
1143 }else{
1145 * No file system, just walk up.
1147 if(n->parent)
1148 n = n->parent;
1150 fid.fsys = n->fsys;
1151 fid.fsyshandle = n->fsyshandle;
1152 fid.cnode = n;
1154 fidtohandle(&fid, nh);
1155 return Nfs3Ok;
1158 Nfs3Status
1159 fsaccess(SunAuthUnix *au, Nfs3Handle *h, u32int want, u32int *got, Nfs3Attr *attr)
1161 Fid fid;
1162 Nfs3Status ok;
1164 trace("access %.*lH 0x%ux\n", h->len, h->h, want);
1165 if((ok = handletofid(h, &fid, HAccess)) != Nfs3Ok)
1166 return ok;
1167 if(fid.fsys)
1168 return fsysaccess(fid.fsys, au, &fid.fsyshandle, want, got, attr);
1169 *got = want & (Nfs3AccessRead|Nfs3AccessLookup|Nfs3AccessExecute);
1170 return cnodegetattr(fid.cnode, attr);
1173 Nfs3Status
1174 fsreadlink(SunAuthUnix *au, Nfs3Handle *h, char **link)
1176 Fid fid;
1177 Nfs3Status ok;
1179 trace("readlink %.*lH\n", h->len, h->h);
1180 if((ok = handletofid(h, &fid, HRead)) != Nfs3Ok)
1181 return ok;
1182 if(fid.fsys)
1183 return fsysreadlink(fid.fsys, au, &fid.fsyshandle, link);
1184 *link = 0;
1185 return Nfs3ErrNotSupp;
1188 Nfs3Status
1189 fsreadfile(SunAuthUnix *au, Nfs3Handle *h, u32int count, u64int offset, uchar **data, u32int *pcount, u1int *peof)
1191 Fid fid;
1192 Nfs3Status ok;
1194 trace("readfile %.*lH\n", h->len, h->h);
1195 if((ok = handletofid(h, &fid, HRead)) != Nfs3Ok)
1196 return ok;
1197 if(fid.cnode->read)
1198 return fid.cnode->read(fid.cnode, count, offset, data, pcount, peof);
1199 if(fid.fsys)
1200 return fsysreadfile(fid.fsys, au, &fid.fsyshandle, count, offset, data, pcount, peof);
1201 return Nfs3ErrNotSupp;
1204 Nfs3Status
1205 fsreaddir(SunAuthUnix *au, Nfs3Handle *h, u32int len, u64int cookie, uchar **pdata, u32int *pcount, u1int *peof)
1207 Fid fid;
1208 Nfs3Status ok;
1210 trace("readdir %.*lH\n", h->len, h->h);
1211 if((ok = handletofid(h, &fid, HRead)) != Nfs3Ok)
1212 return ok;
1213 if(fid.fsys)
1214 return fsysreaddir(fid.fsys, au, &fid.fsyshandle, len, cookie, pdata, pcount, peof);
1215 return cnodereaddir(fid.cnode, len, cookie, pdata, pcount, peof);
1218 Nfs3Status
1219 logread(Cnode *n, u32int count, u64int offset, uchar **data, u32int *pcount, u1int *peof)
1221 *pcount = 0;
1222 *peof = 1;
1223 return Nfs3Ok;
1226 Nfs3Status
1227 refreshdiskread(Cnode *n, u32int count, u64int offset, uchar **data, u32int *pcount, u1int *peof)
1229 char buf[128];
1231 if(offset != 0){
1232 *pcount = 0;
1233 *peof = 1;
1234 return Nfs3Ok;
1236 if(refreshdisk() < 0)
1237 snprint(buf, sizeof buf, "refreshdisk: %r\n");
1238 else
1239 strcpy(buf, "ok\n");
1240 *data = emalloc(strlen(buf));
1241 strcpy((char*)*data, buf);
1242 *pcount = strlen(buf);
1243 *peof = 1;
1244 return Nfs3Ok;
1247 Nfs3Status
1248 refreshconfigread(Cnode *n, u32int count, u64int offset, uchar **data, u32int *pcount, u1int *peof)
1250 char buf[128];
1252 if(offset != 0){
1253 *pcount = 0;
1254 *peof = 1;
1255 return Nfs3Ok;
1257 if(readconfigfile(&config) < 0)
1258 snprint(buf, sizeof buf, "readconfig: %r\n");
1259 else
1260 strcpy(buf, "ok\n");
1261 *data = emalloc(strlen(buf));
1262 strcpy((char*)*data, buf);
1263 *pcount = strlen(buf);
1264 *peof = 1;
1265 return Nfs3Ok;