xref: /plan9/sys/src/cmd/9nfs/nfsserver.c (revision 363b328d5e77ac1e503119274823363e6b14b616)
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