2005-04-17 02:20:36 +04:00
|
|
|
/*
|
|
|
|
* linux/fs/nfs/nfs3xdr.c
|
|
|
|
*
|
|
|
|
* XDR functions to encode/decode NFSv3 RPC arguments and results.
|
|
|
|
*
|
|
|
|
* Copyright (C) 1996, 1997 Olaf Kirch
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/param.h>
|
|
|
|
#include <linux/time.h>
|
|
|
|
#include <linux/mm.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/utsname.h>
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/string.h>
|
|
|
|
#include <linux/in.h>
|
|
|
|
#include <linux/pagemap.h>
|
|
|
|
#include <linux/proc_fs.h>
|
|
|
|
#include <linux/kdev_t.h>
|
|
|
|
#include <linux/sunrpc/clnt.h>
|
|
|
|
#include <linux/nfs.h>
|
|
|
|
#include <linux/nfs3.h>
|
|
|
|
#include <linux/nfs_fs.h>
|
2005-06-22 21:16:27 +04:00
|
|
|
#include <linux/nfsacl.h>
|
NFS: Split fs/nfs/inode.c
As fs/nfs/inode.c is rather large, heterogenous and unwieldy, the attached
patch splits it up into a number of files:
(*) fs/nfs/inode.c
Strictly inode specific functions.
(*) fs/nfs/super.c
Superblock management functions for NFS and NFS4, normal access, clones
and referrals. The NFS4 superblock functions _could_ move out into a
separate conditionally compiled file, but it's probably not worth it as
there're so many common bits.
(*) fs/nfs/namespace.c
Some namespace-specific functions have been moved here.
(*) fs/nfs/nfs4namespace.c
NFS4-specific namespace functions (this could be merged into the previous
file). This file is conditionally compiled.
(*) fs/nfs/internal.h
Inter-file declarations, plus a few simple utility functions moved from
fs/nfs/inode.c.
Additionally, all the in-.c-file externs have been moved here, and those
files they were moved from now includes this file.
For the most part, the functions have not been changed, only some multiplexor
functions have changed significantly.
I've also:
(*) Added some extra banner comments above some functions.
(*) Rearranged the function order within the files to be more logical and
better grouped (IMO), though someone may prefer a different order.
(*) Reduced the number of #ifdefs in .c files.
(*) Added missing __init and __exit directives.
Signed-Off-By: David Howells <dhowells@redhat.com>
2006-06-09 17:34:33 +04:00
|
|
|
#include "internal.h"
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
#define NFSDBG_FACILITY NFSDBG_XDR
|
|
|
|
|
|
|
|
/* Mapping from NFS error code to "errno" error code. */
|
|
|
|
#define errno_NFSERR_IO EIO
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Declare the space requirements for NFS arguments and replies as
|
|
|
|
* number of 32bit-words
|
|
|
|
*/
|
|
|
|
#define NFS3_fhandle_sz (1+16)
|
|
|
|
#define NFS3_fh_sz (NFS3_fhandle_sz) /* shorthand */
|
|
|
|
#define NFS3_sattr_sz (15)
|
|
|
|
#define NFS3_filename_sz (1+(NFS3_MAXNAMLEN>>2))
|
|
|
|
#define NFS3_path_sz (1+(NFS3_MAXPATHLEN>>2))
|
|
|
|
#define NFS3_fattr_sz (21)
|
|
|
|
#define NFS3_wcc_attr_sz (6)
|
|
|
|
#define NFS3_pre_op_attr_sz (1+NFS3_wcc_attr_sz)
|
|
|
|
#define NFS3_post_op_attr_sz (1+NFS3_fattr_sz)
|
|
|
|
#define NFS3_wcc_data_sz (NFS3_pre_op_attr_sz+NFS3_post_op_attr_sz)
|
|
|
|
#define NFS3_fsstat_sz
|
|
|
|
#define NFS3_fsinfo_sz
|
|
|
|
#define NFS3_pathconf_sz
|
|
|
|
#define NFS3_entry_sz (NFS3_filename_sz+3)
|
|
|
|
|
|
|
|
#define NFS3_sattrargs_sz (NFS3_fh_sz+NFS3_sattr_sz+3)
|
|
|
|
#define NFS3_diropargs_sz (NFS3_fh_sz+NFS3_filename_sz)
|
2007-07-14 23:39:57 +04:00
|
|
|
#define NFS3_removeargs_sz (NFS3_fh_sz+NFS3_filename_sz)
|
2005-04-17 02:20:36 +04:00
|
|
|
#define NFS3_accessargs_sz (NFS3_fh_sz+1)
|
|
|
|
#define NFS3_readlinkargs_sz (NFS3_fh_sz)
|
|
|
|
#define NFS3_readargs_sz (NFS3_fh_sz+3)
|
|
|
|
#define NFS3_writeargs_sz (NFS3_fh_sz+5)
|
|
|
|
#define NFS3_createargs_sz (NFS3_diropargs_sz+NFS3_sattr_sz)
|
|
|
|
#define NFS3_mkdirargs_sz (NFS3_diropargs_sz+NFS3_sattr_sz)
|
2006-08-23 04:06:23 +04:00
|
|
|
#define NFS3_symlinkargs_sz (NFS3_diropargs_sz+1+NFS3_sattr_sz)
|
2005-04-17 02:20:36 +04:00
|
|
|
#define NFS3_mknodargs_sz (NFS3_diropargs_sz+2+NFS3_sattr_sz)
|
|
|
|
#define NFS3_renameargs_sz (NFS3_diropargs_sz+NFS3_diropargs_sz)
|
|
|
|
#define NFS3_linkargs_sz (NFS3_fh_sz+NFS3_diropargs_sz)
|
|
|
|
#define NFS3_readdirargs_sz (NFS3_fh_sz+2)
|
|
|
|
#define NFS3_commitargs_sz (NFS3_fh_sz+3)
|
|
|
|
|
|
|
|
#define NFS3_attrstat_sz (1+NFS3_fattr_sz)
|
|
|
|
#define NFS3_wccstat_sz (1+NFS3_wcc_data_sz)
|
2007-07-14 23:39:57 +04:00
|
|
|
#define NFS3_removeres_sz (NFS3_wccstat_sz)
|
2005-04-17 02:20:36 +04:00
|
|
|
#define NFS3_lookupres_sz (1+NFS3_fh_sz+(2 * NFS3_post_op_attr_sz))
|
|
|
|
#define NFS3_accessres_sz (1+NFS3_post_op_attr_sz+1)
|
|
|
|
#define NFS3_readlinkres_sz (1+NFS3_post_op_attr_sz+1)
|
|
|
|
#define NFS3_readres_sz (1+NFS3_post_op_attr_sz+3)
|
|
|
|
#define NFS3_writeres_sz (1+NFS3_wcc_data_sz+4)
|
|
|
|
#define NFS3_createres_sz (1+NFS3_fh_sz+NFS3_post_op_attr_sz+NFS3_wcc_data_sz)
|
|
|
|
#define NFS3_renameres_sz (1+(2 * NFS3_wcc_data_sz))
|
|
|
|
#define NFS3_linkres_sz (1+NFS3_post_op_attr_sz+NFS3_wcc_data_sz)
|
|
|
|
#define NFS3_readdirres_sz (1+NFS3_post_op_attr_sz+2)
|
|
|
|
#define NFS3_fsstatres_sz (1+NFS3_post_op_attr_sz+13)
|
|
|
|
#define NFS3_fsinfores_sz (1+NFS3_post_op_attr_sz+12)
|
|
|
|
#define NFS3_pathconfres_sz (1+NFS3_post_op_attr_sz+6)
|
|
|
|
#define NFS3_commitres_sz (1+NFS3_wcc_data_sz+2)
|
|
|
|
|
2005-06-22 21:16:27 +04:00
|
|
|
#define ACL3_getaclargs_sz (NFS3_fh_sz+1)
|
|
|
|
#define ACL3_setaclargs_sz (NFS3_fh_sz+1+2*(2+5*3))
|
|
|
|
#define ACL3_getaclres_sz (1+NFS3_post_op_attr_sz+1+2*(2+5*3))
|
|
|
|
#define ACL3_setaclres_sz (1+NFS3_post_op_attr_sz)
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/*
|
|
|
|
* Map file type to S_IFMT bits
|
|
|
|
*/
|
|
|
|
static struct {
|
|
|
|
unsigned int mode;
|
|
|
|
unsigned int nfs2type;
|
|
|
|
} nfs_type2fmt[] = {
|
|
|
|
{ 0, NFNON },
|
|
|
|
{ S_IFREG, NFREG },
|
|
|
|
{ S_IFDIR, NFDIR },
|
|
|
|
{ S_IFBLK, NFBLK },
|
|
|
|
{ S_IFCHR, NFCHR },
|
|
|
|
{ S_IFLNK, NFLNK },
|
|
|
|
{ S_IFSOCK, NFSOCK },
|
|
|
|
{ S_IFIFO, NFFIFO },
|
|
|
|
{ 0, NFBAD }
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Common NFS XDR functions as inlines
|
|
|
|
*/
|
2006-10-20 10:28:48 +04:00
|
|
|
static inline __be32 *
|
2007-07-14 23:39:57 +04:00
|
|
|
xdr_encode_fhandle(__be32 *p, const struct nfs_fh *fh)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
return xdr_encode_array(p, fh->data, fh->size);
|
|
|
|
}
|
|
|
|
|
2006-10-20 10:28:48 +04:00
|
|
|
static inline __be32 *
|
|
|
|
xdr_decode_fhandle(__be32 *p, struct nfs_fh *fh)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
if ((fh->size = ntohl(*p++)) <= NFS3_FHSIZE) {
|
|
|
|
memcpy(fh->data, p, fh->size);
|
|
|
|
return p + XDR_QUADLEN(fh->size);
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Encode/decode time.
|
|
|
|
*/
|
2006-10-20 10:28:48 +04:00
|
|
|
static inline __be32 *
|
|
|
|
xdr_encode_time3(__be32 *p, struct timespec *timep)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
*p++ = htonl(timep->tv_sec);
|
|
|
|
*p++ = htonl(timep->tv_nsec);
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2006-10-20 10:28:48 +04:00
|
|
|
static inline __be32 *
|
|
|
|
xdr_decode_time3(__be32 *p, struct timespec *timep)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
timep->tv_sec = ntohl(*p++);
|
|
|
|
timep->tv_nsec = ntohl(*p++);
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2006-10-20 10:28:48 +04:00
|
|
|
static __be32 *
|
|
|
|
xdr_decode_fattr(__be32 *p, struct nfs_fattr *fattr)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
unsigned int type, major, minor;
|
|
|
|
int fmode;
|
|
|
|
|
|
|
|
type = ntohl(*p++);
|
|
|
|
if (type >= NF3BAD)
|
|
|
|
type = NF3BAD;
|
|
|
|
fmode = nfs_type2fmt[type].mode;
|
|
|
|
fattr->type = nfs_type2fmt[type].nfs2type;
|
|
|
|
fattr->mode = (ntohl(*p++) & ~S_IFMT) | fmode;
|
|
|
|
fattr->nlink = ntohl(*p++);
|
|
|
|
fattr->uid = ntohl(*p++);
|
|
|
|
fattr->gid = ntohl(*p++);
|
|
|
|
p = xdr_decode_hyper(p, &fattr->size);
|
|
|
|
p = xdr_decode_hyper(p, &fattr->du.nfs3.used);
|
|
|
|
|
|
|
|
/* Turn remote device info into Linux-specific dev_t */
|
|
|
|
major = ntohl(*p++);
|
|
|
|
minor = ntohl(*p++);
|
|
|
|
fattr->rdev = MKDEV(major, minor);
|
|
|
|
if (MAJOR(fattr->rdev) != major || MINOR(fattr->rdev) != minor)
|
|
|
|
fattr->rdev = 0;
|
|
|
|
|
2006-06-09 17:34:19 +04:00
|
|
|
p = xdr_decode_hyper(p, &fattr->fsid.major);
|
|
|
|
fattr->fsid.minor = 0;
|
2005-04-17 02:20:36 +04:00
|
|
|
p = xdr_decode_hyper(p, &fattr->fileid);
|
|
|
|
p = xdr_decode_time3(p, &fattr->atime);
|
|
|
|
p = xdr_decode_time3(p, &fattr->mtime);
|
|
|
|
p = xdr_decode_time3(p, &fattr->ctime);
|
|
|
|
|
|
|
|
/* Update the mode bits */
|
|
|
|
fattr->valid |= (NFS_ATTR_FATTR | NFS_ATTR_FATTR_V3);
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2006-10-20 10:28:48 +04:00
|
|
|
static inline __be32 *
|
|
|
|
xdr_encode_sattr(__be32 *p, struct iattr *attr)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
if (attr->ia_valid & ATTR_MODE) {
|
|
|
|
*p++ = xdr_one;
|
2006-01-03 11:55:53 +03:00
|
|
|
*p++ = htonl(attr->ia_mode & S_IALLUGO);
|
2005-04-17 02:20:36 +04:00
|
|
|
} else {
|
|
|
|
*p++ = xdr_zero;
|
|
|
|
}
|
|
|
|
if (attr->ia_valid & ATTR_UID) {
|
|
|
|
*p++ = xdr_one;
|
|
|
|
*p++ = htonl(attr->ia_uid);
|
|
|
|
} else {
|
|
|
|
*p++ = xdr_zero;
|
|
|
|
}
|
|
|
|
if (attr->ia_valid & ATTR_GID) {
|
|
|
|
*p++ = xdr_one;
|
|
|
|
*p++ = htonl(attr->ia_gid);
|
|
|
|
} else {
|
|
|
|
*p++ = xdr_zero;
|
|
|
|
}
|
|
|
|
if (attr->ia_valid & ATTR_SIZE) {
|
|
|
|
*p++ = xdr_one;
|
|
|
|
p = xdr_encode_hyper(p, (__u64) attr->ia_size);
|
|
|
|
} else {
|
|
|
|
*p++ = xdr_zero;
|
|
|
|
}
|
|
|
|
if (attr->ia_valid & ATTR_ATIME_SET) {
|
|
|
|
*p++ = xdr_two;
|
|
|
|
p = xdr_encode_time3(p, &attr->ia_atime);
|
|
|
|
} else if (attr->ia_valid & ATTR_ATIME) {
|
|
|
|
*p++ = xdr_one;
|
|
|
|
} else {
|
|
|
|
*p++ = xdr_zero;
|
|
|
|
}
|
|
|
|
if (attr->ia_valid & ATTR_MTIME_SET) {
|
|
|
|
*p++ = xdr_two;
|
|
|
|
p = xdr_encode_time3(p, &attr->ia_mtime);
|
|
|
|
} else if (attr->ia_valid & ATTR_MTIME) {
|
|
|
|
*p++ = xdr_one;
|
|
|
|
} else {
|
|
|
|
*p++ = xdr_zero;
|
|
|
|
}
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2006-10-20 10:28:48 +04:00
|
|
|
static inline __be32 *
|
|
|
|
xdr_decode_wcc_attr(__be32 *p, struct nfs_fattr *fattr)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
p = xdr_decode_hyper(p, &fattr->pre_size);
|
|
|
|
p = xdr_decode_time3(p, &fattr->pre_mtime);
|
|
|
|
p = xdr_decode_time3(p, &fattr->pre_ctime);
|
|
|
|
fattr->valid |= NFS_ATTR_WCC;
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2006-10-20 10:28:48 +04:00
|
|
|
static inline __be32 *
|
|
|
|
xdr_decode_post_op_attr(__be32 *p, struct nfs_fattr *fattr)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
if (*p++)
|
|
|
|
p = xdr_decode_fattr(p, fattr);
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2006-10-20 10:28:48 +04:00
|
|
|
static inline __be32 *
|
|
|
|
xdr_decode_pre_op_attr(__be32 *p, struct nfs_fattr *fattr)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
if (*p++)
|
|
|
|
return xdr_decode_wcc_attr(p, fattr);
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-10-20 10:28:48 +04:00
|
|
|
static inline __be32 *
|
|
|
|
xdr_decode_wcc_data(__be32 *p, struct nfs_fattr *fattr)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
p = xdr_decode_pre_op_attr(p, fattr);
|
|
|
|
return xdr_decode_post_op_attr(p, fattr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* NFS encode functions
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Encode file handle argument
|
|
|
|
*/
|
|
|
|
static int
|
2006-10-20 10:28:48 +04:00
|
|
|
nfs3_xdr_fhandle(struct rpc_rqst *req, __be32 *p, struct nfs_fh *fh)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
p = xdr_encode_fhandle(p, fh);
|
|
|
|
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Encode SETATTR arguments
|
|
|
|
*/
|
|
|
|
static int
|
2006-10-20 10:28:48 +04:00
|
|
|
nfs3_xdr_sattrargs(struct rpc_rqst *req, __be32 *p, struct nfs3_sattrargs *args)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
p = xdr_encode_fhandle(p, args->fh);
|
|
|
|
p = xdr_encode_sattr(p, args->sattr);
|
|
|
|
*p++ = htonl(args->guard);
|
|
|
|
if (args->guard)
|
|
|
|
p = xdr_encode_time3(p, &args->guardtime);
|
|
|
|
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Encode directory ops argument
|
|
|
|
*/
|
|
|
|
static int
|
2006-10-20 10:28:48 +04:00
|
|
|
nfs3_xdr_diropargs(struct rpc_rqst *req, __be32 *p, struct nfs3_diropargs *args)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
p = xdr_encode_fhandle(p, args->fh);
|
|
|
|
p = xdr_encode_array(p, args->name, args->len);
|
|
|
|
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-07-14 23:39:57 +04:00
|
|
|
/*
|
|
|
|
* Encode REMOVE argument
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
nfs3_xdr_removeargs(struct rpc_rqst *req, __be32 *p, const struct nfs_removeargs *args)
|
|
|
|
{
|
|
|
|
p = xdr_encode_fhandle(p, args->fh);
|
|
|
|
p = xdr_encode_array(p, args->name.name, args->name.len);
|
|
|
|
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/*
|
|
|
|
* Encode access() argument
|
|
|
|
*/
|
|
|
|
static int
|
2006-10-20 10:28:48 +04:00
|
|
|
nfs3_xdr_accessargs(struct rpc_rqst *req, __be32 *p, struct nfs3_accessargs *args)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
p = xdr_encode_fhandle(p, args->fh);
|
|
|
|
*p++ = htonl(args->access);
|
|
|
|
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Arguments to a READ call. Since we read data directly into the page
|
|
|
|
* cache, we also set up the reply iovec here so that iov[1] points
|
|
|
|
* exactly to the page we want to fetch.
|
|
|
|
*/
|
|
|
|
static int
|
2006-10-20 10:28:48 +04:00
|
|
|
nfs3_xdr_readargs(struct rpc_rqst *req, __be32 *p, struct nfs_readargs *args)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2007-06-27 22:29:04 +04:00
|
|
|
struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
|
2005-04-17 02:20:36 +04:00
|
|
|
unsigned int replen;
|
|
|
|
u32 count = args->count;
|
|
|
|
|
|
|
|
p = xdr_encode_fhandle(p, args->fh);
|
|
|
|
p = xdr_encode_hyper(p, args->offset);
|
|
|
|
*p++ = htonl(count);
|
|
|
|
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
|
|
|
|
|
|
|
|
/* Inline the page array */
|
|
|
|
replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readres_sz) << 2;
|
|
|
|
xdr_inline_pages(&req->rq_rcv_buf, replen,
|
|
|
|
args->pages, args->pgbase, count);
|
2007-09-10 21:44:58 +04:00
|
|
|
req->rq_rcv_buf.flags |= XDRBUF_READ;
|
2005-04-17 02:20:36 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Write arguments. Splice the buffer to be written into the iovec.
|
|
|
|
*/
|
|
|
|
static int
|
2006-10-20 10:28:48 +04:00
|
|
|
nfs3_xdr_writeargs(struct rpc_rqst *req, __be32 *p, struct nfs_writeargs *args)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct xdr_buf *sndbuf = &req->rq_snd_buf;
|
|
|
|
u32 count = args->count;
|
|
|
|
|
|
|
|
p = xdr_encode_fhandle(p, args->fh);
|
|
|
|
p = xdr_encode_hyper(p, args->offset);
|
|
|
|
*p++ = htonl(count);
|
|
|
|
*p++ = htonl(args->stable);
|
|
|
|
*p++ = htonl(count);
|
|
|
|
sndbuf->len = xdr_adjust_iovec(sndbuf->head, p);
|
|
|
|
|
|
|
|
/* Copy the page array */
|
|
|
|
xdr_encode_pages(sndbuf, args->pages, args->pgbase, count);
|
2007-09-10 21:44:58 +04:00
|
|
|
sndbuf->flags |= XDRBUF_WRITE;
|
2005-04-17 02:20:36 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Encode CREATE arguments
|
|
|
|
*/
|
|
|
|
static int
|
2006-10-20 10:28:48 +04:00
|
|
|
nfs3_xdr_createargs(struct rpc_rqst *req, __be32 *p, struct nfs3_createargs *args)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
p = xdr_encode_fhandle(p, args->fh);
|
|
|
|
p = xdr_encode_array(p, args->name, args->len);
|
|
|
|
|
|
|
|
*p++ = htonl(args->createmode);
|
|
|
|
if (args->createmode == NFS3_CREATE_EXCLUSIVE) {
|
|
|
|
*p++ = args->verifier[0];
|
|
|
|
*p++ = args->verifier[1];
|
|
|
|
} else
|
|
|
|
p = xdr_encode_sattr(p, args->sattr);
|
|
|
|
|
|
|
|
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Encode MKDIR arguments
|
|
|
|
*/
|
|
|
|
static int
|
2006-10-20 10:28:48 +04:00
|
|
|
nfs3_xdr_mkdirargs(struct rpc_rqst *req, __be32 *p, struct nfs3_mkdirargs *args)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
p = xdr_encode_fhandle(p, args->fh);
|
|
|
|
p = xdr_encode_array(p, args->name, args->len);
|
|
|
|
p = xdr_encode_sattr(p, args->sattr);
|
|
|
|
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Encode SYMLINK arguments
|
|
|
|
*/
|
|
|
|
static int
|
2006-10-20 10:28:48 +04:00
|
|
|
nfs3_xdr_symlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_symlinkargs *args)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
p = xdr_encode_fhandle(p, args->fromfh);
|
|
|
|
p = xdr_encode_array(p, args->fromname, args->fromlen);
|
|
|
|
p = xdr_encode_sattr(p, args->sattr);
|
2006-08-23 04:06:23 +04:00
|
|
|
*p++ = htonl(args->pathlen);
|
2005-04-17 02:20:36 +04:00
|
|
|
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
|
2006-08-23 04:06:23 +04:00
|
|
|
|
|
|
|
/* Copy the page */
|
|
|
|
xdr_encode_pages(&req->rq_snd_buf, args->pages, 0, args->pathlen);
|
2005-04-17 02:20:36 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Encode MKNOD arguments
|
|
|
|
*/
|
|
|
|
static int
|
2006-10-20 10:28:48 +04:00
|
|
|
nfs3_xdr_mknodargs(struct rpc_rqst *req, __be32 *p, struct nfs3_mknodargs *args)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
p = xdr_encode_fhandle(p, args->fh);
|
|
|
|
p = xdr_encode_array(p, args->name, args->len);
|
|
|
|
*p++ = htonl(args->type);
|
|
|
|
p = xdr_encode_sattr(p, args->sattr);
|
|
|
|
if (args->type == NF3CHR || args->type == NF3BLK) {
|
|
|
|
*p++ = htonl(MAJOR(args->rdev));
|
|
|
|
*p++ = htonl(MINOR(args->rdev));
|
|
|
|
}
|
|
|
|
|
|
|
|
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Encode RENAME arguments
|
|
|
|
*/
|
|
|
|
static int
|
2006-10-20 10:28:48 +04:00
|
|
|
nfs3_xdr_renameargs(struct rpc_rqst *req, __be32 *p, struct nfs3_renameargs *args)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
p = xdr_encode_fhandle(p, args->fromfh);
|
|
|
|
p = xdr_encode_array(p, args->fromname, args->fromlen);
|
|
|
|
p = xdr_encode_fhandle(p, args->tofh);
|
|
|
|
p = xdr_encode_array(p, args->toname, args->tolen);
|
|
|
|
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Encode LINK arguments
|
|
|
|
*/
|
|
|
|
static int
|
2006-10-20 10:28:48 +04:00
|
|
|
nfs3_xdr_linkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_linkargs *args)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
p = xdr_encode_fhandle(p, args->fromfh);
|
|
|
|
p = xdr_encode_fhandle(p, args->tofh);
|
|
|
|
p = xdr_encode_array(p, args->toname, args->tolen);
|
|
|
|
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Encode arguments to readdir call
|
|
|
|
*/
|
|
|
|
static int
|
2006-10-20 10:28:48 +04:00
|
|
|
nfs3_xdr_readdirargs(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirargs *args)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2007-06-27 22:29:04 +04:00
|
|
|
struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
|
2005-04-17 02:20:36 +04:00
|
|
|
unsigned int replen;
|
|
|
|
u32 count = args->count;
|
|
|
|
|
|
|
|
p = xdr_encode_fhandle(p, args->fh);
|
|
|
|
p = xdr_encode_hyper(p, args->cookie);
|
|
|
|
*p++ = args->verf[0];
|
|
|
|
*p++ = args->verf[1];
|
|
|
|
if (args->plus) {
|
|
|
|
/* readdirplus: need dircount + buffer size.
|
|
|
|
* We just make sure we make dircount big enough */
|
|
|
|
*p++ = htonl(count >> 3);
|
|
|
|
}
|
|
|
|
*p++ = htonl(count);
|
|
|
|
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
|
|
|
|
|
|
|
|
/* Inline the page array */
|
|
|
|
replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readdirres_sz) << 2;
|
|
|
|
xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0, count);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Decode the result of a readdir call.
|
|
|
|
* We just check for syntactical correctness.
|
|
|
|
*/
|
|
|
|
static int
|
2006-10-20 10:28:48 +04:00
|
|
|
nfs3_xdr_readdirres(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirres *res)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
|
|
|
|
struct kvec *iov = rcvbuf->head;
|
|
|
|
struct page **page;
|
2007-10-26 21:31:57 +04:00
|
|
|
size_t hdrlen;
|
|
|
|
u32 len, recvd, pglen;
|
2005-04-17 02:20:36 +04:00
|
|
|
int status, nr;
|
2006-10-20 10:28:48 +04:00
|
|
|
__be32 *entry, *end, *kaddr;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
status = ntohl(*p++);
|
|
|
|
/* Decode post_op_attrs */
|
|
|
|
p = xdr_decode_post_op_attr(p, res->dir_attr);
|
|
|
|
if (status)
|
|
|
|
return -nfs_stat_to_errno(status);
|
|
|
|
/* Decode verifier cookie */
|
|
|
|
if (res->verf) {
|
|
|
|
res->verf[0] = *p++;
|
|
|
|
res->verf[1] = *p++;
|
|
|
|
} else {
|
|
|
|
p += 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
hdrlen = (u8 *) p - (u8 *) iov->iov_base;
|
|
|
|
if (iov->iov_len < hdrlen) {
|
2007-09-12 02:01:10 +04:00
|
|
|
dprintk("NFS: READDIR reply header overflowed:"
|
2007-10-26 21:31:57 +04:00
|
|
|
"length %Zu > %Zu\n", hdrlen, iov->iov_len);
|
2005-04-17 02:20:36 +04:00
|
|
|
return -errno_NFSERR_IO;
|
|
|
|
} else if (iov->iov_len != hdrlen) {
|
|
|
|
dprintk("NFS: READDIR header is short. iovec will be shifted.\n");
|
|
|
|
xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
|
|
|
|
}
|
|
|
|
|
|
|
|
pglen = rcvbuf->page_len;
|
|
|
|
recvd = rcvbuf->len - hdrlen;
|
|
|
|
if (pglen > recvd)
|
|
|
|
pglen = recvd;
|
|
|
|
page = rcvbuf->pages;
|
2006-10-20 10:28:48 +04:00
|
|
|
kaddr = p = kmap_atomic(*page, KM_USER0);
|
|
|
|
end = (__be32 *)((char *)p + pglen);
|
2005-04-17 02:20:36 +04:00
|
|
|
entry = p;
|
|
|
|
for (nr = 0; *p++; nr++) {
|
|
|
|
if (p + 3 > end)
|
|
|
|
goto short_pkt;
|
|
|
|
p += 2; /* inode # */
|
|
|
|
len = ntohl(*p++); /* string length */
|
|
|
|
p += XDR_QUADLEN(len) + 2; /* name + cookie */
|
|
|
|
if (len > NFS3_MAXNAMLEN) {
|
2007-10-26 21:31:57 +04:00
|
|
|
dprintk("NFS: giant filename in readdir (len 0x%x)!\n",
|
2005-04-17 02:20:36 +04:00
|
|
|
len);
|
|
|
|
goto err_unmap;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (res->plus) {
|
|
|
|
/* post_op_attr */
|
|
|
|
if (p + 2 > end)
|
|
|
|
goto short_pkt;
|
|
|
|
if (*p++) {
|
|
|
|
p += 21;
|
|
|
|
if (p + 1 > end)
|
|
|
|
goto short_pkt;
|
|
|
|
}
|
|
|
|
/* post_op_fh3 */
|
|
|
|
if (*p++) {
|
|
|
|
if (p + 1 > end)
|
|
|
|
goto short_pkt;
|
|
|
|
len = ntohl(*p++);
|
|
|
|
if (len > NFS3_FHSIZE) {
|
2007-09-12 02:01:10 +04:00
|
|
|
dprintk("NFS: giant filehandle in "
|
2007-10-26 21:31:57 +04:00
|
|
|
"readdir (len 0x%x)!\n", len);
|
2005-04-17 02:20:36 +04:00
|
|
|
goto err_unmap;
|
|
|
|
}
|
|
|
|
p += XDR_QUADLEN(len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p + 2 > end)
|
|
|
|
goto short_pkt;
|
|
|
|
entry = p;
|
|
|
|
}
|
|
|
|
if (!nr && (entry[0] != 0 || entry[1] == 0))
|
|
|
|
goto short_pkt;
|
|
|
|
out:
|
|
|
|
kunmap_atomic(kaddr, KM_USER0);
|
|
|
|
return nr;
|
|
|
|
short_pkt:
|
|
|
|
entry[0] = entry[1] = 0;
|
|
|
|
/* truncate listing ? */
|
|
|
|
if (!nr) {
|
2007-09-12 02:01:10 +04:00
|
|
|
dprintk("NFS: readdir reply truncated!\n");
|
2005-04-17 02:20:36 +04:00
|
|
|
entry[1] = 1;
|
|
|
|
}
|
|
|
|
goto out;
|
|
|
|
err_unmap:
|
|
|
|
nr = -errno_NFSERR_IO;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2006-10-20 10:28:49 +04:00
|
|
|
__be32 *
|
|
|
|
nfs3_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct nfs_entry old = *entry;
|
|
|
|
|
|
|
|
if (!*p++) {
|
|
|
|
if (!*p)
|
|
|
|
return ERR_PTR(-EAGAIN);
|
|
|
|
entry->eof = 1;
|
|
|
|
return ERR_PTR(-EBADCOOKIE);
|
|
|
|
}
|
|
|
|
|
|
|
|
p = xdr_decode_hyper(p, &entry->ino);
|
|
|
|
entry->len = ntohl(*p++);
|
|
|
|
entry->name = (const char *) p;
|
|
|
|
p += XDR_QUADLEN(entry->len);
|
|
|
|
entry->prev_cookie = entry->cookie;
|
|
|
|
p = xdr_decode_hyper(p, &entry->cookie);
|
|
|
|
|
|
|
|
if (plus) {
|
|
|
|
entry->fattr->valid = 0;
|
|
|
|
p = xdr_decode_post_op_attr(p, entry->fattr);
|
|
|
|
/* In fact, a post_op_fh3: */
|
|
|
|
if (*p++) {
|
|
|
|
p = xdr_decode_fhandle(p, entry->fh);
|
|
|
|
/* Ugh -- server reply was truncated */
|
|
|
|
if (p == NULL) {
|
|
|
|
dprintk("NFS: FH truncated\n");
|
|
|
|
*entry = old;
|
|
|
|
return ERR_PTR(-EAGAIN);
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
memset((u8*)(entry->fh), 0, sizeof(*entry->fh));
|
|
|
|
}
|
|
|
|
|
|
|
|
entry->eof = !p[0] && p[1];
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Encode COMMIT arguments
|
|
|
|
*/
|
|
|
|
static int
|
2006-10-20 10:28:48 +04:00
|
|
|
nfs3_xdr_commitargs(struct rpc_rqst *req, __be32 *p, struct nfs_writeargs *args)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
p = xdr_encode_fhandle(p, args->fh);
|
|
|
|
p = xdr_encode_hyper(p, args->offset);
|
|
|
|
*p++ = htonl(args->count);
|
|
|
|
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-06-22 21:16:27 +04:00
|
|
|
#ifdef CONFIG_NFS_V3_ACL
|
|
|
|
/*
|
|
|
|
* Encode GETACL arguments
|
|
|
|
*/
|
|
|
|
static int
|
2006-10-20 10:28:48 +04:00
|
|
|
nfs3_xdr_getaclargs(struct rpc_rqst *req, __be32 *p,
|
2005-06-22 21:16:27 +04:00
|
|
|
struct nfs3_getaclargs *args)
|
|
|
|
{
|
2007-06-27 22:29:04 +04:00
|
|
|
struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
|
2005-06-22 21:16:27 +04:00
|
|
|
unsigned int replen;
|
|
|
|
|
|
|
|
p = xdr_encode_fhandle(p, args->fh);
|
|
|
|
*p++ = htonl(args->mask);
|
|
|
|
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
|
|
|
|
|
|
|
|
if (args->mask & (NFS_ACL | NFS_DFACL)) {
|
|
|
|
/* Inline the page array */
|
|
|
|
replen = (RPC_REPHDRSIZE + auth->au_rslack +
|
|
|
|
ACL3_getaclres_sz) << 2;
|
|
|
|
xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0,
|
|
|
|
NFSACL_MAXPAGES << PAGE_SHIFT);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Encode SETACL arguments
|
|
|
|
*/
|
|
|
|
static int
|
2006-10-20 10:28:48 +04:00
|
|
|
nfs3_xdr_setaclargs(struct rpc_rqst *req, __be32 *p,
|
2005-06-22 21:16:27 +04:00
|
|
|
struct nfs3_setaclargs *args)
|
|
|
|
{
|
|
|
|
struct xdr_buf *buf = &req->rq_snd_buf;
|
|
|
|
unsigned int base, len_in_head, len = nfsacl_size(
|
|
|
|
(args->mask & NFS_ACL) ? args->acl_access : NULL,
|
|
|
|
(args->mask & NFS_DFACL) ? args->acl_default : NULL);
|
|
|
|
int count, err;
|
|
|
|
|
|
|
|
p = xdr_encode_fhandle(p, NFS_FH(args->inode));
|
|
|
|
*p++ = htonl(args->mask);
|
|
|
|
base = (char *)p - (char *)buf->head->iov_base;
|
|
|
|
/* put as much of the acls into head as possible. */
|
|
|
|
len_in_head = min_t(unsigned int, buf->head->iov_len - base, len);
|
|
|
|
len -= len_in_head;
|
2005-06-22 21:16:28 +04:00
|
|
|
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p + (len_in_head >> 2));
|
2005-06-22 21:16:27 +04:00
|
|
|
|
|
|
|
for (count = 0; (count << PAGE_SHIFT) < len; count++) {
|
|
|
|
args->pages[count] = alloc_page(GFP_KERNEL);
|
|
|
|
if (!args->pages[count]) {
|
|
|
|
while (count)
|
|
|
|
__free_page(args->pages[--count]);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
xdr_encode_pages(buf, args->pages, 0, len);
|
|
|
|
|
|
|
|
err = nfsacl_encode(buf, base, args->inode,
|
|
|
|
(args->mask & NFS_ACL) ?
|
|
|
|
args->acl_access : NULL, 1, 0);
|
|
|
|
if (err > 0)
|
|
|
|
err = nfsacl_encode(buf, base + err, args->inode,
|
|
|
|
(args->mask & NFS_DFACL) ?
|
|
|
|
args->acl_default : NULL, 1,
|
|
|
|
NFS_ACL_DEFAULT);
|
|
|
|
return (err > 0) ? 0 : err;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_NFS_V3_ACL */
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/*
|
|
|
|
* NFS XDR decode functions
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Decode attrstat reply.
|
|
|
|
*/
|
|
|
|
static int
|
2006-10-20 10:28:48 +04:00
|
|
|
nfs3_xdr_attrstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
int status;
|
|
|
|
|
|
|
|
if ((status = ntohl(*p++)))
|
|
|
|
return -nfs_stat_to_errno(status);
|
|
|
|
xdr_decode_fattr(p, fattr);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Decode status+wcc_data reply
|
|
|
|
* SATTR, REMOVE, RMDIR
|
|
|
|
*/
|
|
|
|
static int
|
2006-10-20 10:28:48 +04:00
|
|
|
nfs3_xdr_wccstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
int status;
|
|
|
|
|
|
|
|
if ((status = ntohl(*p++)))
|
|
|
|
status = -nfs_stat_to_errno(status);
|
|
|
|
xdr_decode_wcc_data(p, fattr);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2007-07-14 23:39:57 +04:00
|
|
|
static int
|
|
|
|
nfs3_xdr_removeres(struct rpc_rqst *req, __be32 *p, struct nfs_removeres *res)
|
|
|
|
{
|
|
|
|
return nfs3_xdr_wccstat(req, p, &res->dir_attr);
|
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/*
|
|
|
|
* Decode LOOKUP reply
|
|
|
|
*/
|
|
|
|
static int
|
2006-10-20 10:28:48 +04:00
|
|
|
nfs3_xdr_lookupres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
int status;
|
|
|
|
|
|
|
|
if ((status = ntohl(*p++))) {
|
|
|
|
status = -nfs_stat_to_errno(status);
|
|
|
|
} else {
|
|
|
|
if (!(p = xdr_decode_fhandle(p, res->fh)))
|
|
|
|
return -errno_NFSERR_IO;
|
|
|
|
p = xdr_decode_post_op_attr(p, res->fattr);
|
|
|
|
}
|
|
|
|
xdr_decode_post_op_attr(p, res->dir_attr);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Decode ACCESS reply
|
|
|
|
*/
|
|
|
|
static int
|
2006-10-20 10:28:48 +04:00
|
|
|
nfs3_xdr_accessres(struct rpc_rqst *req, __be32 *p, struct nfs3_accessres *res)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
int status = ntohl(*p++);
|
|
|
|
|
|
|
|
p = xdr_decode_post_op_attr(p, res->fattr);
|
|
|
|
if (status)
|
|
|
|
return -nfs_stat_to_errno(status);
|
|
|
|
res->access = ntohl(*p++);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2006-10-20 10:28:48 +04:00
|
|
|
nfs3_xdr_readlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_readlinkargs *args)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2007-06-27 22:29:04 +04:00
|
|
|
struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
|
2005-04-17 02:20:36 +04:00
|
|
|
unsigned int replen;
|
|
|
|
|
|
|
|
p = xdr_encode_fhandle(p, args->fh);
|
|
|
|
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
|
|
|
|
|
|
|
|
/* Inline the page array */
|
|
|
|
replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readlinkres_sz) << 2;
|
|
|
|
xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, args->pgbase, args->pglen);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Decode READLINK reply
|
|
|
|
*/
|
|
|
|
static int
|
2006-10-20 10:28:48 +04:00
|
|
|
nfs3_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
|
|
|
|
struct kvec *iov = rcvbuf->head;
|
2007-10-26 21:31:57 +04:00
|
|
|
size_t hdrlen;
|
|
|
|
u32 len, recvd;
|
2005-04-17 02:20:36 +04:00
|
|
|
char *kaddr;
|
|
|
|
int status;
|
|
|
|
|
|
|
|
status = ntohl(*p++);
|
|
|
|
p = xdr_decode_post_op_attr(p, fattr);
|
|
|
|
|
|
|
|
if (status != 0)
|
|
|
|
return -nfs_stat_to_errno(status);
|
|
|
|
|
|
|
|
/* Convert length of symlink */
|
|
|
|
len = ntohl(*p++);
|
2007-10-26 21:31:57 +04:00
|
|
|
if (len >= rcvbuf->page_len) {
|
2007-09-12 02:01:10 +04:00
|
|
|
dprintk("nfs: server returned giant symlink!\n");
|
2005-04-17 02:20:36 +04:00
|
|
|
return -ENAMETOOLONG;
|
|
|
|
}
|
|
|
|
|
|
|
|
hdrlen = (u8 *) p - (u8 *) iov->iov_base;
|
|
|
|
if (iov->iov_len < hdrlen) {
|
2007-09-12 02:01:10 +04:00
|
|
|
dprintk("NFS: READLINK reply header overflowed:"
|
2007-10-26 21:31:57 +04:00
|
|
|
"length %Zu > %Zu\n", hdrlen, iov->iov_len);
|
2005-04-17 02:20:36 +04:00
|
|
|
return -errno_NFSERR_IO;
|
|
|
|
} else if (iov->iov_len != hdrlen) {
|
2007-09-12 02:01:10 +04:00
|
|
|
dprintk("NFS: READLINK header is short. "
|
|
|
|
"iovec will be shifted.\n");
|
2005-04-17 02:20:36 +04:00
|
|
|
xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
|
|
|
|
}
|
|
|
|
recvd = req->rq_rcv_buf.len - hdrlen;
|
|
|
|
if (recvd < len) {
|
2007-09-12 02:01:10 +04:00
|
|
|
dprintk("NFS: server cheating in readlink reply: "
|
2005-04-17 02:20:36 +04:00
|
|
|
"count %u > recvd %u\n", len, recvd);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* NULL terminate the string we got */
|
|
|
|
kaddr = (char*)kmap_atomic(rcvbuf->pages[0], KM_USER0);
|
|
|
|
kaddr[len+rcvbuf->page_base] = '\0';
|
|
|
|
kunmap_atomic(kaddr, KM_USER0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Decode READ reply
|
|
|
|
*/
|
|
|
|
static int
|
2006-10-20 10:28:48 +04:00
|
|
|
nfs3_xdr_readres(struct rpc_rqst *req, __be32 *p, struct nfs_readres *res)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct kvec *iov = req->rq_rcv_buf.head;
|
2007-10-26 21:31:57 +04:00
|
|
|
size_t hdrlen;
|
|
|
|
u32 count, ocount, recvd;
|
|
|
|
int status;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
status = ntohl(*p++);
|
|
|
|
p = xdr_decode_post_op_attr(p, res->fattr);
|
|
|
|
|
|
|
|
if (status != 0)
|
|
|
|
return -nfs_stat_to_errno(status);
|
|
|
|
|
2007-10-26 21:31:57 +04:00
|
|
|
/* Decode reply count and EOF flag. NFSv3 is somewhat redundant
|
2005-04-17 02:20:36 +04:00
|
|
|
* in that it puts the count both in the res struct and in the
|
|
|
|
* opaque data count. */
|
|
|
|
count = ntohl(*p++);
|
|
|
|
res->eof = ntohl(*p++);
|
|
|
|
ocount = ntohl(*p++);
|
|
|
|
|
|
|
|
if (ocount != count) {
|
2007-09-12 02:01:10 +04:00
|
|
|
dprintk("NFS: READ count doesn't match RPC opaque count.\n");
|
2005-04-17 02:20:36 +04:00
|
|
|
return -errno_NFSERR_IO;
|
|
|
|
}
|
|
|
|
|
|
|
|
hdrlen = (u8 *) p - (u8 *) iov->iov_base;
|
|
|
|
if (iov->iov_len < hdrlen) {
|
2007-09-12 02:01:10 +04:00
|
|
|
dprintk("NFS: READ reply header overflowed:"
|
2007-10-26 21:31:57 +04:00
|
|
|
"length %Zu > %Zu\n", hdrlen, iov->iov_len);
|
2005-04-17 02:20:36 +04:00
|
|
|
return -errno_NFSERR_IO;
|
|
|
|
} else if (iov->iov_len != hdrlen) {
|
|
|
|
dprintk("NFS: READ header is short. iovec will be shifted.\n");
|
|
|
|
xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen);
|
|
|
|
}
|
|
|
|
|
|
|
|
recvd = req->rq_rcv_buf.len - hdrlen;
|
|
|
|
if (count > recvd) {
|
2007-09-12 02:01:10 +04:00
|
|
|
dprintk("NFS: server cheating in read reply: "
|
2007-10-26 21:31:57 +04:00
|
|
|
"count %u > recvd %u\n", count, recvd);
|
2005-04-17 02:20:36 +04:00
|
|
|
count = recvd;
|
|
|
|
res->eof = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (count < res->count)
|
|
|
|
res->count = count;
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Decode WRITE response
|
|
|
|
*/
|
|
|
|
static int
|
2006-10-20 10:28:48 +04:00
|
|
|
nfs3_xdr_writeres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
int status;
|
|
|
|
|
|
|
|
status = ntohl(*p++);
|
|
|
|
p = xdr_decode_wcc_data(p, res->fattr);
|
|
|
|
|
|
|
|
if (status != 0)
|
|
|
|
return -nfs_stat_to_errno(status);
|
|
|
|
|
|
|
|
res->count = ntohl(*p++);
|
|
|
|
res->verf->committed = (enum nfs3_stable_how)ntohl(*p++);
|
|
|
|
res->verf->verifier[0] = *p++;
|
|
|
|
res->verf->verifier[1] = *p++;
|
|
|
|
|
|
|
|
return res->count;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Decode a CREATE response
|
|
|
|
*/
|
|
|
|
static int
|
2006-10-20 10:28:48 +04:00
|
|
|
nfs3_xdr_createres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
int status;
|
|
|
|
|
|
|
|
status = ntohl(*p++);
|
|
|
|
if (status == 0) {
|
|
|
|
if (*p++) {
|
|
|
|
if (!(p = xdr_decode_fhandle(p, res->fh)))
|
|
|
|
return -errno_NFSERR_IO;
|
|
|
|
p = xdr_decode_post_op_attr(p, res->fattr);
|
|
|
|
} else {
|
|
|
|
memset(res->fh, 0, sizeof(*res->fh));
|
|
|
|
/* Do decode post_op_attr but set it to NULL */
|
|
|
|
p = xdr_decode_post_op_attr(p, res->fattr);
|
|
|
|
res->fattr->valid = 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
status = -nfs_stat_to_errno(status);
|
|
|
|
}
|
|
|
|
p = xdr_decode_wcc_data(p, res->dir_attr);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Decode RENAME reply
|
|
|
|
*/
|
|
|
|
static int
|
2006-10-20 10:28:48 +04:00
|
|
|
nfs3_xdr_renameres(struct rpc_rqst *req, __be32 *p, struct nfs3_renameres *res)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
int status;
|
|
|
|
|
|
|
|
if ((status = ntohl(*p++)) != 0)
|
|
|
|
status = -nfs_stat_to_errno(status);
|
|
|
|
p = xdr_decode_wcc_data(p, res->fromattr);
|
|
|
|
p = xdr_decode_wcc_data(p, res->toattr);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Decode LINK reply
|
|
|
|
*/
|
|
|
|
static int
|
2006-10-20 10:28:48 +04:00
|
|
|
nfs3_xdr_linkres(struct rpc_rqst *req, __be32 *p, struct nfs3_linkres *res)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
int status;
|
|
|
|
|
|
|
|
if ((status = ntohl(*p++)) != 0)
|
|
|
|
status = -nfs_stat_to_errno(status);
|
|
|
|
p = xdr_decode_post_op_attr(p, res->fattr);
|
|
|
|
p = xdr_decode_wcc_data(p, res->dir_attr);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Decode FSSTAT reply
|
|
|
|
*/
|
|
|
|
static int
|
2006-10-20 10:28:48 +04:00
|
|
|
nfs3_xdr_fsstatres(struct rpc_rqst *req, __be32 *p, struct nfs_fsstat *res)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
int status;
|
|
|
|
|
|
|
|
status = ntohl(*p++);
|
|
|
|
|
|
|
|
p = xdr_decode_post_op_attr(p, res->fattr);
|
|
|
|
if (status != 0)
|
|
|
|
return -nfs_stat_to_errno(status);
|
|
|
|
|
|
|
|
p = xdr_decode_hyper(p, &res->tbytes);
|
|
|
|
p = xdr_decode_hyper(p, &res->fbytes);
|
|
|
|
p = xdr_decode_hyper(p, &res->abytes);
|
|
|
|
p = xdr_decode_hyper(p, &res->tfiles);
|
|
|
|
p = xdr_decode_hyper(p, &res->ffiles);
|
|
|
|
p = xdr_decode_hyper(p, &res->afiles);
|
|
|
|
|
|
|
|
/* ignore invarsec */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Decode FSINFO reply
|
|
|
|
*/
|
|
|
|
static int
|
2006-10-20 10:28:48 +04:00
|
|
|
nfs3_xdr_fsinfores(struct rpc_rqst *req, __be32 *p, struct nfs_fsinfo *res)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
int status;
|
|
|
|
|
|
|
|
status = ntohl(*p++);
|
|
|
|
|
|
|
|
p = xdr_decode_post_op_attr(p, res->fattr);
|
|
|
|
if (status != 0)
|
|
|
|
return -nfs_stat_to_errno(status);
|
|
|
|
|
|
|
|
res->rtmax = ntohl(*p++);
|
|
|
|
res->rtpref = ntohl(*p++);
|
|
|
|
res->rtmult = ntohl(*p++);
|
|
|
|
res->wtmax = ntohl(*p++);
|
|
|
|
res->wtpref = ntohl(*p++);
|
|
|
|
res->wtmult = ntohl(*p++);
|
|
|
|
res->dtpref = ntohl(*p++);
|
|
|
|
p = xdr_decode_hyper(p, &res->maxfilesize);
|
|
|
|
|
|
|
|
/* ignore time_delta and properties */
|
|
|
|
res->lease_time = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Decode PATHCONF reply
|
|
|
|
*/
|
|
|
|
static int
|
2006-10-20 10:28:48 +04:00
|
|
|
nfs3_xdr_pathconfres(struct rpc_rqst *req, __be32 *p, struct nfs_pathconf *res)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
int status;
|
|
|
|
|
|
|
|
status = ntohl(*p++);
|
|
|
|
|
|
|
|
p = xdr_decode_post_op_attr(p, res->fattr);
|
|
|
|
if (status != 0)
|
|
|
|
return -nfs_stat_to_errno(status);
|
|
|
|
res->max_link = ntohl(*p++);
|
|
|
|
res->max_namelen = ntohl(*p++);
|
|
|
|
|
|
|
|
/* ignore remaining fields */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Decode COMMIT reply
|
|
|
|
*/
|
|
|
|
static int
|
2006-10-20 10:28:48 +04:00
|
|
|
nfs3_xdr_commitres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
int status;
|
|
|
|
|
|
|
|
status = ntohl(*p++);
|
|
|
|
p = xdr_decode_wcc_data(p, res->fattr);
|
|
|
|
if (status != 0)
|
|
|
|
return -nfs_stat_to_errno(status);
|
|
|
|
|
|
|
|
res->verf->verifier[0] = *p++;
|
|
|
|
res->verf->verifier[1] = *p++;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-06-22 21:16:27 +04:00
|
|
|
#ifdef CONFIG_NFS_V3_ACL
|
|
|
|
/*
|
|
|
|
* Decode GETACL reply
|
|
|
|
*/
|
|
|
|
static int
|
2006-10-20 10:28:48 +04:00
|
|
|
nfs3_xdr_getaclres(struct rpc_rqst *req, __be32 *p,
|
2005-06-22 21:16:27 +04:00
|
|
|
struct nfs3_getaclres *res)
|
|
|
|
{
|
|
|
|
struct xdr_buf *buf = &req->rq_rcv_buf;
|
|
|
|
int status = ntohl(*p++);
|
|
|
|
struct posix_acl **acl;
|
|
|
|
unsigned int *aclcnt;
|
|
|
|
int err, base;
|
|
|
|
|
|
|
|
if (status != 0)
|
|
|
|
return -nfs_stat_to_errno(status);
|
|
|
|
p = xdr_decode_post_op_attr(p, res->fattr);
|
|
|
|
res->mask = ntohl(*p++);
|
|
|
|
if (res->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
|
|
|
|
return -EINVAL;
|
|
|
|
base = (char *)p - (char *)req->rq_rcv_buf.head->iov_base;
|
|
|
|
|
|
|
|
acl = (res->mask & NFS_ACL) ? &res->acl_access : NULL;
|
|
|
|
aclcnt = (res->mask & NFS_ACLCNT) ? &res->acl_access_count : NULL;
|
|
|
|
err = nfsacl_decode(buf, base, aclcnt, acl);
|
|
|
|
|
|
|
|
acl = (res->mask & NFS_DFACL) ? &res->acl_default : NULL;
|
|
|
|
aclcnt = (res->mask & NFS_DFACLCNT) ? &res->acl_default_count : NULL;
|
|
|
|
if (err > 0)
|
|
|
|
err = nfsacl_decode(buf, base + err, aclcnt, acl);
|
|
|
|
return (err > 0) ? 0 : err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Decode setacl reply.
|
|
|
|
*/
|
|
|
|
static int
|
2006-10-20 10:28:48 +04:00
|
|
|
nfs3_xdr_setaclres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
|
2005-06-22 21:16:27 +04:00
|
|
|
{
|
|
|
|
int status = ntohl(*p++);
|
|
|
|
|
|
|
|
if (status)
|
|
|
|
return -nfs_stat_to_errno(status);
|
|
|
|
xdr_decode_post_op_attr(p, fattr);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_NFS_V3_ACL */
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
#define PROC(proc, argtype, restype, timer) \
|
|
|
|
[NFS3PROC_##proc] = { \
|
|
|
|
.p_proc = NFS3PROC_##proc, \
|
|
|
|
.p_encode = (kxdrproc_t) nfs3_xdr_##argtype, \
|
|
|
|
.p_decode = (kxdrproc_t) nfs3_xdr_##restype, \
|
2007-03-30 00:47:53 +04:00
|
|
|
.p_arglen = NFS3_##argtype##_sz, \
|
|
|
|
.p_replen = NFS3_##restype##_sz, \
|
2006-03-20 21:44:22 +03:00
|
|
|
.p_timer = timer, \
|
|
|
|
.p_statidx = NFS3PROC_##proc, \
|
|
|
|
.p_name = #proc, \
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
struct rpc_procinfo nfs3_procedures[] = {
|
|
|
|
PROC(GETATTR, fhandle, attrstat, 1),
|
|
|
|
PROC(SETATTR, sattrargs, wccstat, 0),
|
|
|
|
PROC(LOOKUP, diropargs, lookupres, 2),
|
|
|
|
PROC(ACCESS, accessargs, accessres, 1),
|
|
|
|
PROC(READLINK, readlinkargs, readlinkres, 3),
|
|
|
|
PROC(READ, readargs, readres, 3),
|
|
|
|
PROC(WRITE, writeargs, writeres, 4),
|
|
|
|
PROC(CREATE, createargs, createres, 0),
|
|
|
|
PROC(MKDIR, mkdirargs, createres, 0),
|
|
|
|
PROC(SYMLINK, symlinkargs, createres, 0),
|
|
|
|
PROC(MKNOD, mknodargs, createres, 0),
|
2007-07-14 23:39:57 +04:00
|
|
|
PROC(REMOVE, removeargs, removeres, 0),
|
2005-04-17 02:20:36 +04:00
|
|
|
PROC(RMDIR, diropargs, wccstat, 0),
|
|
|
|
PROC(RENAME, renameargs, renameres, 0),
|
|
|
|
PROC(LINK, linkargs, linkres, 0),
|
|
|
|
PROC(READDIR, readdirargs, readdirres, 3),
|
|
|
|
PROC(READDIRPLUS, readdirargs, readdirres, 3),
|
|
|
|
PROC(FSSTAT, fhandle, fsstatres, 0),
|
|
|
|
PROC(FSINFO, fhandle, fsinfores, 0),
|
|
|
|
PROC(PATHCONF, fhandle, pathconfres, 0),
|
|
|
|
PROC(COMMIT, commitargs, commitres, 5),
|
|
|
|
};
|
|
|
|
|
|
|
|
struct rpc_version nfs_version3 = {
|
|
|
|
.number = 3,
|
2006-03-24 14:15:34 +03:00
|
|
|
.nrprocs = ARRAY_SIZE(nfs3_procedures),
|
2005-04-17 02:20:36 +04:00
|
|
|
.procs = nfs3_procedures
|
|
|
|
};
|
|
|
|
|
2005-06-22 21:16:27 +04:00
|
|
|
#ifdef CONFIG_NFS_V3_ACL
|
|
|
|
static struct rpc_procinfo nfs3_acl_procedures[] = {
|
|
|
|
[ACLPROC3_GETACL] = {
|
|
|
|
.p_proc = ACLPROC3_GETACL,
|
|
|
|
.p_encode = (kxdrproc_t) nfs3_xdr_getaclargs,
|
|
|
|
.p_decode = (kxdrproc_t) nfs3_xdr_getaclres,
|
2007-03-30 00:47:53 +04:00
|
|
|
.p_arglen = ACL3_getaclargs_sz,
|
|
|
|
.p_replen = ACL3_getaclres_sz,
|
2005-06-22 21:16:27 +04:00
|
|
|
.p_timer = 1,
|
2006-03-20 21:44:22 +03:00
|
|
|
.p_name = "GETACL",
|
2005-06-22 21:16:27 +04:00
|
|
|
},
|
|
|
|
[ACLPROC3_SETACL] = {
|
|
|
|
.p_proc = ACLPROC3_SETACL,
|
|
|
|
.p_encode = (kxdrproc_t) nfs3_xdr_setaclargs,
|
|
|
|
.p_decode = (kxdrproc_t) nfs3_xdr_setaclres,
|
2007-03-30 00:47:53 +04:00
|
|
|
.p_arglen = ACL3_setaclargs_sz,
|
|
|
|
.p_replen = ACL3_setaclres_sz,
|
2005-06-22 21:16:27 +04:00
|
|
|
.p_timer = 0,
|
2006-03-20 21:44:22 +03:00
|
|
|
.p_name = "SETACL",
|
2005-06-22 21:16:27 +04:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
struct rpc_version nfsacl_version3 = {
|
|
|
|
.number = 3,
|
|
|
|
.nrprocs = sizeof(nfs3_acl_procedures)/
|
|
|
|
sizeof(nfs3_acl_procedures[0]),
|
|
|
|
.procs = nfs3_acl_procedures,
|
|
|
|
};
|
|
|
|
#endif /* CONFIG_NFS_V3_ACL */
|