nfsxdr.c 12.2 KB
Newer Older
L
Linus Torvalds 已提交
1
/*
2
 * linux/fs/nfsd/nfsxdr.c
L
Linus Torvalds 已提交
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
 *
 * XDR support for nfsd
 *
 * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
 */

#include <linux/types.h>
#include <linux/time.h>
#include <linux/nfs.h>
#include <linux/vfs.h>
#include <linux/sunrpc/xdr.h>
#include <linux/sunrpc/svc.h>
#include <linux/nfsd/nfsd.h>
#include <linux/nfsd/xdr.h>
#include <linux/mm.h>

#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
 */
A
Al Viro 已提交
35 36
static __be32 *
decode_fh(__be32 *p, struct svc_fh *fhp)
L
Linus Torvalds 已提交
37 38 39 40 41 42 43 44 45 46
{
	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);
}

47
/* Helper function for NFSv2 ACL code */
A
Al Viro 已提交
48
__be32 *nfs2svc_decode_fh(__be32 *p, struct svc_fh *fhp)
49 50 51 52
{
	return decode_fh(p, fhp);
}

A
Adrian Bunk 已提交
53
static __be32 *
A
Al Viro 已提交
54
encode_fh(__be32 *p, struct svc_fh *fhp)
L
Linus Torvalds 已提交
55 56 57 58 59 60 61 62 63
{
	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.
 */
A
Adrian Bunk 已提交
64
static __be32 *
A
Al Viro 已提交
65
decode_filename(__be32 *p, char **namp, int *lenp)
L
Linus Torvalds 已提交
66 67 68 69 70 71 72 73 74 75 76 77 78 79
{
	char		*name;
	int		i;

	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;
}

A
Adrian Bunk 已提交
80
static __be32 *
A
Al Viro 已提交
81
decode_pathname(__be32 *p, char **namp, int *lenp)
L
Linus Torvalds 已提交
82 83 84 85 86 87 88 89 90 91 92 93 94 95
{
	char		*name;
	int		i;

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

	return p;
}

A
Adrian Bunk 已提交
96
static __be32 *
A
Al Viro 已提交
97
decode_sattr(__be32 *p, struct iattr *iap)
L
Linus Torvalds 已提交
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 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
{
	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) {
		iap->ia_valid |= ATTR_UID;
		iap->ia_uid = tmp;
	}
	if ((tmp = ntohl(*p++)) != (u32)-1) {
		iap->ia_valid |= ATTR_GID;
		iap->ia_gid = tmp;
	}
	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;
}

A
Al Viro 已提交
149 150
static __be32 *
encode_fattr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp,
151
	     struct kstat *stat)
L
Linus Torvalds 已提交
152 153 154 155 156
{
	struct dentry	*dentry = fhp->fh_dentry;
	int type;
	struct timespec time;

157
	type = (stat->mode & S_IFMT);
L
Linus Torvalds 已提交
158 159

	*p++ = htonl(nfs_ftypes[type >> 12]);
160 161 162 163
	*p++ = htonl((u32) stat->mode);
	*p++ = htonl((u32) stat->nlink);
	*p++ = htonl((u32) nfsd_ruid(rqstp, stat->uid));
	*p++ = htonl((u32) nfsd_rgid(rqstp, stat->gid));
L
Linus Torvalds 已提交
164

165
	if (S_ISLNK(type) && stat->size > NFS_MAXPATHLEN) {
L
Linus Torvalds 已提交
166 167
		*p++ = htonl(NFS_MAXPATHLEN);
	} else {
168
		*p++ = htonl((u32) stat->size);
L
Linus Torvalds 已提交
169
	}
170
	*p++ = htonl((u32) stat->blksize);
L
Linus Torvalds 已提交
171
	if (S_ISCHR(type) || S_ISBLK(type))
172
		*p++ = htonl(new_encode_dev(stat->rdev));
L
Linus Torvalds 已提交
173 174
	else
		*p++ = htonl(0xffffffff);
175
	*p++ = htonl((u32) stat->blocks);
L
Linus Torvalds 已提交
176 177 178
	if (is_fsid(fhp, rqstp->rq_reffh))
		*p++ = htonl((u32) fhp->fh_export->ex_fsid);
	else
179 180 181 182
		*p++ = htonl(new_encode_dev(stat->dev));
	*p++ = htonl((u32) stat->ino);
	*p++ = htonl((u32) stat->atime.tv_sec);
	*p++ = htonl(stat->atime.tv_nsec ? stat->atime.tv_nsec / 1000 : 0);
L
Linus Torvalds 已提交
183 184 185
	lease_get_mtime(dentry->d_inode, &time); 
	*p++ = htonl((u32) time.tv_sec);
	*p++ = htonl(time.tv_nsec ? time.tv_nsec / 1000 : 0); 
186 187
	*p++ = htonl((u32) stat->ctime.tv_sec);
	*p++ = htonl(stat->ctime.tv_nsec ? stat->ctime.tv_nsec / 1000 : 0);
L
Linus Torvalds 已提交
188 189 190 191

	return p;
}

