nfsxdr.c 13.5 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0
Linus Torvalds's avatar
Linus Torvalds committed
2 3 4 5 6 7
/*
 * XDR support for nfsd
 *
 * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
 */

Al Viro's avatar
Al Viro committed
8
#include "vfs.h"
9
#include "xdr.h"
10
#include "auth.h"
Linus Torvalds's avatar
Linus Torvalds committed
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

#define NFSDDBG_FACILITY		NFSDDBG_XDR

/*
 * Mapping of S_IF* types to NFS file types
 */
static u32	nfs_ftypes[] = {
	NFNON,  NFCHR,  NFCHR, NFBAD,
	NFDIR,  NFBAD,  NFBLK, NFBAD,
	NFREG,  NFBAD,  NFLNK, NFBAD,
	NFSOCK, NFBAD,  NFLNK, NFBAD,
};


/*
 * XDR functions for basic NFS types
 */
28 29
static __be32 *
decode_fh(__be32 *p, struct svc_fh *fhp)
Linus Torvalds's avatar
Linus Torvalds committed
30 31 32 33 34 35 36 37 38 39
{
	fh_init(fhp, NFS_FHSIZE);
	memcpy(&fhp->fh_handle.fh_base, p, NFS_FHSIZE);
	fhp->fh_handle.fh_size = NFS_FHSIZE;

	/* FIXME: Look up export pointer here and verify
	 * Sun Secure RPC if requested */
	return p + (NFS_FHSIZE >> 2);
}

40
/* Helper function for NFSv2 ACL code */
41
__be32 *nfs2svc_decode_fh(__be32 *p, struct svc_fh *fhp)
42 43 44 45
{
	return decode_fh(p, fhp);
}

46
static __be32 *
47
encode_fh(__be32 *p, struct svc_fh *fhp)
Linus Torvalds's avatar
Linus Torvalds committed
48 49 50 51 52 53 54 55 56
{
	memcpy(p, &fhp->fh_handle.fh_base, NFS_FHSIZE);
	return p + (NFS_FHSIZE>> 2);
}

/*
 * Decode a file name and make sure that the path contains
 * no slashes or null bytes.
 */
57
static __be32 *
58
decode_filename(__be32 *p, char **namp, unsigned int *lenp)
Linus Torvalds's avatar
Linus Torvalds committed
59 60
{
	char		*name;
61
	unsigned int	i;
Linus Torvalds's avatar
Linus Torvalds committed
62 63 64 65 66 67 68 69 70 71 72

	if ((p = xdr_decode_string_inplace(p, namp, lenp, NFS_MAXNAMLEN)) != NULL) {
		for (i = 0, name = *namp; i < *lenp; i++, name++) {
			if (*name == '\0' || *name == '/')
				return NULL;
		}
	}

	return p;
}

73
static __be32 *
74
decode_sattr(__be32 *p, struct iattr *iap, struct user_namespace *userns)
Linus Torvalds's avatar
Linus Torvalds committed
75 76 77 78 79 80 81 82 83 84 85 86 87 88
{
	u32	tmp, tmp1;

	iap->ia_valid = 0;

	/* Sun client bug compatibility check: some sun clients seem to
	 * put 0xffff in the mode field when they mean 0xffffffff.
	 * Quoting the 4.4BSD nfs server code: Nah nah nah nah na nah.
	 */
	if ((tmp = ntohl(*p++)) != (u32)-1 && tmp != 0xffff) {
		iap->ia_valid |= ATTR_MODE;
		iap->ia_mode = tmp;
	}
	if ((tmp = ntohl(*p++)) != (u32)-1) {
89
		iap->ia_uid = make_kuid(userns, tmp);
90 91
		if (uid_valid(iap->ia_uid))
			iap->ia_valid |= ATTR_UID;
Linus Torvalds's avatar
Linus Torvalds committed
92 93
	}
	if ((tmp = ntohl(*p++)) != (u32)-1) {
94
		iap->ia_gid = make_kgid(userns, tmp);
95 96
		if (gid_valid(iap->ia_gid))
			iap->ia_valid |= ATTR_GID;
Linus Torvalds's avatar
Linus Torvalds committed
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
	}
	if ((tmp = ntohl(*p++)) != (u32)-1) {
		iap->ia_valid |= ATTR_SIZE;
		iap->ia_size = tmp;
	}
	tmp  = ntohl(*p++); tmp1 = ntohl(*p++);
	if (tmp != (u32)-1 && tmp1 != (u32)-1) {
		iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET;
		iap->ia_atime.tv_sec = tmp;
		iap->ia_atime.tv_nsec = tmp1 * 1000; 
	}
	tmp  = ntohl(*p++); tmp1 = ntohl(*p++);
	if (tmp != (u32)-1 && tmp1 != (u32)-1) {
		iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET;
		iap->ia_mtime.tv_sec = tmp;
		iap->ia_mtime.tv_nsec = tmp1 * 1000; 
		/*
		 * Passing the invalid value useconds=1000000 for mtime
		 * is a Sun convention for "set both mtime and atime to
		 * current server time".  It's needed to make permissions
		 * checks for the "touch" program across v2 mounts to
		 * Solaris and Irix boxes work correctly. See description of
		 * sattr in section 6.1 of "NFS Illustrated" by
		 * Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5
		 */
		if (tmp1 == 1000000)
			iap->ia_valid &= ~(ATTR_ATIME_SET|ATTR_MTIME_SET);
	}
	return p;
}

