Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
63
main(int argc, char *argv[])
64
{
65
	server(argc, argv, myport, progmap);
66
}
67
 
68
static 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
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
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
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
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
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
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
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
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
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
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
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
395
nfscreate(int n, Rpccall *cmd, Rpccall *reply)
396
{
397
	chat("create...");
398
	return creat(n, cmd, reply, 0);
399
}
400
 
401
static int
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
452
nfsremove(int n, Rpccall *cmd, Rpccall *reply)
453
{
454
	chat("remove...");
455
	return remov(n, cmd, reply);
456
}
457
 
458
static int
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
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
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
524
nfsmkdir(int n, Rpccall *cmd, Rpccall *reply)
525
{
526
	chat("mkdir...");
527
	return creat(n, cmd, reply, DMDIR);
528
}
529
 
530
static int
531
nfsrmdir(int n, Rpccall *cmd, Rpccall *reply)
532
{
533
	chat("rmdir...");
534
	return remov(n, cmd, reply);
535
}
536
 
537
static int
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
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
}