3 * - gc of file systems (not going to do just yet?)
5 * - configure on amsterdam
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;
31 uchar mask[IPaddrlen];
47 int encryptedhandles = 1;
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*);
77 fprint(2, "usage: vnfs [-LLRVir] [-a addr] [-b blocksize] [-c cachesize] configfile\n");
78 threadexitsall("usage");
82 threadmain(int argc, char **argv)
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);
104 encryptedhandles = 0;
107 if(srv->localonly == 0)
110 srv->localparanoia = 1;
123 addr = EARGF(usage());
126 blocksize = unittoull(EARGF(usage()));
129 cachesize = unittoull(EARGF(usage()));
142 if((z = vtdial(nil)) == nil)
143 sysfatal("vtdial: %r");
145 sysfatal("vtconnect: %r");
146 if((vcache = vtcachealloc(z, blocksize*cachesize)) == nil)
147 sysfatal("vtcache: %r");
149 configfile = argv[0];
150 if(readconfigfile(&config) < 0)
151 sysfatal("readConfig: %r");
154 nfschan = chancreate(sizeof(SunMsg*), 0);
155 mountchan = chancreate(sizeof(SunMsg*), 0);
156 timerchan = chancreate(sizeof(void*), 0);
158 if(sunsrvudp(srv, addr) < 0)
159 sysfatal("starting server: %r");
161 sunsrvthreadcreate(srv, nfs3proc, nfschan);
162 sunsrvthreadcreate(srv, mount3proc, mountchan);
163 sunsrvthreadcreate(srv, timerthread, nil);
164 proccreate(timerproc, nil, 32768);
166 sunsrvprog(srv, &nfs3prog, nfschan);
167 sunsrvprog(srv, &nfsmount3prog, mountchan);
172 #define TWID64 ((u64int)~(u64int)0)
182 n = strtoul(s, &es, 0);
183 if(*es == 'k' || *es == 'K'){
186 }else if(*es == 'm' || *es == 'M'){
189 }else if(*es == 'g' || *es == 'G'){
192 }else if(*es == 't' || *es == 'T'){
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.
211 * The decrypted handles begin with the following header:
213 * sessid[8] random session id chosen at start time
214 * len[4] length of handle that follows
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
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:
227 * - position in the synthesized config tree
228 * - enough of the path to do glob matching
229 * - position in an archived file system image
231 * and the handles need to be stable across changes in the config file
232 * (though not across server restarts since encryption screws
235 * We encode each of the first two as a 10-byte hash that is
236 * the first half of a SHA1 hash.
242 HeaderSize = SessidSize+4,
243 MaxHandleSize = Nfs3MaxHandleSize - HeaderSize
247 uchar sessid[SessidSize];
250 hencrypt(Nfs3Handle *h)
256 * root handle has special encryption - a single 0 byte - so that it
259 if(h->len == root.len && memcmp(h->h, root.h, root.len) == 0){
265 if(!encryptedhandles)
268 if(h->len > MaxHandleSize){
270 fprint(2, "handle too long: %.*lH\n", h->len, h->h);
271 memset(h->h, 'X', Nfs3MaxHandleSize);
272 h->len = Nfs3MaxHandleSize;
277 memmove(p+HeaderSize, p, h->len);
278 memmove(p, sessid, SessidSize);
279 *(u32int*)(p+SessidSize) = h->len;
280 h->len += HeaderSize;
282 if(encryptedhandles){
283 while(h->len < MaxHandleSize)
286 aesCBCencrypt(h->h, MaxHandleSize, &aes);
291 hdecrypt(Nfs3Handle *h)
295 if(h->len == 1 && h->h[0] == 0){ /* single 0 byte is root */
300 if(!encryptedhandles)
303 if(h->len <= HeaderSize)
304 return Nfs3ErrBadHandle;
305 if(encryptedhandles){
306 if(h->len != MaxHandleSize)
307 return Nfs3ErrBadHandle;
309 aesCBCdecrypt(h->h, h->len, &aes);
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);
323 uchar key[32], ivec[AESbsize];
326 *(u32int*)sessid = truerand();
327 for(i=0; i<nelem(key); i+=4)
328 *(u32int*)&key[i] = truerand();
329 for(i=0; i<nelem(ivec); i++)
330 ivec[i] = fastrand();
331 setupAESstate(&aesstate, key, sizeof key, ivec);
337 * The main purpose of the configuration file is to define a tree
338 * in which the archived file system images are mounted.
339 * The tree is stored as Entry structures, defined below.
341 * The configuration file also allows one to define shell-like
342 * glob expressions matching paths that are not to be displayed.
343 * The matched files or directories are shown in directory listings
344 * (could suppress these if we cared) but they cannot be opened,
345 * read, or written, and getattr returns zeroed data.
349 /* sizes used in handles; see nfs server below */
351 FsysHandleOffset = CnodeHandleSize
365 char *name; /* path element */
366 Cnode *parent; /* in tree */
367 Cnode *nextsib; /* in tree */
368 Cnode *kidlist; /* in tree */
369 Cnode *nexthash; /* in hash list */
371 Nfs3Status (*read)(Cnode*, u32int, u64int, uchar**, u32int*, u1int*); /* synthesized read fn */
373 uchar handle[VtScoreSize]; /* sha1(path to here) */
374 ulong mtime; /* mtime for this directory entry */
376 /* fsys overlay on this node */
377 Fsys *fsys; /* cache of memory structure */
378 Nfs3Handle fsyshandle;
379 int isblackhole; /* walking down keeps you here */
383 * if a mount point is inside another file system,
384 * the fsys and fsyshandle above have the old fs info,
385 * the mfsys and mfsyshandle below have the new one.
386 * getattrs must use the old info for consistency.
388 int ismtpt; /* whether there is an fsys mounted here */
389 uchar fsysscore[VtScoreSize]; /* score of fsys image on venti */
390 char *fsysimage; /* raw disk image */
391 Fsys *mfsys; /* mounted file system (nil until walked) */
392 Nfs3Handle mfsyshandle; /* handle to root of mounted fsys */
400 return (s[0]<<2)|(s[1]>>6); /* first 10 bits */
404 mkcnode(Ctree *t, Cnode *parent, char *elem, uint elen, char *path, uint plen)
409 n = emalloc(sizeof *n + elen+1);
410 n->name = (char*)(n+1);
411 memmove(n->name, elem, elen);
415 n->nextsib = parent->kidlist;
419 sha1((uchar*)path, plen, n->handle, nil);
420 h = dumbhash(n->handle);
421 n->nexthash = t->hash[h];
433 for(i=0; i<nelem(t->hash); i++)
434 for(n=t->hash[i]; n; n=n->nexthash)
435 if(n->name[0] != '+')
447 for(i=0; i<nelem(t->hash); i++)
448 for(n=t->hash[i]; n; n=n->nexthash){
450 disksync(n->mfsys->disk);
452 disksync(n->fsys->disk);
463 /* just zero all the garbage and leave it linked into the tree */
464 for(i=0; i<nelem(t->hash); i++){
465 for(n=t->hash[i]; n; n=n->nexthash){
471 memset(n->fsysscore, 0, sizeof n->fsysscore);
474 memset(&n->fsyshandle, 0, sizeof n->fsyshandle);
475 memset(&n->mfsyshandle, 0, sizeof n->mfsyshandle);
481 cnodewalk(Cnode *n, char *name, uint len, int markokay)
485 for(nn=n->kidlist; nn; nn=nn->nextsib)
486 if(strncmp(nn->name, name, len) == 0 && nn->name[len] == 0)
487 if(!nn->mark || markokay)
493 ctreewalkpath(Ctree *t, char *name, ulong createmtime)
504 nextp = strchr(p, '/');
507 if((nn = cnodewalk(n, p, nextp-p, 1)) == nil){
510 nn = mkcnode(t, n, p, nextp-p, name, nextp-name);
511 nn->mtime = createmtime;
526 t = emalloc(sizeof *t);
527 t->root = mkcnode(t, nil, "", 0, "", 0);
529 ctreewalkpath(t, "/+log", time(0))->read = logread;
530 ctreewalkpath(t, "/+refreshdisk", time(0))->read = refreshdiskread;
531 ctreewalkpath(t, "/+refreshconfig", time(0))->read = refreshconfigread;
537 ctreemountfsys(Ctree *t, char *path, ulong time, uchar *score, char *file)
543 n = ctreewalkpath(t, path, time);
545 if(n->ismtpt && (n->fsysimage || memcmp(n->fsysscore, score, VtScoreSize) != 0)){
548 n->fsys = nil; /* leak (might be other refs) */
550 memmove(n->fsysscore, score, VtScoreSize);
552 if(n->ismtpt && (n->fsysimage==nil || strcmp(n->fsysimage, file) != 0)){
555 n->fsys = nil; /* leak (might be other refs) */
557 n->fsysimage = emalloc(strlen(file)+1);
558 strcpy(n->fsysimage, file);
565 cnodebyhandle(Ctree *t, uchar *p)
571 for(n=t->hash[h]; n; n=n->nexthash)
572 if(memcmp(n->handle, p, CnodeHandleSize) == 0)
578 parseipandmask(char *s, uchar *ip, uchar *mask)
585 if(parseip(ip, s) == ~0UL)
588 memset(mask, 0xFF, IPaddrlen);
590 if(isdigit((uchar)*p) && strtol(p, &q, 10)>=0 && *q==0)
592 if(parseipmask(mask, p) == ~0UL)
597 /*fprint(2, "parseipandmask %s => %I %I\n", s, ip, mask); */
602 parsetime(char *s, ulong *time)
609 /* decimal integer is seconds since 1970 */
610 x = strtoul(s, &p, 10);
611 if(x > 0 && *p == 0){
616 /* otherwise expect yyyy/mmdd/hhmm */
617 if(strlen(s) != 14 || s[4] != '/' || s[9] != '/')
620 if(!isdigit((uchar)s[i]) || !isdigit((uchar)s[i+5]) || !isdigit((uchar)s[i+10]))
622 memset(&tm, 0, sizeof tm);
623 tm.year = atoi(s)-1900;
624 if(tm.year < 0 || tm.year > 200)
626 tm.mon = (s[5]-'0')*10+s[6]-'0' - 1;
627 if(tm.mon < 0 || tm.mon > 11)
629 tm.mday = (s[7]-'0')*10+s[8]-'0';
630 if(tm.mday < 0 || tm.mday > 31)
632 tm.hour = (s[10]-'0')*10+s[11]-'0';
633 if(tm.hour < 0 || tm.hour > 23)
635 tm.min = (s[12]-'0')*10+s[13]-'0';
636 if(tm.min < 0 || tm.min > 59)
638 strcpy(tm.zone, "XXX"); /* anything but GMT */
640 print("tm2sec %d/%d/%d/%d/%d\n",
641 tm.year, tm.mon, tm.mday, tm.hour, tm.min);
644 if(0) print("time %lud\n", *time);
650 readconfigfile(Config *cp)
652 char *f[10], *image, *p, *pref, *q, *name;
654 uchar scorebuf[VtScoreSize], *score;
662 if((dir = dirstat(name)) == nil)
664 if(c.mtime == dir->mtime){
668 c.mtime = dir->mtime;
670 if((b = Bopen(name, OREAD)) == nil)
674 * Reuse old tree, garbage collecting entries that
675 * are not mentioned in the new config file.
685 for(; (p=Brdstr(b, '\n', 1)) != nil; free(p)){
687 if((q = strchr(p, '#')) != nil)
689 nf = tokenize(p, f, nelem(f));
692 if(strcmp(f[0], "mount") == 0){
694 werrstr("syntax error: mount /path /dev|score mtime");
698 werrstr("unrooted path %s", f[1]);
704 if(access(f[2], AEXIST) < 0){
705 werrstr("image %s does not exist", f[2]);
710 if(vtparsescore(f[2], &pref, scorebuf) < 0){
711 werrstr("bad score %s", f[2]);
716 if(parsetime(f[3], &time) < 0){
717 fprint(2, "%s:%d: bad time %s\n", name, line, f[3]);
720 ctreemountfsys(c.ctree, f[1], time, score, image);
723 if(strcmp(f[0], "allow") == 0 || strcmp(f[0], "deny") == 0){
725 werrstr("syntax error: allow|deny ip[/mask]");
728 c.ok = erealloc(c.ok, (c.nok+1)*sizeof(c.ok[0]));
729 if(parseipandmask(f[1], c.ok[c.nok].ip, c.ok[c.nok].mask) < 0){
730 werrstr("bad ip[/mask]: %s", f[1]);
733 c.ok[c.nok].okay = (strcmp(f[0], "allow") == 0);
737 werrstr("unknown verb '%s'", f[0]);
739 fprint(2, "%s:%d: %r\n", name, line);
750 ipokay(uchar *ip, ushort port)
753 uchar ipx[IPaddrlen];
756 for(i=0; i<config.nok; i++){
758 maskip(ip, ok->mask, ipx);
759 if(0) fprint(2, "%I & %I = %I (== %I?)\n",
760 ip, ok->mask, ipx, ok->ip);
761 if(memcmp(ipx, ok->ip, IPaddrlen) == 0)
764 if(config.nok == 0) /* all is permitted */
766 /* otherwise default is none allowed */
771 cnodelookup(Ctree *t, Cnode **np, char *name)
778 if((nn = cnodewalk(n, name, strlen(name), 0)) == nil){
779 if(n->ismtpt || n->fsys){
780 if((nn = cnodewalk(n, "", 0, 1)) == nil){
781 nn = mkcnode(t, n, "", 0, (char*)n->handle, SHA1dlen);
794 cnodegetattr(Cnode *n, Nfs3Attr *attr)
796 memset(attr, 0, sizeof *attr);
798 attr->type = Nfs3FileReg;
803 attr->type = Nfs3FileDir;
808 attr->fileid = *(u64int*)n->handle;
809 attr->atime.sec = n->mtime;
810 attr->mtime.sec = n->mtime;
811 attr->ctime.sec = n->mtime;
816 cnodereaddir(Cnode *n, u32int count, u64int cookie, uchar **pdata, u32int *pcount, u1int *peof)
818 uchar *data, *p, *ep, *np;
833 data = emalloc(count);
837 if(n->mark || n->name[0] == '+'){
843 ne.namelen = strlen(n->name);
844 ne.cookie = ++cookie;
845 ne.fileid = *(u64int*)n->handle;
846 if(nfs3entrypack(p, ep, &np, &ne) < 0)
871 /* refreshconfig(); */
876 * Actually serve the NFS requests. Called from nfs3srv.c.
877 * Each request runs in its own thread (coroutine).
879 * Decrypted handles have the form:
881 * config[20] - SHA1 hash identifying a config tree node
882 * glob[10] - SHA1 hash prefix identifying a glob state
883 * fsyshandle[<=10] - disk file system handle (usually 4 bytes)
887 * A fid represents a point in the file tree.
888 * There are three components, all derived from the handle:
890 * - config tree position (also used to find fsys)
891 * - glob state for exclusions
892 * - file system position
902 typedef struct Fid Fid;
907 Nfs3Handle fsyshandle;
911 handlecmp(Nfs3Handle *h, Nfs3Handle *h1)
913 if(h->len != h1->len)
914 return h->len - h1->len;
915 return memcmp(h->h, h1->h, h->len);
919 handletofid(Nfs3Handle *eh, Fid *fid, int mode)
926 Nfs3Handle h2, *h, *fh;
928 memset(fid, 0, sizeof *fid);
934 * Not necessary, but speeds up ls -l /dump/2005
935 * HAttr and HAccess must be handled the same way
936 * because both can be used to fetch attributes.
937 * Acting differently yields inconsistencies at mount points,
938 * and causes FreeBSD ls -l to fail.
940 if(mode == HAttr || mode == HAccess)
948 if((ok = hdecrypt(h)) != Nfs3Ok)
950 trace("handletofid: decrypted %.*lH\n", h->len, h->h);
951 if(h->len < FsysHandleOffset)
952 return Nfs3ErrBadHandle;
955 * Find place in config tree.
957 if((n = cnodebyhandle(config.ctree, h->h)) == nil)
961 if(n->ismtpt && domount){
963 * Open fsys for mount point if needed.
966 trace("handletofid: mounting %V/%s\n", n->fsysscore, n->fsysimage);
968 if(strcmp(n->fsysimage, "/dev/null") == 0)
970 if((disk = diskopenfile(n->fsysimage)) == nil){
971 fprint(2, "cannot open disk %s: %r\n", n->fsysimage);
974 if((cdisk = diskcache(disk, blocksize, 64)) == nil){
975 fprint(2, "cannot cache disk %s: %r\n", n->fsysimage);
980 if((disk = diskopenventi(vcache, n->fsysscore)) == nil){
981 fprint(2, "cannot open venti disk %V: %r\n", n->fsysscore);
985 if((fsys = fsysopen(disk)) == nil){
986 fprint(2, "cannot open fsys on %V: %r\n", n->fsysscore);
991 fsysroot(fsys, &n->mfsyshandle);
997 fid->fsys = n->mfsys;
998 fid->fsyshandle = n->mfsyshandle;
1001 * Use fsys handle from tree or from handle.
1002 * This assumes that fsyshandle was set by fidtohandle
1003 * earlier, so it's not okay to reuse handles (except the root)
1004 * across sessions. The encryption above makes and
1005 * enforces the same restriction, so this is okay.
1007 fid->fsys = n->fsys;
1008 fh = &fid->fsyshandle;
1010 fh->len = h->len-FsysHandleOffset;
1011 memmove(fh->h, h->h+FsysHandleOffset, fh->len);
1013 *fh = n->fsyshandle;
1014 trace("handletofid: fsyshandle %.*lH\n", fh->len, fh->h);
1018 * TO DO (maybe): some sort of path restriction here.
1020 trace("handletofid: cnode %s fsys %p fsyshandle %.*lH\n",
1021 n->name, fid->fsys, fid->fsyshandle.len, fid->fsyshandle.h);
1026 _fidtohandle(Fid *fid, Nfs3Handle *h)
1032 * Record fsys handle in n, don't bother sending it to client
1035 n->fsys = fid->fsys;
1036 if(!n->isblackhole){
1037 n->fsyshandle = fid->fsyshandle;
1038 fid->fsyshandle.len = 0;
1040 memmove(h->h, n->handle, CnodeHandleSize);
1041 memmove(h->h+FsysHandleOffset, fid->fsyshandle.h, fid->fsyshandle.len);
1042 h->len = FsysHandleOffset+fid->fsyshandle.len;
1046 fidtohandle(Fid *fid, Nfs3Handle *h)
1048 _fidtohandle(fid, h);
1057 memset(&fid, 0, sizeof fid);
1058 fid.cnode = config.ctree->root;
1059 _fidtohandle(&fid, &root);
1063 fsgetroot(Nfs3Handle *h)
1070 fsgetattr(SunAuthUnix *au, Nfs3Handle *h, Nfs3Attr *attr)
1075 trace("getattr %.*lH\n", h->len, h->h);
1076 if((ok = handletofid(h, &fid, HAttr)) != Nfs3Ok)
1079 return fsysgetattr(fid.fsys, au, &fid.fsyshandle, attr);
1081 return cnodegetattr(fid.cnode, attr);
1085 * Lookup is always the hard part.
1088 fslookup(SunAuthUnix *au, Nfs3Handle *h, char *name, Nfs3Handle *nh)
1096 trace("lookup %.*lH %s\n", h->len, h->h, name);
1099 if(strcmp(name, "..") == 0 || strcmp(name, ".") == 0)
1101 if((ok = handletofid(h, &fid, mode)) != Nfs3Ok){
1103 trace("lookup: handletofid %r\n");
1107 if(strcmp(name, ".") == 0){
1108 fidtohandle(&fid, nh);
1113 * Walk down file system and cnode simultaneously.
1114 * If dotdot and file system doesn't move, need to walk
1115 * up cnode. Save the corresponding fsys handles in
1116 * the cnode as we walk down so that we'll have them
1117 * for dotdotting back up.
1122 * Walk down config tree and file system simultaneously.
1124 if((ok = cnodelookup(config.ctree, &n, name)) != Nfs3Ok){
1126 trace("lookup: cnodelookup: %r\n");
1131 if((ok = fsyslookup(fid.fsys, au, &fid.fsyshandle, name, &xh)) != Nfs3Ok){
1133 trace("lookup: fsyslookup: %r\n");
1136 fid.fsyshandle = xh;
1140 * Walking dotdot. Ick.
1142 trace("lookup dotdot fsys=%p\n", fid.fsys);
1145 * Walk up file system, then try up config tree.
1147 if((ok = fsyslookup(fid.fsys, au, &fid.fsyshandle, "..", &xh)) != Nfs3Ok){
1149 trace("lookup fsyslookup: %r\n");
1152 fid.fsyshandle = xh;
1155 * Usually just go to n->parent.
1157 * If we're in a subtree of the mounted file system that
1158 * isn't represented explicitly by the config tree (instead
1159 * the black hole node represents the entire file tree),
1160 * then we only go to n->parent when we've dotdotted back
1161 * to the right handle.
1163 if(n->parent == nil)
1164 trace("lookup dotdot: no parent\n");
1166 trace("lookup dotdot: parent %.*lH, have %.*lH\n",
1167 n->parent->fsyshandle.len, n->parent->fsyshandle.h,
1172 if(handlecmp(&n->parent->mfsyshandle, &xh) == 0)
1180 * No file system, just walk up.
1187 fid.fsyshandle = n->fsyshandle;
1190 fidtohandle(&fid, nh);
1195 fsaccess(SunAuthUnix *au, Nfs3Handle *h, u32int want, u32int *got, Nfs3Attr *attr)
1200 trace("access %.*lH 0x%ux\n", h->len, h->h, want);
1201 if((ok = handletofid(h, &fid, HAccess)) != Nfs3Ok)
1204 return fsysaccess(fid.fsys, au, &fid.fsyshandle, want, got, attr);
1205 *got = want & (Nfs3AccessRead|Nfs3AccessLookup|Nfs3AccessExecute);
1206 return cnodegetattr(fid.cnode, attr);
1210 fsreadlink(SunAuthUnix *au, Nfs3Handle *h, char **link)
1215 trace("readlink %.*lH\n", h->len, h->h);
1216 if((ok = handletofid(h, &fid, HRead)) != Nfs3Ok)
1219 return fsysreadlink(fid.fsys, au, &fid.fsyshandle, link);
1221 return Nfs3ErrNotSupp;
1225 fsreadfile(SunAuthUnix *au, Nfs3Handle *h, u32int count, u64int offset, uchar **data, u32int *pcount, u1int *peof)
1230 trace("readfile %.*lH\n", h->len, h->h);
1231 if((ok = handletofid(h, &fid, HRead)) != Nfs3Ok)
1234 return fid.cnode->read(fid.cnode, count, offset, data, pcount, peof);
1236 return fsysreadfile(fid.fsys, au, &fid.fsyshandle, count, offset, data, pcount, peof);
1237 return Nfs3ErrNotSupp;
1241 fsreaddir(SunAuthUnix *au, Nfs3Handle *h, u32int len, u64int cookie, uchar **pdata, u32int *pcount, u1int *peof)
1246 trace("readdir %.*lH\n", h->len, h->h);
1247 if((ok = handletofid(h, &fid, HRead)) != Nfs3Ok)
1250 return fsysreaddir(fid.fsys, au, &fid.fsyshandle, len, cookie, pdata, pcount, peof);
1251 return cnodereaddir(fid.cnode, len, cookie, pdata, pcount, peof);
1255 logread(Cnode *n, u32int count, u64int offset, uchar **data, u32int *pcount, u1int *peof)
1263 refreshdiskread(Cnode *n, u32int count, u64int offset, uchar **data, u32int *pcount, u1int *peof)
1272 if(refreshdisk() < 0)
1273 snprint(buf, sizeof buf, "refreshdisk: %r\n");
1275 strcpy(buf, "ok\n");
1276 *data = emalloc(strlen(buf));
1277 strcpy((char*)*data, buf);
1278 *pcount = strlen(buf);
1284 refreshconfigread(Cnode *n, u32int count, u64int offset, uchar **data, u32int *pcount, u1int *peof)
1293 if(readconfigfile(&config) < 0)
1294 snprint(buf, sizeof buf, "readconfig: %r\n");
1296 strcpy(buf, "ok\n");
1297 *data = emalloc(strlen(buf));
1298 strcpy((char*)*data, buf);
1299 *pcount = strlen(buf);