128 129
static __be32 *
encode_fattr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp,
130
	     struct kstat *stat)
Linus Torvalds's avatar
Linus Torvalds committed
131
{
132
	struct user_namespace *userns = nfsd_user_namespace(rqstp);
Linus Torvalds's avatar
Linus Torvalds committed
133 134
	struct dentry	*dentry = fhp->fh_dentry;
	int type;
135
	struct timespec64 time;
136
	u32 f;
Linus Torvalds's avatar
Linus Torvalds committed
137

138
	type = (stat->mode & S_IFMT);
Linus Torvalds's avatar
Linus Torvalds committed
139 140

	*p++ = htonl(nfs_ftypes[type >> 12]);
141
	*p++ = htonl((u32) stat->mode);
142
	*p++ = htonl((u32) stat->nlink);
143 144
	*p++ = htonl((u32) from_kuid_munged(userns, stat->uid));
	*p++ = htonl((u32) from_kgid_munged(userns, stat->gid));
Linus Torvalds's avatar
Linus Torvalds committed
145

146
	if (S_ISLNK(type) && stat->size > NFS_MAXPATHLEN) {
Linus Torvalds's avatar
Linus Torvalds committed
147 148
		*p++ = htonl(NFS_MAXPATHLEN);
	} else {
149
		*p++ = htonl((u32) stat->size);
Linus Torvalds's avatar
Linus Torvalds committed
150
	}
151
	*p++ = htonl((u32) stat->blksize);
Linus Torvalds's avatar
Linus Torvalds committed
152
	if (S_ISCHR(type) || S_ISBLK(type))
153
		*p++ = htonl(new_encode_dev(stat->rdev));
Linus Torvalds's avatar
Linus Torvalds committed
154 155
	else
		*p++ = htonl(0xffffffff);
156
	*p++ = htonl((u32) stat->blocks);
157 158 159
	switch (fsid_source(fhp)) {
	default:
	case FSIDSOURCE_DEV:
160
		*p++ = htonl(new_encode_dev(stat->dev));
161 162 163 164 165 166 167 168 169 170 171 172
		break;
	case FSIDSOURCE_FSID:
		*p++ = htonl((u32) fhp->fh_export->ex_fsid);
		break;
	case FSIDSOURCE_UUID:
		f = ((u32*)fhp->fh_export->ex_uuid)[0];
		f ^= ((u32*)fhp->fh_export->ex_uuid)[1];
		f ^= ((u32*)fhp->fh_export->ex_uuid)[2];
		f ^= ((u32*)fhp->fh_export->ex_uuid)[3];
		*p++ = htonl(f);
		break;
	}
173 174 175
	*p++ = htonl((u32) stat->ino);
	*p++ = htonl((u32) stat->atime.tv_sec);
	*p++ = htonl(stat->atime.tv_nsec ? stat->atime.tv_nsec / 1000 : 0);
176
	time = stat->mtime;
177
	lease_get_mtime(d_inode(dentry), &time); 
Linus Torvalds's avatar
Linus Torvalds committed
178 179
	*p++ = htonl((u32) time.tv_sec);
	*p++ = htonl(time.tv_nsec ? time.tv_nsec / 1000 : 0); 
180 181
	*p++ = htonl((u32) stat->ctime.tv_sec);
	*p++ = htonl(stat->ctime.tv_nsec ? stat->ctime.tv_nsec / 1000 : 0);
Linus Torvalds's avatar
Linus Torvalds committed
182 183 184 185

	return p;
}