192
/* Helper function for NFSv2 ACL code */
A
Al Viro 已提交
193
__be32 *nfs2svc_encode_fattr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp)
194
{
195 196 197
	struct kstat stat;
	vfs_getattr(fhp->fh_export->ex_mnt, fhp->fh_dentry, &stat);
	return encode_fattr(rqstp, p, fhp, &stat);
198
}
L
Linus Torvalds 已提交
199 200 201 202 203

/*
 * XDR decode functions
 */
int
A
Al Viro 已提交
204
nfssvc_decode_void(struct svc_rqst *rqstp, __be32 *p, void *dummy)
L
Linus Torvalds 已提交
205 206 207 208 209
{
	return xdr_argsize_check(rqstp, p);
}

int
A
Al Viro 已提交
210
nfssvc_decode_fhandle(struct svc_rqst *rqstp, __be32 *p, struct nfsd_fhandle *args)
L
Linus Torvalds 已提交
211 212 213 214 215 216 217
{
	if (!(p = decode_fh(p, &args->fh)))
		return 0;
	return xdr_argsize_check(rqstp, p);
}

int
A
Al Viro 已提交
218
nfssvc_decode_sattrargs(struct svc_rqst *rqstp, __be32 *p,
L
Linus Torvalds 已提交
219 220 221 222 223 224 225 226 227 228
					struct nfsd_sattrargs *args)
{
	if (!(p = decode_fh(p, &args->fh))
	 || !(p = decode_sattr(p, &args->attrs)))
		return 0;

	return xdr_argsize_check(rqstp, p);
}

int
A
Al Viro 已提交
229
nfssvc_decode_diropargs(struct svc_rqst *rqstp, __be32 *p,
L
Linus Torvalds 已提交
230 231 232 233 234 235 236 237 238 239
					struct nfsd_diropargs *args)
{
	if (!(p = decode_fh(p, &args->fh))
	 || !(p = decode_filename(p, &args->name, &args->len)))
		return 0;

	 return xdr_argsize_check(rqstp, p);
}

int
A
Al Viro 已提交
240
nfssvc_decode_readargs(struct svc_rqst *rqstp, __be32 *p,
L
Linus Torvalds 已提交
241 242 243 244 245 246 247 248 249 250 251
					struct nfsd_readargs *args)
{
	unsigned int len;
	int v,pn;
	if (!(p = decode_fh(p, &args->fh)))
		return 0;

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

252 253
	if (len > NFSSVC_MAXBLKSIZE_V2)
		len = NFSSVC_MAXBLKSIZE_V2;
L
Linus Torvalds 已提交
254 255 256 257 258 259

	/* set up somewhere to store response.
	 * We take pages, put them on reslist and include in iovec
	 */
	v=0;
	while (len > 0) {
260
		pn = rqstp->rq_resused++;
261 262 263
		rqstp->rq_vec[v].iov_base = page_address(rqstp->rq_respages[pn]);
		rqstp->rq_vec[v].iov_len = len < PAGE_SIZE?len:PAGE_SIZE;
		len -= rqstp->rq_vec[v].iov_len;
L
Linus Torvalds 已提交
264 265 266 267 268 269 270
		v++;
	}
	args->vlen = v;
	return xdr_argsize_check(rqstp, p);
}

