URI:
       tvnfs.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tvnfs.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 }