186
/* Helper function for NFSv2 ACL code */
187
__be32 *nfs2svc_encode_fattr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp, struct kstat *stat)
188
{
189
	return encode_fattr(rqstp, p, fhp, stat);
190
}
Linus Torvalds's avatar
Linus Torvalds committed
191 192 193 194 195

/*
 * XDR decode functions
 */
int
196
nfssvc_decode_void(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds's avatar
Linus Torvalds committed
197 198 199 200 201
{
	return xdr_argsize_check(rqstp, p);
}

int
202
nfssvc_decode_fhandle(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds's avatar
Linus Torvalds committed
203
{
204 205
	struct nfsd_fhandle *args = rqstp->rq_argp;

206 207
	p = decode_fh(p, &args->fh);
	if (!p)
Linus Torvalds's avatar
Linus Torvalds committed
208 209 210 211 212
		return 0;
	return xdr_argsize_check(rqstp, p);
}

int
213
nfssvc_decode_sattrargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds's avatar
Linus Torvalds committed
214
{
215 216
	struct nfsd_sattrargs *args = rqstp->rq_argp;

NeilBrown's avatar
NeilBrown committed
217 218
	p = decode_fh(p, &args->fh);
	if (!p)
Linus Torvalds's avatar
Linus Torvalds committed
219
		return 0;
220
	p = decode_sattr(p, &args->attrs, nfsd_user_namespace(rqstp));
Linus Torvalds's avatar
Linus Torvalds committed
221 222 223 224 225

	return xdr_argsize_check(rqstp, p);
}

int
226
nfssvc_decode_diropargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds's avatar
Linus Torvalds committed
227
{
228 229
	struct nfsd_diropargs *args = rqstp->rq_argp;

Linus Torvalds's avatar
Linus Torvalds committed
230 231 232 233
	if (!(p = decode_fh(p, &args->fh))
	 || !(p = decode_filename(p, &args->name, &args->len)))
		return 0;

234
	return xdr_argsize_check(rqstp, p);
Linus Torvalds's avatar
Linus Torvalds committed
235 236 237
}

int
238
nfssvc_decode_readargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds's avatar
Linus Torvalds committed
239
{
240
	struct nfsd_readargs *args = rqstp->rq_argp;
Linus Torvalds's avatar
Linus Torvalds committed
241
	unsigned int len;
242
	int v;
243 244
	p = decode_fh(p, &args->fh);
	if (!p)
Linus Torvalds's avatar
Linus Torvalds committed
245 246 247 248 249 250
		return 0;

	args->offset    = ntohl(*p++);
	len = args->count     = ntohl(*p++);
	p++; /* totalcount - unused */

251
	len = min_t(unsigned int, len, NFSSVC_MAXBLKSIZE_V2);
Linus Torvalds's avatar
Linus Torvalds committed
252 253 254 255 256 257

	/* set up somewhere to store response.
	 * We take pages, put them on reslist and include in iovec
	 */
	v=0;
	while (len > 0) {
258 259 260
		struct page *p = *(rqstp->rq_next_page++);

		rqstp->rq_vec[v].iov_base = page_address(p);
261
		rqstp->rq_vec[v].iov_len = min_t(unsigned int, len, PAGE_SIZE);
262
		len -= rqstp->rq_vec[v].iov_len;
Linus Torvalds's avatar
Linus Torvalds committed
263 264 265
		v++;
	}
	args->vlen = v;
266
	return xdr_argsize_check(rqstp, p);
Linus Torvalds's avatar
Linus Torvalds committed
267 268 269
}

int
270
nfssvc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds's avatar
Linus Torvalds committed
271
{
272
	struct nfsd_writeargs *args = rqstp->rq_argp;
273
	unsigned int len, hdr, dlen;
274
	struct kvec *head = rqstp->rq_arg.head;
275

276 277
	p = decode_fh(p, &args->fh);
	if (!p)
Linus Torvalds's avatar
Linus Torvalds committed
278 279 280 281 282 283
		return 0;

	p++;				/* beginoffset */
	args->offset = ntohl(*p++);	/* offset */
	p++;				/* totalcount */
	len = args->len = ntohl(*p++);
284 285 286
	/*
	 * The protocol specifies a maximum of 8192 bytes.
	 */
287
	if (len > NFSSVC_MAXBLKSIZE_V2)
288 289 290 291 292 293
		return 0;

	/*
	 * Check to make sure that we got the right number of
	 * bytes.
	 */
294
	hdr = (void*)p - head->iov_base;
295 296
	if (hdr > head->iov_len)
		return 0;
297
	dlen = head->iov_len + rqstp->rq_arg.page_len - hdr;
NeilBrown's avatar
NeilBrown committed
298

299 300 301 302
	/*
	 * Round the length of the data which was specified up to
	 * the next multiple of XDR units and then compare that
	 * against the length which was actually received.
303 304 305
	 * Note that when RPCSEC/GSS (for example) is used, the
	 * data buffer can be padded so dlen might be larger
	 * than required.  It must never be smaller.
306
	 */
307
	if (dlen < XDR_QUADLEN(len)*4)
308 309
		return 0;

310 311
	args->first.iov_base = (void *)p;
	args->first.iov_len = head->iov_len - hdr;
312
	return 1;
Linus Torvalds's avatar
Linus Torvalds committed
313 314 315
}

int
316
nfssvc_decode_createargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds's avatar
Linus Torvalds committed
317
{
318 319
	struct nfsd_createargs *args = rqstp->rq_argp;

NeilBrown's avatar
NeilBrown committed
320 321
	if (   !(p = decode_fh(p, &args->fh))
	    || !(p = decode_filename(p, &args->name, &args->len)))
Linus Torvalds's avatar
Linus Torvalds committed
322
		return 0;
323
	p = decode_sattr(p, &args->attrs, nfsd_user_namespace(rqstp));
Linus Torvalds's avatar
Linus Torvalds committed
324 325 326 327 328

	return xdr_argsize_check(rqstp, p);
}

int
329
nfssvc_decode_renameargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds's avatar
Linus Torvalds committed
330
{
331 332
	struct nfsd_renameargs *args = rqstp->rq_argp;

Linus Torvalds's avatar
Linus Torvalds committed
333 334 335 336 337 338 339 340 341 342
	if (!(p = decode_fh(p, &args->ffh))
	 || !(p = decode_filename(p, &args->fname, &args->flen))
	 || !(p = decode_fh(p, &args->tfh))
	 || !(p = decode_filename(p, &args->tname, &args->tlen)))
		return 0;

	return xdr_argsize_check(rqstp, p);
}

int
343
nfssvc_decode_readlinkargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds's avatar
Linus Torvalds committed
344
{
345 346
	struct nfsd_readlinkargs *args = rqstp->rq_argp;

347 348
	p = decode_fh(p, &args->fh);
	if (!p)
Linus Torvalds's avatar
Linus Torvalds committed
349
		return 0;
350
	args->buffer = page_address(*(rqstp->rq_next_page++));
Linus Torvalds's avatar
Linus Torvalds committed
351

352
	return xdr_argsize_check(rqstp, p);
Linus Torvalds's avatar
Linus Torvalds committed
353 354 355
}

int
356
nfssvc_decode_linkargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds's avatar
Linus Torvalds committed
357
{
358 359
	struct nfsd_linkargs *args = rqstp->rq_argp;

Linus Torvalds's avatar
Linus Torvalds committed
360 361 362 363 364 365 366 367 368
	if (!(p = decode_fh(p, &args->ffh))
	 || !(p = decode_fh(p, &args->tfh))
	 || !(p = decode_filename(p, &args->tname, &args->tlen)))
		return 0;

	return xdr_argsize_check(rqstp, p);
}

int
369
nfssvc_decode_symlinkargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds's avatar
Linus Torvalds committed
370
{
371
	struct nfsd_symlinkargs *args = rqstp->rq_argp;
372 373
	char *base = (char *)p;
	size_t xdrlen;
374

NeilBrown's avatar
NeilBrown committed
375
	if (   !(p = decode_fh(p, &args->ffh))
376
	    || !(p = decode_filename(p, &args->fname, &args->flen)))
Linus Torvalds's avatar
Linus Torvalds committed
377 378
		return 0;

379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401
	args->tlen = ntohl(*p++);
	if (args->tlen == 0)
		return 0;

	args->first.iov_base = p;
	args->first.iov_len = rqstp->rq_arg.head[0].iov_len;
	args->first.iov_len -= (char *)p - base;

	/* This request is never larger than a page. Therefore,
	 * transport will deliver either:
	 * 1. pathname in the pagelist -> sattr is in the tail.
	 * 2. everything in the head buffer -> sattr is in the head.
	 */
	if (rqstp->rq_arg.page_len) {
		if (args->tlen != rqstp->rq_arg.page_len)
			return 0;
		p = rqstp->rq_arg.tail[0].iov_base;
	} else {
		xdrlen = XDR_QUADLEN(args->tlen);
		if (xdrlen > args->first.iov_len - (8 * sizeof(__be32)))
			return 0;
		p += xdrlen;
	}
402
	decode_sattr(p, &args->attrs, nfsd_user_namespace(rqstp));
403 404

	return 1;
Linus Torvalds's avatar
Linus Torvalds committed
405 406 407
}

int
408
nfssvc_decode_readdirargs(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds's avatar
Linus Torvalds committed
409
{
410 411
	struct nfsd_readdirargs *args = rqstp->rq_argp;

412 413
	p = decode_fh(p, &args->fh);
	if (!p)
Linus Torvalds's avatar
Linus Torvalds committed
414 415 416
		return 0;
	args->cookie = ntohl(*p++);
	args->count  = ntohl(*p++);
417
	args->count  = min_t(u32, args->count, PAGE_SIZE);
418
	args->buffer = page_address(*(rqstp->rq_next_page++));
Linus Torvalds's avatar
Linus Torvalds committed
419

420
	return xdr_argsize_check(rqstp, p);
Linus Torvalds's avatar
Linus Torvalds committed
421 422 423 424 425 426
}

/*
 * XDR encode functions
 */
int
427
nfssvc_encode_void(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds's avatar
Linus Torvalds committed
428 429 430 431 432
{
	return xdr_ressize_check(rqstp, p);
}

int
433
nfssvc_encode_attrstat(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds's avatar
Linus Torvalds committed
434
{
435 436
	struct nfsd_attrstat *resp = rqstp->rq_resp;

437
	p = encode_fattr(rqstp, p, &resp->fh, &resp->stat);
Linus Torvalds's avatar
Linus Torvalds committed
438 439 440 441
	return xdr_ressize_check(rqstp, p);
}

int
442
nfssvc_encode_diropres(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds's avatar
Linus Torvalds committed
443
{
444 445
	struct nfsd_diropres *resp = rqstp->rq_resp;

Linus Torvalds's avatar
Linus Torvalds committed
446
	p = encode_fh(p, &resp->fh);
447
	p = encode_fattr(rqstp, p, &resp->fh, &resp->stat);
Linus Torvalds's avatar
Linus Torvalds committed
448 449 450 451
	return xdr_ressize_check(rqstp, p);
}

int
452
nfssvc_encode_readlinkres(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds's avatar
Linus Torvalds committed
453
{
454 455
	struct nfsd_readlinkres *resp = rqstp->rq_resp;

Linus Torvalds's avatar
Linus Torvalds committed
456 457 458 459 460 461 462 463 464 465 466 467 468
	*p++ = htonl(resp->len);
	xdr_ressize_check(rqstp, p);
	rqstp->rq_res.page_len = resp->len;
	if (resp->len & 3) {
		/* need to pad the tail */
		rqstp->rq_res.tail[0].iov_base = p;
		*p = 0;
		rqstp->rq_res.tail[0].iov_len = 4 - (resp->len&3);
	}
	return 1;
}

int
469
nfssvc_encode_readres(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds's avatar
Linus Torvalds committed
470
{
471 472
	struct nfsd_readres *resp = rqstp->rq_resp;

473
	p = encode_fattr(rqstp, p, &resp->fh, &resp->stat);
Linus Torvalds's avatar
Linus Torvalds committed
474 475 476
	*p++ = htonl(resp->count);
	xdr_ressize_check(rqstp, p);

Lucas De Marchi's avatar
Lucas De Marchi committed
477
	/* now update rqstp->rq_res to reflect data as well */
Linus Torvalds's avatar
Linus Torvalds committed
478 479 480 481 482 483 484 485 486 487 488
	rqstp->rq_res.page_len = resp->count;
	if (resp->count & 3) {
		/* need to pad the tail */
		rqstp->rq_res.tail[0].iov_base = p;
		*p = 0;
		rqstp->rq_res.tail[0].iov_len = 4 - (resp->count&3);
	}
	return 1;
}

int
489
nfssvc_encode_readdirres(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds's avatar
Linus Torvalds committed
490
{
491 492
	struct nfsd_readdirres *resp = rqstp->rq_resp;

Linus Torvalds's avatar
Linus Torvalds committed
493 494 495 496 497 498 499 500 501 502
	xdr_ressize_check(rqstp, p);
	p = resp->buffer;
	*p++ = 0;			/* no more entries */
	*p++ = htonl((resp->common.err == nfserr_eof));
	rqstp->rq_res.page_len = (((unsigned long)p-1) & ~PAGE_MASK)+1;

	return 1;
}

int
503
nfssvc_encode_statfsres(struct svc_rqst *rqstp, __be32 *p)
Linus Torvalds's avatar
Linus Torvalds committed
504
{
505
	struct nfsd_statfsres *resp = rqstp->rq_resp;
Linus Torvalds's avatar
Linus Torvalds committed
506 507
	struct kstatfs	*stat = &resp->stats;

508
	*p++ = htonl(NFSSVC_MAXBLKSIZE_V2);	/* max transfer size */
Linus Torvalds's avatar
Linus Torvalds committed
509 510 511 512 513 514 515 516
	*p++ = htonl(stat->f_bsize);
	*p++ = htonl(stat->f_blocks);
	*p++ = htonl(stat->f_bfree);
	*p++ = htonl(stat->f_bavail);
	return xdr_ressize_check(rqstp, p);
}

int
517 518
nfssvc_encode_entry(void *ccdv, const char *name,
		    int namlen, loff_t offset, u64 ino, unsigned int d_type)
Linus Torvalds's avatar
Linus Torvalds committed
519
{
520
	struct readdir_cd *ccd = ccdv;
Linus Torvalds's avatar
Linus Torvalds committed
521
	struct nfsd_readdirres *cd = container_of(ccd, struct nfsd_readdirres, common);
522
	__be32	*p = cd->buffer;
Linus Torvalds's avatar
Linus Torvalds committed
523 524 525 526 527 528 529 530 531 532 533 534 535 536
	int	buflen, slen;

	/*
	dprintk("nfsd: entry(%.*s off %ld ino %ld)\n",
			namlen, name, offset, ino);
	 */

	if (offset > ~((u32) 0)) {
		cd->common.err = nfserr_fbig;
		return -EINVAL;
	}
	if (cd->offset)
		*cd->offset = htonl(offset);

537 538
	/* truncate filename */
	namlen = min(namlen, NFS2_MAXNAMLEN);
Linus Torvalds's avatar
Linus Torvalds committed
539
	slen = XDR_QUADLEN(namlen);
540

Linus Torvalds's avatar
Linus Torvalds committed
541 542 543 544
	if ((buflen = cd->buflen - slen - 4) < 0) {
		cd->common.err = nfserr_toosmall;
		return -EINVAL;
	}
545 546 547 548
	if (ino > ~((u32) 0)) {
		cd->common.err = nfserr_fbig;
		return -EINVAL;
	}
Linus Torvalds's avatar
Linus Torvalds committed
549 550 551 552
	*p++ = xdr_one;				/* mark entry present */
	*p++ = htonl((u32) ino);		/* file id */
	p    = xdr_encode_array(p, name, namlen);/* name length & name */
	cd->offset = p;			/* remember pointer */
553
	*p++ = htonl(~0U);		/* offset of next entry */
Linus Torvalds's avatar
Linus Torvalds committed
554 555 556 557 558 559 560 561 562 563

	cd->buflen = buflen;
	cd->buffer = p;
	cd->common.err = nfs_ok;
	return 0;
}

/*
 * XDR release functions
 */
564 565
void
nfssvc_release_fhandle(struct svc_rqst *rqstp)
Linus Torvalds's avatar
Linus Torvalds committed
566
{
567 568
	struct nfsd_fhandle *resp = rqstp->rq_resp;

Linus Torvalds's avatar
Linus Torvalds committed
569 570
	fh_put(&resp->fh);
}