int
A
Al Viro 已提交
271
nfssvc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p,
L
Linus Torvalds 已提交
272 273 274 275 276 277 278 279 280 281 282
					struct nfsd_writeargs *args)
{
	unsigned int len;
	int v;
	if (!(p = decode_fh(p, &args->fh)))
		return 0;

	p++;				/* beginoffset */
	args->offset = ntohl(*p++);	/* offset */
	p++;				/* totalcount */
	len = args->len = ntohl(*p++);
283 284
	rqstp->rq_vec[0].iov_base = (void*)p;
	rqstp->rq_vec[0].iov_len = rqstp->rq_arg.head[0].iov_len -
L
Linus Torvalds 已提交
285
				(((void*)p) - rqstp->rq_arg.head[0].iov_base);
286 287
	if (len > NFSSVC_MAXBLKSIZE_V2)
		len = NFSSVC_MAXBLKSIZE_V2;
L
Linus Torvalds 已提交
288
	v = 0;
289 290
	while (len > rqstp->rq_vec[v].iov_len) {
		len -= rqstp->rq_vec[v].iov_len;
L
Linus Torvalds 已提交
291
		v++;
292 293
		rqstp->rq_vec[v].iov_base = page_address(rqstp->rq_pages[v]);
		rqstp->rq_vec[v].iov_len = PAGE_SIZE;
L
Linus Torvalds 已提交
294
	}
295
	rqstp->rq_vec[v].iov_len = len;
L
Linus Torvalds 已提交
296
	args->vlen = v+1;
297
	return rqstp->rq_vec[0].iov_len > 0;
L
Linus Torvalds 已提交
298 299 300
}

int
A
Al Viro 已提交
301
nfssvc_decode_createargs(struct svc_rqst *rqstp, __be32 *p,
L
Linus Torvalds 已提交
302 303 304 305 306 307 308 309 310 311 312
					struct nfsd_createargs *args)
{
	if (!(p = decode_fh(p, &args->fh))
	 || !(p = decode_filename(p, &args->name, &args->len))
	 || !(p = decode_sattr(p, &args->attrs)))
		return 0;

	return xdr_argsize_check(rqstp, p);
}

int
A
Al Viro 已提交
313
nfssvc_decode_renameargs(struct svc_rqst *rqstp, __be32 *p,
L
Linus Torvalds 已提交
314 315 316 317 318 319 320 321 322 323 324 325
					struct nfsd_renameargs *args)
{
	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
A
Al Viro 已提交
326
nfssvc_decode_readlinkargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd_readlinkargs *args)
L
Linus Torvalds 已提交
327 328 329
{
	if (!(p = decode_fh(p, &args->fh)))
		return 0;
330
	args->buffer = page_address(rqstp->rq_respages[rqstp->rq_resused++]);
L
Linus Torvalds 已提交
331 332 333 334 335

	return xdr_argsize_check(rqstp, p);
}

int
A
Al Viro 已提交
336
nfssvc_decode_linkargs(struct svc_rqst *rqstp, __be32 *p,
L
Linus Torvalds 已提交
337 338 339 340 341 342 343 344 345 346 347
					struct nfsd_linkargs *args)
{
	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
A
Al Viro 已提交
348
nfssvc_decode_symlinkargs(struct svc_rqst *rqstp, __be32 *p,
L
Linus Torvalds 已提交
349 350 351 352 353 354 355 356 357 358 359 360
					struct nfsd_symlinkargs *args)
{
	if (!(p = decode_fh(p, &args->ffh))
	 || !(p = decode_filename(p, &args->fname, &args->flen))
	 || !(p = decode_pathname(p, &args->tname, &args->tlen))
	 || !(p = decode_sattr(p, &args->attrs)))
		return 0;

	return xdr_argsize_check(rqstp, p);
}

int
A
Al Viro 已提交
361
nfssvc_decode_readdirargs(struct svc_rqst *rqstp, __be32 *p,
L
Linus Torvalds 已提交
362 363 364 365 366 367 368 369 370
					struct nfsd_readdirargs *args)
{
	if (!(p = decode_fh(p, &args->fh)))
		return 0;
	args->cookie = ntohl(*p++);
	args->count  = ntohl(*p++);
	if (args->count > PAGE_SIZE)
		args->count = PAGE_SIZE;

371
	args->buffer = page_address(rqstp->rq_respages[rqstp->rq_resused++]);
L
Linus Torvalds 已提交
372 373 374 375 376 377 378 379

