1 #include "all.h"
2
3 /*
4 * Cf. /lib/rfc/rfc1094
5 */
6
7 static int nfsnull(int, Rpccall*, Rpccall*);
8 static int nfsgetattr(int, Rpccall*, Rpccall*);
9 static int nfssetattr(int, Rpccall*, Rpccall*);
10 static int nfsroot(int, Rpccall*, Rpccall*);
11 static int nfslookup(int, Rpccall*, Rpccall*);
12 static int nfsreadlink(int, Rpccall*, Rpccall*);
13 static int nfsread(int, Rpccall*, Rpccall*);
14 static int nfswritecache(int, Rpccall*, Rpccall*);
15 static int nfswrite(int, Rpccall*, Rpccall*);
16 static int nfscreate(int, Rpccall*, Rpccall*);
17 static int nfsremove(int, Rpccall*, Rpccall*);
18 static int nfsrename(int, Rpccall*, Rpccall*);
19 static int nfslink(int, Rpccall*, Rpccall*);
20 static int nfssymlink(int, Rpccall*, Rpccall*);
21 static int nfsmkdir(int, Rpccall*, Rpccall*);
22 static int nfsrmdir(int, Rpccall*, Rpccall*);
23 static int nfsreaddir(int, Rpccall*, Rpccall*);
24 static int nfsstatfs(int, Rpccall*, Rpccall*);
25
26 Procmap nfsproc[] = {
27 0, nfsnull, /* void */
28 1, nfsgetattr, /* Fhandle */
29 2, nfssetattr, /* Fhandle, Sattr */
30 3, nfsroot, /* void */
31 4, nfslookup, /* Fhandle, String */
32 5, nfsreadlink, /* Fhandle */
33 6, nfsread, /* Fhandle, long, long, long */
34 7, nfswritecache,/* void */
35 8, nfswrite, /* Fhandle, long, long, long, String */
36 9, nfscreate, /* Fhandle, String, Sattr */
37 10, nfsremove, /* Fhandle, String */
38 11, nfsrename, /* Fhandle, String, Fhandle, String */
39 12, nfslink, /* Fhandle, Fhandle, String */
40 13, nfssymlink, /* Fhandle, String, String, Sattr */
41 14, nfsmkdir, /* Fhandle, String, Sattr */
42 15, nfsrmdir, /* Fhandle, String */
43 16, nfsreaddir, /* Fhandle, long, long */
44 17, nfsstatfs, /* Fhandle */
45 0, 0
46 };
47
48 void nfsinit(int, char**);
49 extern void mntinit(int, char**);
50 extern Procmap mntproc[];
51
52 Progmap progmap[] = {
53 100005, 1, mntinit, mntproc,
54 100003, 2, nfsinit, nfsproc,
55 0, 0, 0,
56 };
57
58 int myport = 2049;
59 long nfstime;
60 int conftime;
61
62 void
main(int argc,char * argv[])63 main(int argc, char *argv[])
64 {
65 server(argc, argv, myport, progmap);
66 }
67
68 static void
doalarm(void)69 doalarm(void)
70 {
71 nfstime = time(0);
72 mnttimer(nfstime);
73 if(conftime+5*60 < nfstime){
74 conftime = nfstime;
75 readunixidmaps(config);
76 }
77 }
78
79 void
nfsinit(int argc,char ** argv)80 nfsinit(int argc, char **argv)
81 {
82 /*
83 * mntinit will have already parsed our options.
84 */
85 USED(argc, argv);
86 clog("nfs file server init\n");
87 rpcalarm = doalarm;
88 nfstime = time(0);
89 }
90
91 static int
nfsnull(int n,Rpccall * cmd,Rpccall * reply)92 nfsnull(int n, Rpccall *cmd, Rpccall *reply)
93 {
94 USED(n, reply);
95 chat("nfsnull...");
96 showauth(&cmd->cred);
97 chat("OK\n");
98 return 0;
99 }
100
101 static int
nfsgetattr(int n,Rpccall * cmd,Rpccall * reply)102 nfsgetattr(int n, Rpccall *cmd, Rpccall *reply)
103 {
104 Xfid *xf;
105 Dir dir;
106 uchar *dataptr = reply->results;
107
108 chat("getattr...");
109 if(n != FHSIZE)
110 return garbage(reply, "bad count");
111 xf = rpc2xfid(cmd, &dir);
112 if(xf == 0)
113 return error(reply, NFSERR_STALE);
114 chat("%s...", xf->xp->name);
115 PLONG(NFS_OK);
116 dataptr += dir2fattr(cmd->up, &dir, dataptr);
117 chat("OK\n");
118 return dataptr - (uchar *)reply->results;
119 }
120
121 static int
nfssetattr(int n,Rpccall * cmd,Rpccall * reply)122 nfssetattr(int n, Rpccall *cmd, Rpccall *reply)
123 {
124 Xfid *xf;
125 Dir dir, nd;
126 Sattr sattr;
127 int r;
128 uchar *argptr = cmd->args;
129 uchar *dataptr = reply->results;
130
131 chat("setattr...");
132 if(n <= FHSIZE)
133 return garbage(reply, "count too small");
134 xf = rpc2xfid(cmd, &dir);
135 argptr += FHSIZE;
136 argptr += convM2sattr(argptr, &sattr);
137 if(argptr != &((uchar *)cmd->args)[n])
138 return garbage(reply, "bad count");
139 chat("mode=0%lo,u=%ld,g=%ld,size=%ld,atime=%ld,mtime=%ld...",
140 sattr.mode, sattr.uid, sattr.gid, sattr.size,
141 sattr.atime, sattr.mtime);
142 if(xf == 0)
143 return error(reply, NFSERR_STALE);
144 if(sattr.uid != NOATTR || sattr.gid != NOATTR)
145 return error(reply, NFSERR_PERM);
146 if(sattr.size == 0){
147 if(xf->xp->s != xf->xp->parent->s){
148 if(xfauthremove(xf, cmd->user) < 0)
149 return error(reply, NFSERR_PERM);
150 }else if(dir.length && xfopen(xf, Trunc|Oread|Owrite) < 0)
151 return error(reply, NFSERR_PERM);
152 }else if(sattr.size != NOATTR)
153 return error(reply, NFSERR_PERM);
154 r = 0;
155 nulldir(&nd);
156 if(sattr.mode != NOATTR)
157 ++r, nd.mode = (dir.mode & ~0777) | (sattr.mode & 0777);
158 if(sattr.atime != NOATTR)
159 ++r, nd.atime = sattr.atime;
160 if(sattr.mtime != NOATTR)
161 ++r, nd.mtime = sattr.mtime;
162 chat("sattr.mode=%luo dir.mode=%luo nd.mode=%luo...", sattr.mode, dir.mode, nd.mode);
163 if(r){
164 r = xfwstat(xf, &nd);
165 if(r < 0)
166 return error(reply, NFSERR_PERM);
167 }
168 if(xfstat(xf, &dir) < 0)
169 return error(reply, NFSERR_STALE);
170 PLONG(NFS_OK);
171 dataptr += dir2fattr(cmd->up, &dir, dataptr);
172 chat("OK\n");
173 return dataptr - (uchar *)reply->results;
174 }
175
176 static int
nfsroot(int n,Rpccall * cmd,Rpccall * reply)177 nfsroot(int n, Rpccall *cmd, Rpccall *reply)
178 {
179 USED(n, reply);
180 chat("nfsroot...");
181 showauth(&cmd->cred);
182 chat("OK\n");
183 return 0;
184 }
185
186 static int
nfslookup(int n,Rpccall * cmd,Rpccall * reply)187 nfslookup(int n, Rpccall *cmd, Rpccall *reply)
188 {
189 Xfile *xp;
190 Xfid *xf, *newxf;
191 String elem;
192 Dir dir;
193 uchar *argptr = cmd->args;
194 uchar *dataptr = reply->results;
195
196 chat("lookup...");
197 if(n <= FHSIZE)
198 return garbage(reply, "count too small");
199 xf = rpc2xfid(cmd, 0);
200 argptr += FHSIZE;
201 argptr += string2S(argptr, &elem);
202 if(argptr != &((uchar *)cmd->args)[n])
203 return garbage(reply, "bad count");
204 if(xf == 0)
205 return error(reply, NFSERR_STALE);
206 xp = xf->xp;
207 if(!(xp->qid.type & QTDIR))
208 return error(reply, NFSERR_NOTDIR);
209 chat("%s -> \"%.*s\"...", xp->name, utfnlen(elem.s, elem.n), elem.s);
210 if(xp->s->noauth == 0 && xp->parent == xp && elem.s[0] == '#')
211 newxf = xfauth(xp, &elem);
212 else
213 newxf = xfwalkcr(Twalk, xf, &elem, 0);
214 if(newxf == 0)
215 return error(reply, NFSERR_NOENT);
216 if(xfstat(newxf, &dir) < 0)
217 return error(reply, NFSERR_IO);
218 PLONG(NFS_OK);
219 dataptr += xp2fhandle(newxf->xp, dataptr);
220 dataptr += dir2fattr(cmd->up, &dir, dataptr);
221 chat("OK\n");
222 return dataptr - (uchar *)reply->results;
223 }
224
225 static int
nfsreadlink(int n,Rpccall * cmd,Rpccall * reply)226 nfsreadlink(int n, Rpccall *cmd, Rpccall *reply)
227 {
228 USED(n, reply);
229 chat("readlink...");
230 showauth(&cmd->cred);
231 return error(reply, NFSERR_NOENT);
232 }
233
234 static int
nfsread(int n,Rpccall * cmd,Rpccall * reply)235 nfsread(int n, Rpccall *cmd, Rpccall *reply)
236 {
237 Session *s;
238 Xfid *xf;
239 Dir dir;
240 int offset, count;
241 uchar *argptr = cmd->args;
242 uchar *dataptr = reply->results;
243 uchar *readptr = dataptr + 4 + 17*4 + 4;
244
245 chat("read...");
246 if(n != FHSIZE+12)
247 return garbage(reply, "bad count");
248 xf = rpc2xfid(cmd, 0);
249 argptr += FHSIZE;
250 offset = GLONG();
251 count = GLONG();
252 if(xf == 0)
253 return error(reply, NFSERR_STALE);
254 chat("%s %d %d...", xf->xp->name, offset, count);
255 if(xf->xp->s != xf->xp->parent->s){
256 count = xfauthread(xf, offset, readptr, count);
257 }else{
258 if(xfopen(xf, Oread) < 0)
259 return error(reply, NFSERR_PERM);
260 if(count > 8192)
261 count = 8192;
262 s = xf->xp->s;
263 setfid(s, xf->opfid);
264 xf->opfid->tstale = nfstime + 60;
265 s->f.offset = offset;
266 s->f.count = count;
267 if(xmesg(s, Tread) < 0)
268 return error(reply, NFSERR_IO);
269 count = s->f.count;
270 memmove(readptr, s->f.data, count);
271 }
272 if(xfstat(xf, &dir) < 0)
273 return error(reply, NFSERR_IO);
274 PLONG(NFS_OK);
275 dataptr += dir2fattr(cmd->up, &dir, dataptr);
276 PLONG(count);
277 dataptr += ROUNDUP(count);
278 chat("%d OK\n", count);
279 return dataptr - (uchar *)reply->results;
280 }
281
282 static int
nfswritecache(int n,Rpccall * cmd,Rpccall * reply)283 nfswritecache(int n, Rpccall *cmd, Rpccall *reply)
284 {
285 USED(n, reply);
286 chat("writecache...");
287 showauth(&cmd->cred);
288 chat("OK\n");
289 return 0;
290 }
291
292 static int
nfswrite(int n,Rpccall * cmd,Rpccall * reply)293 nfswrite(int n, Rpccall *cmd, Rpccall *reply)
294 {
295 Session *s;
296 Xfid *xf;
297 Dir dir;
298 int offset, count;
299 uchar *argptr = cmd->args;
300 uchar *dataptr = reply->results;
301
302 chat("write...");
303 if(n < FHSIZE+16)
304 return garbage(reply, "count too small");
305 xf = rpc2xfid(cmd, 0);
306 argptr += FHSIZE + 4;
307 offset = GLONG();
308 argptr += 4;
309 count = GLONG();
310 if(xf == 0)
311 return error(reply, NFSERR_STALE);
312 chat("%s %d %d...", xf->xp->name, offset, count);
313 if(xf->xp->s != xf->xp->parent->s){
314 if(xfauthwrite(xf, offset, argptr, count) < 0)
315 return error(reply, NFSERR_IO);
316 }else{
317 if(xfopen(xf, Owrite) < 0)
318 return error(reply, NFSERR_PERM);
319 s = xf->xp->s;
320 setfid(s, xf->opfid);
321 xf->opfid->tstale = nfstime + 60;
322 s->f.offset = offset;
323 s->f.count = count;
324 s->f.data = (char *)argptr;
325 if(xmesg(s, Twrite) < 0)
326 return error(reply, NFSERR_IO);
327 }
328 if(xfstat(xf, &dir) < 0)
329 return error(reply, NFSERR_IO);
330 PLONG(NFS_OK);
331 dataptr += dir2fattr(cmd->up, &dir, dataptr);
332 chat("OK\n");
333 return dataptr - (uchar *)reply->results;
334 }
335
336 static int
creat(int n,Rpccall * cmd,Rpccall * reply,int chdir)337 creat(int n, Rpccall *cmd, Rpccall *reply, int chdir)
338 {
339 Xfid *xf, *newxf;
340 Xfile *xp;
341 String elem;
342 Dir dir; Sattr sattr;
343 uchar *argptr = cmd->args;
344 uchar *dataptr = reply->results;
345 int trunced;
346
347 if(n <= FHSIZE)
348 return garbage(reply, "count too small");
349 xf = rpc2xfid(cmd, 0);
350 argptr += FHSIZE;
351 argptr += string2S(argptr, &elem);
352 argptr += convM2sattr(argptr, &sattr);
353 if(argptr != &((uchar *)cmd->args)[n])
354 return garbage(reply, "bad count");
355 if(xf == 0)
356 return error(reply, NFSERR_STALE);
357 xp = xf->xp;
358 if(!(xp->qid.type & QTDIR))
359 return error(reply, NFSERR_NOTDIR);
360 chat("%s/%.*s...", xp->name, utfnlen(elem.s, elem.n), elem.s);
361 trunced = 0;
362 if(xp->parent == xp && elem.s[0] == '#'){
363 newxf = xfauth(xp, &elem);
364 if(newxf == 0)
365 return error(reply, NFSERR_PERM);
366 if(xfauthremove(newxf, cmd->user) < 0)
367 return error(reply, NFSERR_PERM);
368 trunced = 1;
369 }else
370 newxf = xfwalkcr(Twalk, xf, &elem, 0);
371 if(newxf == 0){
372 newxf = xfwalkcr(Tcreate, xf, &elem, chdir|(sattr.mode&0777));
373 if(newxf)
374 trunced = 1;
375 else
376 newxf = xfwalkcr(Twalk, xf, &elem, 0);
377 }
378 if(newxf == 0)
379 return error(reply, NFSERR_PERM);
380 if(!trunced && chdir)
381 return error(reply, NFSERR_EXIST);
382 if(!trunced && xfopen(newxf, Trunc|Oread|Owrite) < 0)
383 return error(reply, NFSERR_PERM);
384 if(xfstat(newxf, &dir) < 0)
385 return error(reply, NFSERR_IO);
386
387 PLONG(NFS_OK);
388 dataptr += xp2fhandle(newxf->xp, dataptr);
389 dataptr += dir2fattr(cmd->up, &dir, dataptr);
390 chat("OK\n");
391 return dataptr - (uchar *)reply->results;
392 }
393
394 static int
nfscreate(int n,Rpccall * cmd,Rpccall * reply)395 nfscreate(int n, Rpccall *cmd, Rpccall *reply)
396 {
397 chat("create...");
398 return creat(n, cmd, reply, 0);
399 }
400
401 static int
remov(int n,Rpccall * cmd,Rpccall * reply)402 remov(int n, Rpccall *cmd, Rpccall *reply)
403 {
404 Session *s;
405 Xfile *xp;
406 Xfid *xf, *newxf;
407 String elem;
408 Fid *nfid;
409 uchar *argptr = cmd->args;
410 uchar *dataptr = reply->results;
411
412 if(n <= FHSIZE)
413 return garbage(reply, "count too small");
414 xf = rpc2xfid(cmd, 0);
415 argptr += FHSIZE;
416 argptr += string2S(argptr, &elem);
417 if(argptr != &((uchar *)cmd->args)[n])
418 return garbage(reply, "bad count");
419 if(xf == 0)
420 return error(reply, NFSERR_STALE);
421 xp = xf->xp;
422 if(!(xp->qid.type & QTDIR))
423 return error(reply, NFSERR_NOTDIR);
424 chat("%s/%.*s...", xp->name, utfnlen(elem.s, elem.n), elem.s);
425 if(xp->s->noauth == 0 && xp->parent == xp && elem.s[0] == '#')
426 return error(reply, NFSERR_PERM);
427 newxf = xfwalkcr(Twalk, xf, &elem, 0);
428 if(newxf == 0)
429 return error(reply, NFSERR_NOENT);
430 s = xp->s;
431 nfid = newfid(s);
432 setfid(s, newxf->urfid);
433 s->f.newfid = nfid - s->fids;
434 s->f.nwname = 0;
435 if(xmesg(s, Twalk) < 0){
436 putfid(s, nfid);
437 return error(reply, NFSERR_IO);
438 }
439 s->f.fid = nfid - s->fids;
440 if(xmesg(s, Tremove) < 0){
441 putfid(s, nfid);
442 return error(reply, NFSERR_PERM);
443 }
444 putfid(s, nfid);
445 xpclear(newxf->xp);
446 PLONG(NFS_OK);
447 chat("OK\n");
448 return dataptr - (uchar *)reply->results;
449 }
450
451 static int
nfsremove(int n,Rpccall * cmd,Rpccall * reply)452 nfsremove(int n, Rpccall *cmd, Rpccall *reply)
453 {
454 chat("remove...");
455 return remov(n, cmd, reply);
456 }
457
458 static int
nfsrename(int n,Rpccall * cmd,Rpccall * reply)459 nfsrename(int n, Rpccall *cmd, Rpccall *reply)
460 {
461 Xfid *xf, *newxf;
462 Xfile *xp;
463 uchar *fromdir, *todir;
464 String fromelem, toelem;
465 Dir dir;
466 uchar *argptr = cmd->args;
467 uchar *dataptr = reply->results;
468
469 chat("rename...");
470 if(n <= FHSIZE)
471 return garbage(reply, "count too small");
472 xf = rpc2xfid(cmd, 0);
473 fromdir = argptr;
474 argptr += FHSIZE;
475 argptr += string2S(argptr, &fromelem);
476 todir = argptr;
477 argptr += FHSIZE;
478 argptr += string2S(argptr, &toelem);
479 if(argptr != &((uchar *)cmd->args)[n])
480 return garbage(reply, "bad count");
481 if(xf == 0)
482 return error(reply, NFSERR_STALE);
483 xp = xf->xp;
484 if(!(xp->qid.type & QTDIR))
485 return error(reply, NFSERR_NOTDIR);
486 if(memcmp(fromdir, todir, FHSIZE) != 0)
487 return error(reply, NFSERR_NXIO);
488 newxf = xfwalkcr(Twalk, xf, &fromelem, 0);
489 if(newxf == 0)
490 return error(reply, NFSERR_NOENT);
491 if(xfstat(newxf, &dir) < 0)
492 return error(reply, NFSERR_IO);
493
494 if(xp->parent == xp && toelem.s[0] == '#')
495 return error(reply, NFSERR_PERM);
496 nulldir(&dir);
497 dir.name = toelem.s;
498 if(xfwstat(newxf, &dir) < 0)
499 return error(reply, NFSERR_PERM);
500 PLONG(NFS_OK);
501 chat("OK\n");
502 return dataptr - (uchar *)reply->results;
503 }
504
505 static int
nfslink(int n,Rpccall * cmd,Rpccall * reply)506 nfslink(int n, Rpccall *cmd, Rpccall *reply)
507 {
508 USED(n, reply);
509 chat("link...");
510 showauth(&cmd->cred);
511 return error(reply, NFSERR_NOENT);
512 }
513
514 static int
nfssymlink(int n,Rpccall * cmd,Rpccall * reply)515 nfssymlink(int n, Rpccall *cmd, Rpccall *reply)
516 {
517 USED(n, reply);
518 chat("symlink...");
519 showauth(&cmd->cred);
520 return error(reply, NFSERR_NOENT);
521 }
522
523 static int
nfsmkdir(int n,Rpccall * cmd,Rpccall * reply)524 nfsmkdir(int n, Rpccall *cmd, Rpccall *reply)
525 {
526 chat("mkdir...");
527 return creat(n, cmd, reply, DMDIR);
528 }
529
530 static int
nfsrmdir(int n,Rpccall * cmd,Rpccall * reply)531 nfsrmdir(int n, Rpccall *cmd, Rpccall *reply)
532 {
533 chat("rmdir...");
534 return remov(n, cmd, reply);
535 }
536
537 static int
nfsreaddir(int n,Rpccall * cmd,Rpccall * reply)538 nfsreaddir(int n, Rpccall *cmd, Rpccall *reply)
539 {
540 Session *s;
541 Xfid *xf;
542 Dir dir;
543 char *rdata;
544 int k, offset, count, sfcount, entries, dsize;
545 uchar *argptr = cmd->args;
546 uchar *dataptr = reply->results;
547
548 chat("readdir...");
549 if(n != FHSIZE+8)
550 return garbage(reply, "bad count");
551 xf = rpc2xfid(cmd, 0);
552 argptr += FHSIZE;
553 offset = GLONG();
554 count = GLONG();
555 if(xf == 0)
556 return error(reply, NFSERR_STALE);
557 chat("%s (%ld) %d %d...", xf->xp->name, xf->offset, offset, count);
558 s = xf->xp->s;
559 if((xf->mode & Open) && xf->offset > offset)
560 xfclose(xf);
561 if(xfopen(xf, Oread) < 0)
562 return error(reply, NFSERR_PERM);
563 while(xf->offset < offset){ /* if we reopened, xf->offset will be zero */
564 sfcount = offset - xf->offset;
565 if(sfcount > messagesize-IOHDRSZ)
566 sfcount = messagesize-IOHDRSZ;
567 setfid(s, xf->opfid);
568 s->f.offset = xf->offset;
569 s->f.count = sfcount;
570 if(xmesg(s, Tread) < 0){
571 xfclose(xf);
572 return error(reply, NFSERR_IO);
573 }
574 if(s->f.count <= BIT16SZ)
575 break;
576 xf->offset += s->f.count;
577 }
578 if(count > messagesize-IOHDRSZ)
579 count = messagesize-IOHDRSZ;
580 PLONG(NFS_OK);
581 entries = 0;
582 while(count > 16){ /* at least 16 bytes required; we don't know size of name */
583 chat("top of loop\n");
584 setfid(s, xf->opfid);
585 s->f.offset = xf->offset;
586 s->f.count = count; /* as good a guess as any */
587 if(xmesg(s, Tread) < 0){
588 xfclose(xf);
589 return error(reply, NFSERR_IO);
590 }
591 sfcount = s->f.count;
592 if(sfcount <= BIT16SZ)
593 break;
594 xf->offset += sfcount;
595 chat("count %d data 0x%p\n", s->f.count, s->f.data);
596 rdata = s->f.data;
597 /* now have a buffer of Plan 9 directories; unpack into NFS thingies */
598 while(sfcount >= 0){
599 dsize = convM2D((uchar*)rdata, sfcount, &dir, (char*)s->statbuf);
600 if(dsize <= BIT16SZ){
601 count = 0; /* force break from outer loop */
602 break;
603 }
604 offset += dsize;
605 k = strlen(dir.name);
606 if(count < 16+ROUNDUP(k)){
607 count = 0; /* force break from outer loop */
608 break;
609 }
610 PLONG(TRUE);
611 PLONG(dir.qid.path);
612 PLONG(k);
613 PPTR(dir.name, k);
614 PLONG(offset);
615 count -= 16+ROUNDUP(k);
616 rdata += dsize;
617 sfcount -= dsize;
618 }
619 }
620 PLONG(FALSE);
621 if(s->f.count <= 0){
622 xfclose(xf);
623 chat("eof...");
624 PLONG(TRUE);
625 }else
626 PLONG(FALSE);
627 chat("%d OK\n", entries);
628 return dataptr - (uchar *)reply->results;
629 }
630
631 static int
nfsstatfs(int n,Rpccall * cmd,Rpccall * reply)632 nfsstatfs(int n, Rpccall *cmd, Rpccall *reply)
633 {
634 uchar *dataptr = reply->results;
635 enum {
636 Xfersize = 2048,
637 Maxlong = (long)((1ULL<<31) - 1),
638 Maxfreeblks = Maxlong / Xfersize,
639 };
640
641 chat("statfs...");
642 showauth(&cmd->cred);
643 if(n != FHSIZE)
644 return garbage(reply, "bad count");
645 PLONG(NFS_OK);
646 PLONG(4096); /* tsize (fs block size) */
647 PLONG(Xfersize); /* bsize (optimal transfer size) */
648 PLONG(Maxfreeblks); /* blocks in fs */
649 PLONG(Maxfreeblks); /* bfree to root*/
650 PLONG(Maxfreeblks); /* bavail (free to mortals) */
651 chat("OK\n");
652 /*conftime = 0;
653 readunixidmaps(config);*/
654 return dataptr - (uchar *)reply->results;
655 }
656