2005-04-17 02:20:36 +04:00
/*
* linux / fs / nfs / nfs2xdr . c
*
* XDR functions to encode / decode NFS RPC arguments and results .
*
* Copyright ( C ) 1992 , 1993 , 1994 Rick Sladkey
* Copyright ( C ) 1996 Olaf Kirch
* 04 Aug 1998 Ion Badulescu < ionut @ cs . columbia . edu >
* FIFO ' s need special handling in NFSv2
*/
# 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/sunrpc/clnt.h>
# include <linux/nfs.h>
# include <linux/nfs2.h>
# include <linux/nfs_fs.h>
2006-06-24 16:41:41 +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 32 bit - words
*/
# define NFS_fhandle_sz (8)
# define NFS_sattr_sz (8)
# define NFS_filename_sz (1+(NFS2_MAXNAMLEN>>2))
# define NFS_path_sz (1+(NFS2_MAXPATHLEN>>2))
# define NFS_fattr_sz (17)
# define NFS_info_sz (5)
# define NFS_entry_sz (NFS_filename_sz+3)
# define NFS_diropargs_sz (NFS_fhandle_sz+NFS_filename_sz)
2007-07-14 23:39:57 +04:00
# define NFS_removeargs_sz (NFS_fhandle_sz+NFS_filename_sz)
2005-04-17 02:20:36 +04:00
# define NFS_sattrargs_sz (NFS_fhandle_sz+NFS_sattr_sz)
# define NFS_readlinkargs_sz (NFS_fhandle_sz)
# define NFS_readargs_sz (NFS_fhandle_sz+3)
# define NFS_writeargs_sz (NFS_fhandle_sz+4)
# define NFS_createargs_sz (NFS_diropargs_sz+NFS_sattr_sz)
# define NFS_renameargs_sz (NFS_diropargs_sz+NFS_diropargs_sz)
# define NFS_linkargs_sz (NFS_fhandle_sz+NFS_diropargs_sz)
2006-08-23 04:06:23 +04:00
# define NFS_symlinkargs_sz (NFS_diropargs_sz+1+NFS_sattr_sz)
2005-04-17 02:20:36 +04:00
# define NFS_readdirargs_sz (NFS_fhandle_sz+2)
# define NFS_attrstat_sz (1+NFS_fattr_sz)
# define NFS_diropres_sz (1+NFS_fhandle_sz+NFS_fattr_sz)
# define NFS_readlinkres_sz (2)
# define NFS_readres_sz (1+NFS_fattr_sz+1)
# define NFS_writeres_sz (NFS_attrstat_sz)
# define NFS_stat_sz (1)
# define NFS_readdirres_sz (1)
# define NFS_statfsres_sz (1+NFS_info_sz)
/*
* Common NFS XDR functions as inlines
*/
2006-10-20 10:28:47 +04:00
static inline __be32 *
2007-07-14 23:39:57 +04:00
xdr_encode_fhandle ( __be32 * p , const struct nfs_fh * fhandle )
2005-04-17 02:20:36 +04:00
{
memcpy ( p , fhandle - > data , NFS2_FHSIZE ) ;
return p + XDR_QUADLEN ( NFS2_FHSIZE ) ;
}
2006-10-20 10:28:47 +04:00
static inline __be32 *
xdr_decode_fhandle ( __be32 * p , struct nfs_fh * fhandle )
2005-04-17 02:20:36 +04:00
{
/* NFSv2 handles have a fixed length */
fhandle - > size = NFS2_FHSIZE ;
memcpy ( fhandle - > data , p , NFS2_FHSIZE ) ;
return p + XDR_QUADLEN ( NFS2_FHSIZE ) ;
}
2006-10-20 10:28:47 +04:00
static inline __be32 *
xdr_encode_time ( __be32 * p , struct timespec * timep )
2005-04-17 02:20:36 +04:00
{
* p + + = htonl ( timep - > tv_sec ) ;
/* Convert nanoseconds into microseconds */
* p + + = htonl ( timep - > tv_nsec ? timep - > tv_nsec / 1000 : 0 ) ;
return p ;
}
2006-10-20 10:28:47 +04:00
static inline __be32 *
xdr_encode_current_server_time ( __be32 * p , struct timespec * timep )
2005-04-17 02:20:36 +04:00
{
/*
* Passing the invalid value useconds = 1000000 is a
* Sun convention for " set 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
*/
* p + + = htonl ( timep - > tv_sec ) ;
* p + + = htonl ( 1000000 ) ;
return p ;
}
2006-10-20 10:28:47 +04:00
static inline __be32 *
xdr_decode_time ( __be32 * p , struct timespec * timep )
2005-04-17 02:20:36 +04:00
{
timep - > tv_sec = ntohl ( * p + + ) ;
/* Convert microseconds into nanoseconds */
timep - > tv_nsec = ntohl ( * p + + ) * 1000 ;
return p ;
}
2006-10-20 10:28:47 +04:00
static __be32 *
xdr_decode_fattr ( __be32 * p , struct nfs_fattr * fattr )
2005-04-17 02:20:36 +04:00
{
u32 rdev ;
fattr - > type = ( enum nfs_ftype ) ntohl ( * p + + ) ;
fattr - > mode = ntohl ( * p + + ) ;
fattr - > nlink = ntohl ( * p + + ) ;
fattr - > uid = ntohl ( * p + + ) ;
fattr - > gid = ntohl ( * p + + ) ;
fattr - > size = ntohl ( * p + + ) ;
fattr - > du . nfs2 . blocksize = ntohl ( * p + + ) ;
rdev = ntohl ( * p + + ) ;
fattr - > du . nfs2 . blocks = ntohl ( * p + + ) ;
2006-06-09 17:34:19 +04:00
fattr - > fsid . major = ntohl ( * p + + ) ;
fattr - > fsid . minor = 0 ;
2005-04-17 02:20:36 +04:00
fattr - > fileid = ntohl ( * p + + ) ;
p = xdr_decode_time ( p , & fattr - > atime ) ;
p = xdr_decode_time ( p , & fattr - > mtime ) ;
p = xdr_decode_time ( p , & fattr - > ctime ) ;
fattr - > valid | = NFS_ATTR_FATTR ;
fattr - > rdev = new_decode_dev ( rdev ) ;
if ( fattr - > type = = NFCHR & & rdev = = NFS2_FIFO_DEV ) {
fattr - > type = NFFIFO ;
fattr - > mode = ( fattr - > mode & ~ S_IFMT ) | S_IFIFO ;
fattr - > rdev = 0 ;
}
return p ;
}
2006-10-20 10:28:47 +04:00
static inline __be32 *
xdr_encode_sattr ( __be32 * p , struct iattr * attr )
2005-04-17 02:20:36 +04:00
{
2006-10-20 10:28:47 +04:00
const __be32 not_set = __constant_htonl ( 0xFFFFFFFF ) ;
2006-01-03 11:55:54 +03:00
* p + + = ( attr - > ia_valid & ATTR_MODE ) ? htonl ( attr - > ia_mode ) : not_set ;
* p + + = ( attr - > ia_valid & ATTR_UID ) ? htonl ( attr - > ia_uid ) : not_set ;
* p + + = ( attr - > ia_valid & ATTR_GID ) ? htonl ( attr - > ia_gid ) : not_set ;
* p + + = ( attr - > ia_valid & ATTR_SIZE ) ? htonl ( attr - > ia_size ) : not_set ;
2005-04-17 02:20:36 +04:00
if ( attr - > ia_valid & ATTR_ATIME_SET ) {
p = xdr_encode_time ( p , & attr - > ia_atime ) ;
} else if ( attr - > ia_valid & ATTR_ATIME ) {
p = xdr_encode_current_server_time ( p , & attr - > ia_atime ) ;
} else {
2006-01-03 11:55:54 +03:00
* p + + = not_set ;
* p + + = not_set ;
2005-04-17 02:20:36 +04:00
}
if ( attr - > ia_valid & ATTR_MTIME_SET ) {
p = xdr_encode_time ( p , & attr - > ia_mtime ) ;
} else if ( attr - > ia_valid & ATTR_MTIME ) {
p = xdr_encode_current_server_time ( p , & attr - > ia_mtime ) ;
} else {
2006-01-03 11:55:54 +03:00
* p + + = not_set ;
* p + + = not_set ;
2005-04-17 02:20:36 +04:00
}
return p ;
}
/*
* NFS encode functions
*/
/*
* Encode file handle argument
* GETATTR , READLINK , STATFS
*/
static int
2006-10-20 10:28:47 +04:00
nfs_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:47 +04:00
nfs_xdr_sattrargs ( struct rpc_rqst * req , __be32 * p , struct nfs_sattrargs * args )
2005-04-17 02:20:36 +04:00
{
p = xdr_encode_fhandle ( p , args - > fh ) ;
p = xdr_encode_sattr ( p , args - > sattr ) ;
req - > rq_slen = xdr_adjust_iovec ( req - > rq_svec , p ) ;
return 0 ;
}
/*
* Encode directory ops argument
2007-07-14 23:39:57 +04:00
* LOOKUP , RMDIR
2005-04-17 02:20:36 +04:00
*/
static int
2006-10-20 10:28:47 +04:00
nfs_xdr_diropargs ( struct rpc_rqst * req , __be32 * p , struct nfs_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
nfs_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
/*
* 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:47 +04:00
nfs_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 offset = ( u32 ) args - > offset ;
u32 count = args - > count ;
p = xdr_encode_fhandle ( p , args - > fh ) ;
* p + + = htonl ( offset ) ;
* p + + = htonl ( count ) ;
* p + + = htonl ( count ) ;
req - > rq_slen = xdr_adjust_iovec ( req - > rq_svec , p ) ;
/* Inline the page array */
replen = ( RPC_REPHDRSIZE + auth - > au_rslack + NFS_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 ;
}
/*
* Decode READ reply
*/
static int
2006-10-20 10:28:47 +04:00
nfs_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:52 +04:00
size_t hdrlen ;
u32 count , recvd ;
int status ;
2005-04-17 02:20:36 +04:00
if ( ( status = ntohl ( * p + + ) ) )
2008-03-31 18:39:06 +04:00
return nfs_stat_to_errno ( status ) ;
2005-04-17 02:20:36 +04:00
p = xdr_decode_fattr ( p , res - > fattr ) ;
count = ntohl ( * p + + ) ;
res - > eof = 0 ;
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:52 +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:52 +04:00
" count %u > recvd %u \n " , count , recvd ) ;
2005-04-17 02:20:36 +04:00
count = recvd ;
}
2007-10-26 21:31:52 +04:00
dprintk ( " RPC: readres OK count %u \n " , count ) ;
2005-04-17 02:20:36 +04:00
if ( count < res - > count )
res - > count = count ;
return count ;
}
/*
* Write arguments . Splice the buffer to be written into the iovec .
*/
static int
2006-10-20 10:28:47 +04:00
nfs_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 offset = ( u32 ) args - > offset ;
u32 count = args - > count ;
p = xdr_encode_fhandle ( p , args - > fh ) ;
* p + + = htonl ( offset ) ;
* p + + = htonl ( offset ) ;
* p + + = htonl ( count ) ;
* 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
* CREATE , MKDIR
*/
static int
2006-10-20 10:28:47 +04:00
nfs_xdr_createargs ( struct rpc_rqst * req , __be32 * p , struct nfs_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 = xdr_encode_sattr ( p , args - > sattr ) ;
req - > rq_slen = xdr_adjust_iovec ( req - > rq_svec , p ) ;
return 0 ;
}
/*
* Encode RENAME arguments
*/
static int
2006-10-20 10:28:47 +04:00
nfs_xdr_renameargs ( struct rpc_rqst * req , __be32 * p , struct nfs_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:47 +04:00
nfs_xdr_linkargs ( struct rpc_rqst * req , __be32 * p , struct nfs_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 SYMLINK arguments
*/
static int
2006-10-20 10:28:47 +04:00
nfs_xdr_symlinkargs ( struct rpc_rqst * req , __be32 * p , struct nfs_symlinkargs * args )
2005-04-17 02:20:36 +04:00
{
2006-08-23 04:06:23 +04:00
struct xdr_buf * sndbuf = & req - > rq_snd_buf ;
size_t pad ;
2005-04-17 02:20:36 +04:00
p = xdr_encode_fhandle ( p , args - > fromfh ) ;
p = xdr_encode_array ( p , args - > fromname , args - > fromlen ) ;
2006-08-23 04:06:23 +04:00
* p + + = htonl ( args - > pathlen ) ;
sndbuf - > len = xdr_adjust_iovec ( sndbuf - > head , p ) ;
xdr_encode_pages ( sndbuf , args - > pages , 0 , args - > pathlen ) ;
/*
* xdr_encode_pages may have added a few bytes to ensure the
* pathname ends on a 4 - byte boundary . Start encoding the
* attributes after the pad bytes .
*/
pad = sndbuf - > tail - > iov_len ;
if ( pad > 0 )
p + + ;
2005-04-17 02:20:36 +04:00
p = xdr_encode_sattr ( p , args - > sattr ) ;
2006-08-23 04:06:23 +04:00
sndbuf - > len + = xdr_adjust_iovec ( sndbuf - > tail , p ) - pad ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/*
* Encode arguments to readdir call
*/
static int
2006-10-20 10:28:47 +04:00
nfs_xdr_readdirargs ( struct rpc_rqst * req , __be32 * p , struct nfs_readdirargs * args )
2005-04-17 02:20:36 +04:00
{
struct rpc_task * task = req - > rq_task ;
2007-06-27 22:29:04 +04:00
struct rpc_auth * auth = 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 + + = htonl ( args - > cookie ) ;
* p + + = htonl ( count ) ; /* see above */
req - > rq_slen = xdr_adjust_iovec ( req - > rq_svec , p ) ;
/* Inline the page array */
replen = ( RPC_REPHDRSIZE + auth - > au_rslack + NFS_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 ' re not really decoding anymore , we just leave the buffer untouched
* and only check that it is syntactically correct .
* The real decoding happens in nfs_decode_entry below , called directly
* from nfs_readdir for each entry .
*/
static int
2006-10-20 10:28:47 +04:00
nfs_xdr_readdirres ( struct rpc_rqst * req , __be32 * p , void * dummy )
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:52 +04:00
size_t hdrlen ;
unsigned int pglen , recvd ;
u32 len ;
2008-02-22 22:49:59 +03:00
int status , nr = 0 ;
2006-10-20 10:28:47 +04:00
__be32 * end , * entry , * kaddr ;
2005-04-17 02:20:36 +04:00
if ( ( status = ntohl ( * p + + ) ) )
2008-03-31 18:39:06 +04:00
return nfs_stat_to_errno ( status ) ;
2005-04-17 02:20:36 +04:00
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:52 +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:47 +04:00
kaddr = p = kmap_atomic ( * page , KM_USER0 ) ;
end = ( __be32 * ) ( ( char * ) p + pglen ) ;
2005-04-17 02:20:36 +04:00
entry = p ;
2008-02-22 22:49:59 +03:00
/* Make sure the packet actually has a value_follows and EOF entry */
if ( ( entry + 1 ) > end )
goto short_pkt ;
for ( ; * p + + ; nr + + ) {
2005-04-17 02:20:36 +04:00
if ( p + 2 > end )
goto short_pkt ;
p + + ; /* fileid */
len = ntohl ( * p + + ) ;
p + = XDR_QUADLEN ( len ) + 1 ; /* name plus cookie */
if ( len > NFS2_MAXNAMLEN ) {
2007-09-12 02:01:10 +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 ( p + 2 > end )
goto short_pkt ;
entry = p ;
}
2008-02-22 22:49:59 +03:00
/*
* Apparently some server sends responses that are a valid size , but
* contain no entries , and have value_follows = = 0 and EOF = = 0. For
* those , just set the EOF marker .
*/
if ( ! nr & & entry [ 1 ] = = 0 ) {
dprintk ( " NFS: readdir reply truncated! \n " ) ;
entry [ 1 ] = 1 ;
}
2005-04-17 02:20:36 +04:00
out :
kunmap_atomic ( kaddr , KM_USER0 ) ;
return nr ;
short_pkt :
2008-02-22 22:49:59 +03:00
/*
* When we get a short packet there are 2 possibilities . We can
* return an error , or fix up the response to look like a valid
* response and return what we have so far . If there are no
* entries and the packet was short , then return - EIO . If there
* are valid entries in the response , return them and pretend that
* the call was successful , but incomplete . The caller can retry the
* readdir starting at the last cookie .
*/
2005-04-17 02:20:36 +04:00
entry [ 0 ] = entry [ 1 ] = 0 ;
2008-02-22 22:49:59 +03:00
if ( ! nr )
nr = - errno_NFSERR_IO ;
2005-04-17 02:20:36 +04:00
goto out ;
err_unmap :
nr = - errno_NFSERR_IO ;
goto out ;
}
2006-10-20 10:28:49 +04:00
__be32 *
nfs_decode_dirent ( __be32 * p , struct nfs_entry * entry , int plus )
2005-04-17 02:20:36 +04:00
{
if ( ! * p + + ) {
if ( ! * p )
return ERR_PTR ( - EAGAIN ) ;
entry - > eof = 1 ;
return ERR_PTR ( - EBADCOOKIE ) ;
}
entry - > ino = ntohl ( * p + + ) ;
entry - > len = ntohl ( * p + + ) ;
entry - > name = ( const char * ) p ;
p + = XDR_QUADLEN ( entry - > len ) ;
entry - > prev_cookie = entry - > cookie ;
entry - > cookie = ntohl ( * p + + ) ;
entry - > eof = ! p [ 0 ] & & p [ 1 ] ;
return p ;
}
/*
* NFS XDR decode functions
*/
/*
* Decode simple status reply
*/
static int
2006-10-20 10:28:47 +04:00
nfs_xdr_stat ( struct rpc_rqst * req , __be32 * p , void * dummy )
2005-04-17 02:20:36 +04:00
{
int status ;
if ( ( status = ntohl ( * p + + ) ) ! = 0 )
2008-03-31 18:39:06 +04:00
status = nfs_stat_to_errno ( status ) ;
2005-04-17 02:20:36 +04:00
return status ;
}
/*
* Decode attrstat reply
* GETATTR , SETATTR , WRITE
*/
static int
2006-10-20 10:28:47 +04:00
nfs_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 + + ) ) )
2008-03-31 18:39:06 +04:00
return nfs_stat_to_errno ( status ) ;
2005-04-17 02:20:36 +04:00
xdr_decode_fattr ( p , fattr ) ;
return 0 ;
}
/*
* Decode diropres reply
* LOOKUP , CREATE , MKDIR
*/
static int
2006-10-20 10:28:47 +04:00
nfs_xdr_diropres ( struct rpc_rqst * req , __be32 * p , struct nfs_diropok * res )
2005-04-17 02:20:36 +04:00
{
int status ;
if ( ( status = ntohl ( * p + + ) ) )
2008-03-31 18:39:06 +04:00
return nfs_stat_to_errno ( status ) ;
2005-04-17 02:20:36 +04:00
p = xdr_decode_fhandle ( p , res - > fh ) ;
xdr_decode_fattr ( p , res - > fattr ) ;
return 0 ;
}
/*
* Encode READLINK args
*/
static int
2006-10-20 10:28:47 +04:00
nfs_xdr_readlinkargs ( struct rpc_rqst * req , __be32 * p , struct nfs_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 + NFS_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:47 +04:00
nfs_xdr_readlinkres ( struct rpc_rqst * req , __be32 * p , void * dummy )
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:52 +04:00
size_t hdrlen ;
u32 len , recvd ;
2005-04-17 02:20:36 +04:00
char * kaddr ;
int status ;
if ( ( status = ntohl ( * p + + ) ) )
2008-03-31 18:39:06 +04:00
return nfs_stat_to_errno ( status ) ;
2005-04-17 02:20:36 +04:00
/* Convert length of symlink */
len = ntohl ( * p + + ) ;
2007-10-26 21:31:52 +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:52 +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: READLINK header is short. iovec will be shifted. \n " ) ;
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 WRITE reply
*/
static int
2006-10-20 10:28:47 +04:00
nfs_xdr_writeres ( struct rpc_rqst * req , __be32 * p , struct nfs_writeres * res )
2005-04-17 02:20:36 +04:00
{
res - > verf - > committed = NFS_FILE_SYNC ;
return nfs_xdr_attrstat ( req , p , res - > fattr ) ;
}
/*
* Decode STATFS reply
*/
static int
2006-10-20 10:28:47 +04:00
nfs_xdr_statfsres ( struct rpc_rqst * req , __be32 * p , struct nfs2_fsstat * res )
2005-04-17 02:20:36 +04:00
{
int status ;
if ( ( status = ntohl ( * p + + ) ) )
2008-03-31 18:39:06 +04:00
return nfs_stat_to_errno ( status ) ;
2005-04-17 02:20:36 +04:00
res - > tsize = ntohl ( * p + + ) ;
res - > bsize = ntohl ( * p + + ) ;
res - > blocks = ntohl ( * p + + ) ;
res - > bfree = ntohl ( * p + + ) ;
res - > bavail = ntohl ( * p + + ) ;
return 0 ;
}
/*
* We need to translate between nfs status return values and
* the local errno values which may not be the same .
*/
static struct {
int stat ;
int errno ;
} nfs_errtbl [ ] = {
{ NFS_OK , 0 } ,
2008-03-31 18:39:06 +04:00
{ NFSERR_PERM , - EPERM } ,
{ NFSERR_NOENT , - ENOENT } ,
{ NFSERR_IO , - errno_NFSERR_IO } ,
{ NFSERR_NXIO , - ENXIO } ,
/* { NFSERR_EAGAIN, -EAGAIN }, */
{ NFSERR_ACCES , - EACCES } ,
{ NFSERR_EXIST , - EEXIST } ,
{ NFSERR_XDEV , - EXDEV } ,
{ NFSERR_NODEV , - ENODEV } ,
{ NFSERR_NOTDIR , - ENOTDIR } ,
{ NFSERR_ISDIR , - EISDIR } ,
{ NFSERR_INVAL , - EINVAL } ,
{ NFSERR_FBIG , - EFBIG } ,
{ NFSERR_NOSPC , - ENOSPC } ,
{ NFSERR_ROFS , - EROFS } ,
{ NFSERR_MLINK , - EMLINK } ,
{ NFSERR_NAMETOOLONG , - ENAMETOOLONG } ,
{ NFSERR_NOTEMPTY , - ENOTEMPTY } ,
{ NFSERR_DQUOT , - EDQUOT } ,
{ NFSERR_STALE , - ESTALE } ,
{ NFSERR_REMOTE , - EREMOTE } ,
2005-04-17 02:20:36 +04:00
# ifdef EWFLUSH
2008-03-31 18:39:06 +04:00
{ NFSERR_WFLUSH , - EWFLUSH } ,
2005-04-17 02:20:36 +04:00
# endif
2008-03-31 18:39:06 +04:00
{ NFSERR_BADHANDLE , - EBADHANDLE } ,
{ NFSERR_NOT_SYNC , - ENOTSYNC } ,
{ NFSERR_BAD_COOKIE , - EBADCOOKIE } ,
{ NFSERR_NOTSUPP , - ENOTSUPP } ,
{ NFSERR_TOOSMALL , - ETOOSMALL } ,
{ NFSERR_SERVERFAULT , - ESERVERFAULT } ,
{ NFSERR_BADTYPE , - EBADTYPE } ,
{ NFSERR_JUKEBOX , - EJUKEBOX } ,
{ - 1 , - EIO }
2005-04-17 02:20:36 +04:00
} ;
/*
* Convert an NFS error code to a local one .
* This one is used jointly by NFSv2 and NFSv3 .
*/
int
nfs_stat_to_errno ( int stat )
{
int i ;
for ( i = 0 ; nfs_errtbl [ i ] . stat ! = - 1 ; i + + ) {
if ( nfs_errtbl [ i ] . stat = = stat )
return nfs_errtbl [ i ] . errno ;
}
2007-09-12 02:01:10 +04:00
dprintk ( " nfs_stat_to_errno: bad nfs status return value: %d \n " , stat ) ;
2005-04-17 02:20:36 +04:00
return nfs_errtbl [ i ] . errno ;
}
# define PROC(proc, argtype, restype, timer) \
[ NFSPROC_ # # proc ] = { \
. p_proc = NFSPROC_ # # proc , \
. p_encode = ( kxdrproc_t ) nfs_xdr_ # # argtype , \
. p_decode = ( kxdrproc_t ) nfs_xdr_ # # restype , \
2007-03-30 00:47:53 +04:00
. p_arglen = NFS_ # # argtype # # _sz , \
. p_replen = NFS_ # # restype # # _sz , \
2006-03-20 21:44:22 +03:00
. p_timer = timer , \
. p_statidx = NFSPROC_ # # proc , \
. p_name = # proc , \
2005-04-17 02:20:36 +04:00
}
struct rpc_procinfo nfs_procedures [ ] = {
PROC ( GETATTR , fhandle , attrstat , 1 ) ,
PROC ( SETATTR , sattrargs , attrstat , 0 ) ,
PROC ( LOOKUP , diropargs , diropres , 2 ) ,
PROC ( READLINK , readlinkargs , readlinkres , 3 ) ,
PROC ( READ , readargs , readres , 3 ) ,
PROC ( WRITE , writeargs , writeres , 4 ) ,
PROC ( CREATE , createargs , diropres , 0 ) ,
2007-07-14 23:39:57 +04:00
PROC ( REMOVE , removeargs , stat , 0 ) ,
2005-04-17 02:20:36 +04:00
PROC ( RENAME , renameargs , stat , 0 ) ,
PROC ( LINK , linkargs , stat , 0 ) ,
PROC ( SYMLINK , symlinkargs , stat , 0 ) ,
PROC ( MKDIR , createargs , diropres , 0 ) ,
PROC ( RMDIR , diropargs , stat , 0 ) ,
PROC ( READDIR , readdirargs , readdirres , 3 ) ,
PROC ( STATFS , fhandle , statfsres , 0 ) ,
} ;
struct rpc_version nfs_version2 = {
. number = 2 ,
2006-03-24 14:15:34 +03:00
. nrprocs = ARRAY_SIZE ( nfs_procedures ) ,
2005-04-17 02:20:36 +04:00
. procs = nfs_procedures
} ;