	return xdr_argsize_check(rqstp, p);
}

/*
 * XDR encode functions
 */
int
A
Al Viro 已提交
380
nfssvc_encode_void(struct svc_rqst *rqstp, __be32 *p, void *dummy)
L
Linus Torvalds 已提交
381 382 383 384 385
{
	return xdr_ressize_check(rqstp, p);
}

int
A
Al Viro 已提交
386
nfssvc_encode_attrstat(struct svc_rqst *rqstp, __be32 *p,
L
Linus Torvalds 已提交
387 388
					struct nfsd_attrstat *resp)
{
389
	p = encode_fattr(rqstp, p, &resp->fh, &resp->stat);
L
Linus Torvalds 已提交
390 391 392 393
	return xdr_ressize_check(rqstp, p);
}

int
A
Al Viro 已提交
394
nfssvc_encode_diropres(struct svc_rqst *rqstp, __be32 *p,
L
Linus Torvalds 已提交
395 396 397
					struct nfsd_diropres *resp)
{
	p = encode_fh(p, &resp->fh);
398
	p = encode_fattr(rqstp, p, &resp->fh, &resp->stat);
L
Linus Torvalds 已提交
399 400 401 402
	return xdr_ressize_check(rqstp, p);
}

int
A
Al Viro 已提交
403
nfssvc_encode_readlinkres(struct svc_rqst *rqstp, __be32 *p,
L
Linus Torvalds 已提交
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
					struct nfsd_readlinkres *resp)
{
	*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
A
Al Viro 已提交
419
nfssvc_encode_readres(struct svc_rqst *rqstp, __be32 *p,
L
Linus Torvalds 已提交
420 421
					struct nfsd_readres *resp)
{
422
	p = encode_fattr(rqstp, p, &resp->fh, &resp->stat);
L
Linus Torvalds 已提交
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437
	*p++ = htonl(resp->count);
	xdr_ressize_check(rqstp, p);

	/* now update rqstp->rq_res to reflect data aswell */
	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
A
Al Viro 已提交
438
nfssvc_encode_readdirres(struct svc_rqst *rqstp, __be32 *p,
L
Linus Torvalds 已提交
439 440 441 442 443 444 445 446 447 448 449 450
					struct nfsd_readdirres *resp)
{
	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
A
Al Viro 已提交
451
nfssvc_encode_statfsres(struct svc_rqst *rqstp, __be32 *p,
L
Linus Torvalds 已提交
452 453 454 455
					struct nfsd_statfsres *resp)
{
	struct kstatfs	*stat = &resp->stats;

456
	*p++ = htonl(NFSSVC_MAXBLKSIZE_V2);	/* max transfer size */
L
Linus Torvalds 已提交
457 458 459 460 461 462 463 464
	*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
465 466
nfssvc_encode_entry(void *ccdv, const char *name,
		    int namlen, loff_t offset, u64 ino, unsigned int d_type)
L
Linus Torvalds 已提交
467
{
468
	struct readdir_cd *ccd = ccdv;
L
Linus Torvalds 已提交
469
	struct nfsd_readdirres *cd = container_of(ccd, struct nfsd_readdirres, common);
A
Al Viro 已提交
470
	__be32	*p = cd->buffer;
L
Linus Torvalds 已提交
471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495
	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);
	if (namlen > NFS2_MAXNAMLEN)
		namlen = NFS2_MAXNAMLEN;/* truncate filename */

	slen = XDR_QUADLEN(namlen);
	if ((buflen = cd->buflen - slen - 4) < 0) {
		cd->common.err = nfserr_toosmall;
		return -EINVAL;
	}
	*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 */
A
Al Viro 已提交
496
	*p++ = htonl(~0U);		/* offset of next entry */
L
Linus Torvalds 已提交
497 498 499 500 501 502 503 504 505 506 507

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

/*
 * XDR release functions
 */
int
A
Al Viro 已提交
508
nfssvc_release_fhandle(struct svc_rqst *rqstp, __be32 *p,
L
Linus Torvalds 已提交
509 510 511 512 513
					struct nfsd_fhandle *resp)
{
	fh_put(&resp->fh);
	return 1;
}