Skip to content
Snippets Groups Projects
nfs4xdr.c 116 KiB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
/*
 *  fs/nfs/nfs4xdr.c
 *
 *  Client-side XDR for NFSv4.
 *
 *  Copyright (c) 2002 The Regents of the University of Michigan.
 *  All rights reserved.
 *
 *  Kendrick Smith <kmsmith@umich.edu>
 *  Andy Adamson   <andros@umich.edu>
 * 
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *  3. Neither the name of the University nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#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/nfs4.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_idmap.h>
#include "nfs4_fs.h"
Linus Torvalds's avatar
Linus Torvalds committed

#define NFSDBG_FACILITY		NFSDBG_XDR

/* Mapping from NFS error code to "errno" error code. */
#define errno_NFSERR_IO		EIO

static int nfs4_stat_to_errno(int);
Linus Torvalds's avatar
Linus Torvalds committed

/* NFSv4 COMPOUND tags are only wanted for debugging purposes */
#ifdef DEBUG
#define NFS4_MAXTAGLEN		20
#else
#define NFS4_MAXTAGLEN		0
#endif

/* lock,open owner id: 
 * we currently use size 1 (u32) out of (NFS4_OPAQUE_LIMIT  >> 2)
 */
#define open_owner_id_maxsz	(1 + 1)
#define lock_owner_id_maxsz	(1 + 1)
Linus Torvalds's avatar
Linus Torvalds committed
#define compound_encode_hdr_maxsz	(3 + (NFS4_MAXTAGLEN >> 2))
#define compound_decode_hdr_maxsz	(3 + (NFS4_MAXTAGLEN >> 2))
#define op_encode_hdr_maxsz	(1)
#define op_decode_hdr_maxsz	(2)
#define encode_putfh_maxsz	(op_encode_hdr_maxsz + 1 + \
				(NFS4_FHSIZE >> 2))
#define decode_putfh_maxsz	(op_decode_hdr_maxsz)
#define encode_putrootfh_maxsz	(op_encode_hdr_maxsz)
#define decode_putrootfh_maxsz	(op_decode_hdr_maxsz)
#define encode_getfh_maxsz      (op_encode_hdr_maxsz)
#define decode_getfh_maxsz      (op_decode_hdr_maxsz + 1 + \
				((3+NFS4_FHSIZE) >> 2))
#define nfs4_fattr_bitmap_maxsz 3
#define encode_getattr_maxsz    (op_encode_hdr_maxsz + nfs4_fattr_bitmap_maxsz)
Linus Torvalds's avatar
Linus Torvalds committed
#define nfs4_name_maxsz		(1 + ((3 + NFS4_MAXNAMLEN) >> 2))
#define nfs4_path_maxsz		(1 + ((3 + NFS4_MAXPATHLEN) >> 2))
#define nfs4_owner_maxsz	(1 + XDR_QUADLEN(IDMAP_NAMESZ))
#define nfs4_group_maxsz	(1 + XDR_QUADLEN(IDMAP_NAMESZ))
/* This is based on getfattr, which uses the most attributes: */
#define nfs4_fattr_value_maxsz	(1 + (1 + 2 + 2 + 4 + 2 + 1 + 1 + 2 + 2 + \
				3 + 3 + 3 + nfs4_owner_maxsz + nfs4_group_maxsz))
#define nfs4_fattr_maxsz	(nfs4_fattr_bitmap_maxsz + \
				nfs4_fattr_value_maxsz)
#define decode_getattr_maxsz    (op_decode_hdr_maxsz + nfs4_fattr_maxsz)
Linus Torvalds's avatar
Linus Torvalds committed
#define encode_savefh_maxsz     (op_encode_hdr_maxsz)
#define decode_savefh_maxsz     (op_decode_hdr_maxsz)
#define encode_restorefh_maxsz  (op_encode_hdr_maxsz)
#define decode_restorefh_maxsz  (op_decode_hdr_maxsz)
Linus Torvalds's avatar
Linus Torvalds committed
#define encode_fsinfo_maxsz	(op_encode_hdr_maxsz + 2)
#define decode_fsinfo_maxsz	(op_decode_hdr_maxsz + 11)
#define encode_renew_maxsz	(op_encode_hdr_maxsz + 3)
#define decode_renew_maxsz	(op_decode_hdr_maxsz)
#define encode_setclientid_maxsz \
				(op_encode_hdr_maxsz + \
				4 /*server->ip_addr*/ + \
				1 /*Netid*/ + \
				6 /*uaddr*/ + \
				6 + (NFS4_VERIFIER_SIZE >> 2))
#define decode_setclientid_maxsz \
				(op_decode_hdr_maxsz + \
				2 + \
				1024) /* large value for CLID_INUSE */
#define encode_setclientid_confirm_maxsz \
				(op_encode_hdr_maxsz + \
				3 + (NFS4_VERIFIER_SIZE >> 2))
#define decode_setclientid_confirm_maxsz \
				(op_decode_hdr_maxsz)
#define encode_lookup_maxsz	(op_encode_hdr_maxsz + \
				1 + ((3 + NFS4_FHSIZE) >> 2))
#define encode_share_access_maxsz \
				(2)
#define encode_createmode_maxsz	(1 + nfs4_fattr_maxsz)
#define encode_opentype_maxsz	(1 + encode_createmode_maxsz)
#define encode_claim_null_maxsz	(1 + nfs4_name_maxsz)
#define encode_open_maxsz	(op_encode_hdr_maxsz + \
				2 + encode_share_access_maxsz + 2 + \
				open_owner_id_maxsz + \
				encode_opentype_maxsz + \
				encode_claim_null_maxsz)
#define decode_ace_maxsz	(3 + nfs4_owner_maxsz)
#define decode_delegation_maxsz	(1 + XDR_QUADLEN(NFS4_STATEID_SIZE) + 1 + \
				decode_ace_maxsz)
#define decode_change_info_maxsz	(5)
#define decode_open_maxsz	(op_decode_hdr_maxsz + \
				XDR_QUADLEN(NFS4_STATEID_SIZE) + \
				decode_change_info_maxsz + 1 + \
				nfs4_fattr_bitmap_maxsz + \
				decode_delegation_maxsz)
Linus Torvalds's avatar
Linus Torvalds committed
#define encode_remove_maxsz	(op_encode_hdr_maxsz + \
				nfs4_name_maxsz)
#define encode_rename_maxsz	(op_encode_hdr_maxsz + \
				2 * nfs4_name_maxsz)
#define decode_rename_maxsz	(op_decode_hdr_maxsz + 5 + 5)
#define encode_link_maxsz	(op_encode_hdr_maxsz + \
				nfs4_name_maxsz)
#define decode_link_maxsz	(op_decode_hdr_maxsz + 5)
#define encode_symlink_maxsz	(op_encode_hdr_maxsz + \
				1 + nfs4_name_maxsz + \
				nfs4_fattr_maxsz)
Linus Torvalds's avatar
Linus Torvalds committed
#define decode_symlink_maxsz	(op_decode_hdr_maxsz + 8)
#define encode_create_maxsz	(op_encode_hdr_maxsz + \
				2 + nfs4_name_maxsz + \
				nfs4_fattr_maxsz)
#define decode_create_maxsz	(op_decode_hdr_maxsz + \
				decode_change_info_maxsz + \
				nfs4_fattr_bitmap_maxsz)
Linus Torvalds's avatar
Linus Torvalds committed
#define encode_delegreturn_maxsz (op_encode_hdr_maxsz + 4)
#define decode_delegreturn_maxsz (op_decode_hdr_maxsz)
#define NFS4_enc_compound_sz	(1024)  /* XXX: large enough? */
#define NFS4_dec_compound_sz	(1024)  /* XXX: large enough? */
#define NFS4_enc_read_sz	(compound_encode_hdr_maxsz + \
				encode_putfh_maxsz + \
				op_encode_hdr_maxsz + 7)
#define NFS4_dec_read_sz	(compound_decode_hdr_maxsz + \
				decode_putfh_maxsz + \
				op_decode_hdr_maxsz + 2)
#define NFS4_enc_readlink_sz	(compound_encode_hdr_maxsz + \
				encode_putfh_maxsz + \
				op_encode_hdr_maxsz)
#define NFS4_dec_readlink_sz	(compound_decode_hdr_maxsz + \
				decode_putfh_maxsz + \
				op_decode_hdr_maxsz)
#define NFS4_enc_readdir_sz	(compound_encode_hdr_maxsz + \
				encode_putfh_maxsz + \
				op_encode_hdr_maxsz + 9)
#define NFS4_dec_readdir_sz	(compound_decode_hdr_maxsz + \
				decode_putfh_maxsz + \
				op_decode_hdr_maxsz + 2)
#define NFS4_enc_write_sz	(compound_encode_hdr_maxsz + \
				encode_putfh_maxsz + \
				op_encode_hdr_maxsz + 8 + \
				encode_getattr_maxsz)
Linus Torvalds's avatar
Linus Torvalds committed
#define NFS4_dec_write_sz	(compound_decode_hdr_maxsz + \
				decode_putfh_maxsz + \
				op_decode_hdr_maxsz + 4 + \
				decode_getattr_maxsz)
Linus Torvalds's avatar
Linus Torvalds committed
#define NFS4_enc_commit_sz	(compound_encode_hdr_maxsz + \
				encode_putfh_maxsz + \
				op_encode_hdr_maxsz + 3 + \
				encode_getattr_maxsz)
Linus Torvalds's avatar
Linus Torvalds committed
#define NFS4_dec_commit_sz	(compound_decode_hdr_maxsz + \
				decode_putfh_maxsz + \
				op_decode_hdr_maxsz + 2 + \
				decode_getattr_maxsz)
Linus Torvalds's avatar
Linus Torvalds committed
#define NFS4_enc_open_sz        (compound_encode_hdr_maxsz + \
				encode_putfh_maxsz + \
				encode_savefh_maxsz + \
				encode_open_maxsz + \
				encode_getfh_maxsz + \
				encode_getattr_maxsz + \
				encode_restorefh_maxsz + \
				encode_getattr_maxsz)
Linus Torvalds's avatar
Linus Torvalds committed
#define NFS4_dec_open_sz        (compound_decode_hdr_maxsz + \
				decode_putfh_maxsz + \
				decode_savefh_maxsz + \
				decode_open_maxsz + \
				decode_getfh_maxsz + \
				decode_getattr_maxsz + \
				decode_restorefh_maxsz + \
				decode_getattr_maxsz)
Linus Torvalds's avatar
Linus Torvalds committed
#define NFS4_enc_open_confirm_sz      \
                                (compound_encode_hdr_maxsz + \
                                encode_putfh_maxsz + \
                                op_encode_hdr_maxsz + 5)
#define NFS4_dec_open_confirm_sz        (compound_decode_hdr_maxsz + \
                                        decode_putfh_maxsz + \
                                        op_decode_hdr_maxsz + 4)
#define NFS4_enc_open_noattr_sz	(compound_encode_hdr_maxsz + \
					encode_putfh_maxsz + \
					encode_open_maxsz + \
					encode_getattr_maxsz)
Linus Torvalds's avatar
Linus Torvalds committed
#define NFS4_dec_open_noattr_sz	(compound_decode_hdr_maxsz + \
					decode_putfh_maxsz + \
					decode_open_maxsz + \
					decode_getattr_maxsz)
Linus Torvalds's avatar
Linus Torvalds committed
#define NFS4_enc_open_downgrade_sz \
				(compound_encode_hdr_maxsz + \
                                encode_putfh_maxsz + \
                                op_encode_hdr_maxsz + 7 + \
				encode_getattr_maxsz)
Linus Torvalds's avatar
Linus Torvalds committed
#define NFS4_dec_open_downgrade_sz \
				(compound_decode_hdr_maxsz + \
                                decode_putfh_maxsz + \
                                op_decode_hdr_maxsz + 4 + \
				decode_getattr_maxsz)
Linus Torvalds's avatar
Linus Torvalds committed
#define NFS4_enc_close_sz       (compound_encode_hdr_maxsz + \
                                encode_putfh_maxsz + \
                                op_encode_hdr_maxsz + 5 + \
				encode_getattr_maxsz)
Linus Torvalds's avatar
Linus Torvalds committed
#define NFS4_dec_close_sz       (compound_decode_hdr_maxsz + \
                                decode_putfh_maxsz + \
                                op_decode_hdr_maxsz + 4 + \
				decode_getattr_maxsz)
Linus Torvalds's avatar
Linus Torvalds committed
#define NFS4_enc_setattr_sz     (compound_encode_hdr_maxsz + \
                                encode_putfh_maxsz + \
                                op_encode_hdr_maxsz + 4 + \
                                nfs4_fattr_maxsz + \
Linus Torvalds's avatar
Linus Torvalds committed
                                encode_getattr_maxsz)
#define NFS4_dec_setattr_sz     (compound_decode_hdr_maxsz + \
                                decode_putfh_maxsz + \
                                op_decode_hdr_maxsz + 3 + \
                                nfs4_fattr_maxsz)
Linus Torvalds's avatar
Linus Torvalds committed
#define NFS4_enc_fsinfo_sz	(compound_encode_hdr_maxsz + \
				encode_putfh_maxsz + \
				encode_fsinfo_maxsz)
#define NFS4_dec_fsinfo_sz	(compound_decode_hdr_maxsz + \
				decode_putfh_maxsz + \
				decode_fsinfo_maxsz)
#define NFS4_enc_renew_sz	(compound_encode_hdr_maxsz + \
				encode_renew_maxsz)
#define NFS4_dec_renew_sz	(compound_decode_hdr_maxsz + \
				decode_renew_maxsz)
#define NFS4_enc_setclientid_sz	(compound_encode_hdr_maxsz + \
				encode_setclientid_maxsz)
#define NFS4_dec_setclientid_sz	(compound_decode_hdr_maxsz + \
				decode_setclientid_maxsz)
#define NFS4_enc_setclientid_confirm_sz \
				(compound_encode_hdr_maxsz + \
				encode_setclientid_confirm_maxsz + \
				encode_putrootfh_maxsz + \
				encode_fsinfo_maxsz)
#define NFS4_dec_setclientid_confirm_sz \
				(compound_decode_hdr_maxsz + \
				decode_setclientid_confirm_maxsz + \
				decode_putrootfh_maxsz + \
				decode_fsinfo_maxsz)
#define NFS4_enc_lock_sz        (compound_encode_hdr_maxsz + \
				encode_putfh_maxsz + \
				encode_getattr_maxsz + \
				op_encode_hdr_maxsz + \
				1 + 1 + 2 + 2 + \
				1 + 4 + 1 + 2 + \
Linus Torvalds's avatar
Linus Torvalds committed
#define NFS4_dec_lock_sz        (compound_decode_hdr_maxsz + \
				decode_putfh_maxsz + \
				decode_getattr_maxsz + \
				op_decode_hdr_maxsz + \
				2 + 2 + 1 + 2 + \
Linus Torvalds's avatar
Linus Torvalds committed
#define NFS4_enc_lockt_sz       (compound_encode_hdr_maxsz + \
				encode_putfh_maxsz + \
				encode_getattr_maxsz + \
				op_encode_hdr_maxsz + \
				1 + 2 + 2 + 2 + \
Linus Torvalds's avatar
Linus Torvalds committed
#define NFS4_dec_lockt_sz       (NFS4_dec_lock_sz)
#define NFS4_enc_locku_sz       (compound_encode_hdr_maxsz + \
				encode_putfh_maxsz + \
				encode_getattr_maxsz + \
				op_encode_hdr_maxsz + \
				1 + 1 + 4 + 2 + 2)
#define NFS4_dec_locku_sz       (compound_decode_hdr_maxsz + \
				decode_putfh_maxsz + \
				decode_getattr_maxsz + \
				op_decode_hdr_maxsz + 4)
#define NFS4_enc_access_sz	(compound_encode_hdr_maxsz + \
				encode_putfh_maxsz + \
				op_encode_hdr_maxsz + 1)
#define NFS4_dec_access_sz	(compound_decode_hdr_maxsz + \
				decode_putfh_maxsz + \
				op_decode_hdr_maxsz + 2)
#define NFS4_enc_getattr_sz	(compound_encode_hdr_maxsz + \
				encode_putfh_maxsz + \
				encode_getattr_maxsz)
#define NFS4_dec_getattr_sz	(compound_decode_hdr_maxsz + \
				decode_putfh_maxsz + \
				decode_getattr_maxsz)
#define NFS4_enc_lookup_sz	(compound_encode_hdr_maxsz + \
				encode_putfh_maxsz + \
				encode_lookup_maxsz + \
				encode_getattr_maxsz + \
				encode_getfh_maxsz)
#define NFS4_dec_lookup_sz	(compound_decode_hdr_maxsz + \
				decode_putfh_maxsz + \
				op_decode_hdr_maxsz + \
				decode_getattr_maxsz + \
				decode_getfh_maxsz)
#define NFS4_enc_lookup_root_sz (compound_encode_hdr_maxsz + \
				encode_putrootfh_maxsz + \
				encode_getattr_maxsz + \
				encode_getfh_maxsz)
#define NFS4_dec_lookup_root_sz (compound_decode_hdr_maxsz + \
				decode_putrootfh_maxsz + \
				decode_getattr_maxsz + \
				decode_getfh_maxsz)
#define NFS4_enc_remove_sz	(compound_encode_hdr_maxsz + \
				encode_putfh_maxsz + \
				encode_remove_maxsz + \
				encode_getattr_maxsz)
Linus Torvalds's avatar
Linus Torvalds committed
#define NFS4_dec_remove_sz	(compound_decode_hdr_maxsz + \
				decode_putfh_maxsz + \
				op_decode_hdr_maxsz + 5 + \
				decode_getattr_maxsz)
Linus Torvalds's avatar
Linus Torvalds committed
#define NFS4_enc_rename_sz	(compound_encode_hdr_maxsz + \
				encode_putfh_maxsz + \
				encode_savefh_maxsz + \
				encode_putfh_maxsz + \
				encode_rename_maxsz + \
				encode_getattr_maxsz + \
				encode_restorefh_maxsz + \
				encode_getattr_maxsz)
Linus Torvalds's avatar
Linus Torvalds committed
#define NFS4_dec_rename_sz	(compound_decode_hdr_maxsz + \
				decode_putfh_maxsz + \
				decode_savefh_maxsz + \
				decode_putfh_maxsz + \
				decode_rename_maxsz + \
				decode_getattr_maxsz + \
				decode_restorefh_maxsz + \
				decode_getattr_maxsz)
Linus Torvalds's avatar
Linus Torvalds committed
#define NFS4_enc_link_sz	(compound_encode_hdr_maxsz + \
				encode_putfh_maxsz + \
				encode_savefh_maxsz + \
				encode_putfh_maxsz + \
				encode_link_maxsz + \
				decode_getattr_maxsz + \
				encode_restorefh_maxsz + \
				decode_getattr_maxsz)
Linus Torvalds's avatar
Linus Torvalds committed
#define NFS4_dec_link_sz	(compound_decode_hdr_maxsz + \
				decode_putfh_maxsz + \
				decode_savefh_maxsz + \
				decode_putfh_maxsz + \
				decode_link_maxsz + \
				decode_getattr_maxsz + \
				decode_restorefh_maxsz + \
				decode_getattr_maxsz)
Linus Torvalds's avatar
Linus Torvalds committed
#define NFS4_enc_symlink_sz	(compound_encode_hdr_maxsz + \
				encode_putfh_maxsz + \
				encode_symlink_maxsz + \
				encode_getattr_maxsz + \
				encode_getfh_maxsz)
#define NFS4_dec_symlink_sz	(compound_decode_hdr_maxsz + \
				decode_putfh_maxsz + \
				decode_symlink_maxsz + \
				decode_getattr_maxsz + \
				decode_getfh_maxsz)
#define NFS4_enc_create_sz	(compound_encode_hdr_maxsz + \
				encode_putfh_maxsz + \
Linus Torvalds's avatar
Linus Torvalds committed
				encode_create_maxsz + \
Linus Torvalds's avatar
Linus Torvalds committed
				encode_getattr_maxsz + \
				encode_restorefh_maxsz + \
				encode_getattr_maxsz)
Linus Torvalds's avatar
Linus Torvalds committed
#define NFS4_dec_create_sz	(compound_decode_hdr_maxsz + \
				decode_putfh_maxsz + \
Linus Torvalds's avatar
Linus Torvalds committed
				decode_create_maxsz + \
Linus Torvalds's avatar
Linus Torvalds committed
				decode_getattr_maxsz + \
				decode_restorefh_maxsz + \
				decode_getattr_maxsz)
Linus Torvalds's avatar
Linus Torvalds committed
#define NFS4_enc_pathconf_sz	(compound_encode_hdr_maxsz + \
				encode_putfh_maxsz + \
				encode_getattr_maxsz)
#define NFS4_dec_pathconf_sz	(compound_decode_hdr_maxsz + \
				decode_putfh_maxsz + \
				decode_getattr_maxsz)
#define NFS4_enc_statfs_sz	(compound_encode_hdr_maxsz + \
				encode_putfh_maxsz + \
				encode_getattr_maxsz)
#define NFS4_dec_statfs_sz	(compound_decode_hdr_maxsz + \
				decode_putfh_maxsz + \
				op_decode_hdr_maxsz + 12)
#define NFS4_enc_server_caps_sz (compound_encode_hdr_maxsz + \
Linus Torvalds's avatar
Linus Torvalds committed
				encode_getattr_maxsz)
#define NFS4_dec_server_caps_sz (compound_decode_hdr_maxsz + \
Linus Torvalds's avatar
Linus Torvalds committed
				decode_getattr_maxsz)
#define NFS4_enc_delegreturn_sz	(compound_encode_hdr_maxsz + \
				encode_putfh_maxsz + \
				encode_delegreturn_maxsz + \
				encode_getattr_maxsz)
Linus Torvalds's avatar
Linus Torvalds committed
#define NFS4_dec_delegreturn_sz (compound_decode_hdr_maxsz + \
				decode_delegreturn_maxsz + \
				decode_getattr_maxsz)
#define NFS4_enc_getacl_sz	(compound_encode_hdr_maxsz + \
				encode_putfh_maxsz + \
				encode_getattr_maxsz)
#define NFS4_dec_getacl_sz	(compound_decode_hdr_maxsz + \
				decode_putfh_maxsz + \
				op_decode_hdr_maxsz + \
				nfs4_fattr_bitmap_maxsz + 1)
#define NFS4_enc_setacl_sz	(compound_encode_hdr_maxsz + \
				encode_putfh_maxsz + \
				op_encode_hdr_maxsz + 4 + \
				nfs4_fattr_bitmap_maxsz + 1)
#define NFS4_dec_setacl_sz	(compound_decode_hdr_maxsz + \
				decode_putfh_maxsz + \
				op_decode_hdr_maxsz + nfs4_fattr_bitmap_maxsz)
#define NFS4_enc_fs_locations_sz \
				(compound_encode_hdr_maxsz + \
				 encode_putfh_maxsz + \
				 encode_getattr_maxsz)
#define NFS4_dec_fs_locations_sz \
				(compound_decode_hdr_maxsz + \
				 decode_putfh_maxsz + \
				 op_decode_hdr_maxsz + \
				 nfs4_fattr_bitmap_maxsz)
Linus Torvalds's avatar
Linus Torvalds committed

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,		NFNON	     },
	{ 0,		NFNON	     },
};

struct compound_hdr {
	int32_t		status;
	uint32_t	nops;
	uint32_t	taglen;
	char *		tag;
};

/*
 * START OF "GENERIC" ENCODE ROUTINES.
 *   These may look a little ugly since they are imported from a "generic"
 * set of XDR encode/decode routines which are intended to be shared by
 * all of our NFSv4 implementations (OpenBSD, MacOS X...).
 *
 * If the pain of reading these is too great, it should be a straightforward
 * task to translate them into Linux-specific versions which are more
 * consistent with the style used in NFSv2/v3...
 */
#define WRITE32(n)               *p++ = htonl(n)
#define WRITE64(n)               do {				\
	*p++ = htonl((uint32_t)((n) >> 32));				\
	*p++ = htonl((uint32_t)(n));					\
} while (0)
#define WRITEMEM(ptr,nbytes)     do {				\
	p = xdr_encode_opaque_fixed(p, ptr, nbytes);		\
} while (0)

#define RESERVE_SPACE(nbytes)	do {				\
	p = xdr_reserve_space(xdr, nbytes);			\
	if (!p) printk("RESERVE_SPACE(%d) failed in function %s\n", (int) (nbytes), __FUNCTION__); \
	BUG_ON(!p);						\
} while (0)

static void encode_string(struct xdr_stream *xdr, unsigned int len, const char *str)
{
Al Viro's avatar
Al Viro committed
	__be32 *p;
Linus Torvalds's avatar
Linus Torvalds committed

	p = xdr_reserve_space(xdr, 4 + len);
	BUG_ON(p == NULL);
	xdr_encode_opaque(p, str, len);
}

static int encode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr)
{
Al Viro's avatar
Al Viro committed
	__be32 *p;
Linus Torvalds's avatar
Linus Torvalds committed

	dprintk("encode_compound: tag=%.*s\n", (int)hdr->taglen, hdr->tag);
	BUG_ON(hdr->taglen > NFS4_MAXTAGLEN);
	RESERVE_SPACE(12+(XDR_QUADLEN(hdr->taglen)<<2));
	WRITE32(hdr->taglen);
	WRITEMEM(hdr->tag, hdr->taglen);
	WRITE32(NFS4_MINOR_VERSION);
	WRITE32(hdr->nops);
	return 0;
}

static void encode_nfs4_verifier(struct xdr_stream *xdr, const nfs4_verifier *verf)
{
Al Viro's avatar
Al Viro committed
	__be32 *p;
Linus Torvalds's avatar
Linus Torvalds committed

	p = xdr_reserve_space(xdr, NFS4_VERIFIER_SIZE);
	BUG_ON(p == NULL);
	xdr_encode_opaque_fixed(p, verf->data, NFS4_VERIFIER_SIZE);
}

static int encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const struct nfs_server *server)
{
	char owner_name[IDMAP_NAMESZ];
	char owner_group[IDMAP_NAMESZ];
	int owner_namelen = 0;
	int owner_grouplen = 0;
Al Viro's avatar
Al Viro committed
	__be32 *p;
	__be32 *q;
Linus Torvalds's avatar
Linus Torvalds committed
	int len;
	uint32_t bmval0 = 0;
	uint32_t bmval1 = 0;
	int status;

	/*
	 * We reserve enough space to write the entire attribute buffer at once.
	 * In the worst-case, this would be
	 *   12(bitmap) + 4(attrlen) + 8(size) + 4(mode) + 4(atime) + 4(mtime)
	 *          = 36 bytes, plus any contribution from variable-length fields
Linus Torvalds's avatar
Linus Torvalds committed
	 */
	len = 16;

	/* Sigh */
	if (iap->ia_valid & ATTR_SIZE)
		len += 8;
	if (iap->ia_valid & ATTR_MODE)
		len += 4;
	if (iap->ia_valid & ATTR_UID) {
		owner_namelen = nfs_map_uid_to_name(server->nfs_client, iap->ia_uid, owner_name);
Linus Torvalds's avatar
Linus Torvalds committed
		if (owner_namelen < 0) {
			printk(KERN_WARNING "nfs: couldn't resolve uid %d to string\n",
			       iap->ia_uid);
			/* XXX */
			strcpy(owner_name, "nobody");
			owner_namelen = sizeof("nobody") - 1;
			/* goto out; */
		}
		len += 4 + (XDR_QUADLEN(owner_namelen) << 2);
	}
	if (iap->ia_valid & ATTR_GID) {
		owner_grouplen = nfs_map_gid_to_group(server->nfs_client, iap->ia_gid, owner_group);
Linus Torvalds's avatar
Linus Torvalds committed
		if (owner_grouplen < 0) {
			printk(KERN_WARNING "nfs4: couldn't resolve gid %d to string\n",
			       iap->ia_gid);
			strcpy(owner_group, "nobody");
			owner_grouplen = sizeof("nobody") - 1;
			/* goto out; */
		}
		len += 4 + (XDR_QUADLEN(owner_grouplen) << 2);
	}
	if (iap->ia_valid & ATTR_ATIME_SET)
		len += 16;
	else if (iap->ia_valid & ATTR_ATIME)
		len += 4;
	if (iap->ia_valid & ATTR_MTIME_SET)
		len += 16;
	else if (iap->ia_valid & ATTR_MTIME)
		len += 4;
	RESERVE_SPACE(len);

	/*
	 * We write the bitmap length now, but leave the bitmap and the attribute
	 * buffer length to be backfilled at the end of this routine.
	 */
	WRITE32(2);
	q = p;
	p += 3;

	if (iap->ia_valid & ATTR_SIZE) {
		bmval0 |= FATTR4_WORD0_SIZE;
		WRITE64(iap->ia_size);
	}
	if (iap->ia_valid & ATTR_MODE) {
		bmval1 |= FATTR4_WORD1_MODE;
		WRITE32(iap->ia_mode & S_IALLUGO);
Linus Torvalds's avatar
Linus Torvalds committed
	}
	if (iap->ia_valid & ATTR_UID) {
		bmval1 |= FATTR4_WORD1_OWNER;
		WRITE32(owner_namelen);
		WRITEMEM(owner_name, owner_namelen);
	}
	if (iap->ia_valid & ATTR_GID) {
		bmval1 |= FATTR4_WORD1_OWNER_GROUP;
		WRITE32(owner_grouplen);
		WRITEMEM(owner_group, owner_grouplen);
	}
	if (iap->ia_valid & ATTR_ATIME_SET) {
		bmval1 |= FATTR4_WORD1_TIME_ACCESS_SET;
		WRITE32(NFS4_SET_TO_CLIENT_TIME);
		WRITE32(0);
		WRITE32(iap->ia_mtime.tv_sec);
		WRITE32(iap->ia_mtime.tv_nsec);
	}
	else if (iap->ia_valid & ATTR_ATIME) {
		bmval1 |= FATTR4_WORD1_TIME_ACCESS_SET;
		WRITE32(NFS4_SET_TO_SERVER_TIME);
	}
	if (iap->ia_valid & ATTR_MTIME_SET) {
		bmval1 |= FATTR4_WORD1_TIME_MODIFY_SET;
		WRITE32(NFS4_SET_TO_CLIENT_TIME);
		WRITE32(0);
		WRITE32(iap->ia_mtime.tv_sec);
		WRITE32(iap->ia_mtime.tv_nsec);
	}
	else if (iap->ia_valid & ATTR_MTIME) {
		bmval1 |= FATTR4_WORD1_TIME_MODIFY_SET;
		WRITE32(NFS4_SET_TO_SERVER_TIME);
	}
	
	/*
	 * Now we backfill the bitmap and the attribute buffer length.
	 */
	if (len != ((char *)p - (char *)q) + 4) {
		printk ("encode_attr: Attr length calculation error! %u != %Zu\n",
				len, ((char *)p - (char *)q) + 4);
		BUG();
	}
	len = (char *)p - (char *)q - 12;
	*q++ = htonl(bmval0);
	*q++ = htonl(bmval1);
	*q++ = htonl(len);

	status = 0;
/* out: */
	return status;
}

static int encode_access(struct xdr_stream *xdr, u32 access)
{
Al Viro's avatar
Al Viro committed
	__be32 *p;
Linus Torvalds's avatar
Linus Torvalds committed

	RESERVE_SPACE(8);
	WRITE32(OP_ACCESS);
	WRITE32(access);
	
	return 0;
}

static int encode_close(struct xdr_stream *xdr, const struct nfs_closeargs *arg)
{
Al Viro's avatar
Al Viro committed
	__be32 *p;
Linus Torvalds's avatar
Linus Torvalds committed

	RESERVE_SPACE(8+NFS4_STATEID_SIZE);
Linus Torvalds's avatar
Linus Torvalds committed
	WRITE32(OP_CLOSE);
	WRITE32(arg->seqid->sequence->counter);
	WRITEMEM(arg->stateid->data, NFS4_STATEID_SIZE);
Linus Torvalds's avatar
Linus Torvalds committed
	
	return 0;
}

static int encode_commit(struct xdr_stream *xdr, const struct nfs_writeargs *args)
{
Al Viro's avatar
Al Viro committed
	__be32 *p;
Linus Torvalds's avatar
Linus Torvalds committed
        
        RESERVE_SPACE(16);
        WRITE32(OP_COMMIT);
        WRITE64(args->offset);
        WRITE32(args->count);

        return 0;
}

static int encode_create(struct xdr_stream *xdr, const struct nfs4_create_arg *create)
{
Al Viro's avatar
Al Viro committed
	__be32 *p;
Linus Torvalds's avatar
Linus Torvalds committed
	
	RESERVE_SPACE(8);
	WRITE32(OP_CREATE);
	WRITE32(create->ftype);

	switch (create->ftype) {
	case NF4LNK:
		RESERVE_SPACE(4);
		WRITE32(create->u.symlink.len);
		xdr_write_pages(xdr, create->u.symlink.pages, 0, create->u.symlink.len);
Linus Torvalds's avatar
Linus Torvalds committed
		break;

	case NF4BLK: case NF4CHR:
		RESERVE_SPACE(8);
		WRITE32(create->u.device.specdata1);
		WRITE32(create->u.device.specdata2);
		break;

	default:
		break;
	}

	RESERVE_SPACE(4 + create->name->len);
	WRITE32(create->name->len);
	WRITEMEM(create->name->name, create->name->len);

	return encode_attrs(xdr, create->attrs, create->server);
}

static int encode_getattr_one(struct xdr_stream *xdr, uint32_t bitmap)
{
Al Viro's avatar
Al Viro committed
        __be32 *p;
Linus Torvalds's avatar
Linus Torvalds committed

        RESERVE_SPACE(12);
        WRITE32(OP_GETATTR);
        WRITE32(1);
        WRITE32(bitmap);
        return 0;
}

static int encode_getattr_two(struct xdr_stream *xdr, uint32_t bm0, uint32_t bm1)
{
Al Viro's avatar
Al Viro committed
        __be32 *p;
Linus Torvalds's avatar
Linus Torvalds committed

        RESERVE_SPACE(16);
        WRITE32(OP_GETATTR);
        WRITE32(2);
        WRITE32(bm0);
        WRITE32(bm1);
        return 0;
}

static int encode_getfattr(struct xdr_stream *xdr, const u32* bitmask)
{
	return encode_getattr_two(xdr,
			bitmask[0] & nfs4_fattr_bitmap[0],
			bitmask[1] & nfs4_fattr_bitmap[1]);
}

static int encode_fsinfo(struct xdr_stream *xdr, const u32* bitmask)
{
	return encode_getattr_two(xdr, bitmask[0] & nfs4_fsinfo_bitmap[0],
			bitmask[1] & nfs4_fsinfo_bitmap[1]);
}

static int encode_fs_locations(struct xdr_stream *xdr, const u32* bitmask)
{
	return encode_getattr_two(xdr,
				  bitmask[0] & nfs4_fs_locations_bitmap[0],
				  bitmask[1] & nfs4_fs_locations_bitmap[1]);
}

Linus Torvalds's avatar
Linus Torvalds committed
static int encode_getfh(struct xdr_stream *xdr)
{
Al Viro's avatar
Al Viro committed
	__be32 *p;
Linus Torvalds's avatar
Linus Torvalds committed

	RESERVE_SPACE(4);
	WRITE32(OP_GETFH);

	return 0;
}

static int encode_link(struct xdr_stream *xdr, const struct qstr *name)
{
Al Viro's avatar
Al Viro committed
	__be32 *p;
Linus Torvalds's avatar
Linus Torvalds committed

	RESERVE_SPACE(8 + name->len);
	WRITE32(OP_LINK);
	WRITE32(name->len);
	WRITEMEM(name->name, name->len);
	
	return 0;
}

static inline int nfs4_lock_type(struct file_lock *fl, int block)
{
	if ((fl->fl_type & (F_RDLCK|F_WRLCK|F_UNLCK)) == F_RDLCK)
		return block ? NFS4_READW_LT : NFS4_READ_LT;
	return block ? NFS4_WRITEW_LT : NFS4_WRITE_LT;
}

static inline uint64_t nfs4_lock_length(struct file_lock *fl)
{
	if (fl->fl_end == OFFSET_MAX)
		return ~(uint64_t)0;
	return fl->fl_end - fl->fl_start + 1;
}

Linus Torvalds's avatar
Linus Torvalds committed
/*
 * opcode,type,reclaim,offset,length,new_lock_owner = 32
 * open_seqid,open_stateid,lock_seqid,lock_owner.clientid, lock_owner.id = 40
 */
static int encode_lock(struct xdr_stream *xdr, const struct nfs_lock_args *args)
Linus Torvalds's avatar
Linus Torvalds committed
{
Al Viro's avatar
Al Viro committed
	__be32 *p;
Linus Torvalds's avatar
Linus Torvalds committed

	RESERVE_SPACE(32);
	WRITE32(OP_LOCK);
	WRITE32(nfs4_lock_type(args->fl, args->block));
	WRITE32(args->reclaim);
	WRITE64(args->fl->fl_start);
	WRITE64(nfs4_lock_length(args->fl));
	WRITE32(args->new_lock_owner);
	if (args->new_lock_owner){
		RESERVE_SPACE(4+NFS4_STATEID_SIZE+20);
		WRITE32(args->open_seqid->sequence->counter);
		WRITEMEM(args->open_stateid->data, NFS4_STATEID_SIZE);
		WRITE32(args->lock_seqid->sequence->counter);
		WRITE64(args->lock_owner.clientid);
Linus Torvalds's avatar
Linus Torvalds committed
		WRITE32(4);
		WRITE32(args->lock_owner.id);
Linus Torvalds's avatar
Linus Torvalds committed
	}
	else {
		RESERVE_SPACE(NFS4_STATEID_SIZE+4);
		WRITEMEM(args->lock_stateid->data, NFS4_STATEID_SIZE);
		WRITE32(args->lock_seqid->sequence->counter);
static int encode_lockt(struct xdr_stream *xdr, const struct nfs_lockt_args *args)
Linus Torvalds's avatar
Linus Torvalds committed
{
Al Viro's avatar
Al Viro committed
	__be32 *p;
Linus Torvalds's avatar
Linus Torvalds committed

	RESERVE_SPACE(40);
	WRITE32(OP_LOCKT);
	WRITE32(nfs4_lock_type(args->fl, 0));
	WRITE64(args->fl->fl_start);
	WRITE64(nfs4_lock_length(args->fl));
	WRITE64(args->lock_owner.clientid);
Linus Torvalds's avatar
Linus Torvalds committed
	WRITE32(4);
	WRITE32(args->lock_owner.id);
Linus Torvalds's avatar
Linus Torvalds committed

	return 0;
}

static int encode_locku(struct xdr_stream *xdr, const struct nfs_locku_args *args)
Linus Torvalds's avatar
Linus Torvalds committed
{
Al Viro's avatar
Al Viro committed
	__be32 *p;
Linus Torvalds's avatar
Linus Torvalds committed

	RESERVE_SPACE(12+NFS4_STATEID_SIZE+16);
Linus Torvalds's avatar
Linus Torvalds committed
	WRITE32(OP_LOCKU);
	WRITE32(nfs4_lock_type(args->fl, 0));
	WRITE32(args->seqid->sequence->counter);
	WRITEMEM(args->stateid->data, NFS4_STATEID_SIZE);
	WRITE64(args->fl->fl_start);
	WRITE64(nfs4_lock_length(args->fl));
Linus Torvalds's avatar
Linus Torvalds committed

	return 0;
}

static int encode_lookup(struct xdr_stream *xdr, const struct qstr *name)
{
	int len = name->len;
Al Viro's avatar
Al Viro committed
	__be32 *p;
Linus Torvalds's avatar
Linus Torvalds committed

	RESERVE_SPACE(8 + len);
	WRITE32(OP_LOOKUP);
	WRITE32(len);
	WRITEMEM(name->name, len);

	return 0;
}

static void encode_share_access(struct xdr_stream *xdr, int open_flags)
{
Al Viro's avatar
Al Viro committed
	__be32 *p;
Linus Torvalds's avatar
Linus Torvalds committed

	RESERVE_SPACE(8);
	switch (open_flags & (FMODE_READ|FMODE_WRITE)) {
		case FMODE_READ:
			WRITE32(NFS4_SHARE_ACCESS_READ);
			break;
		case FMODE_WRITE:
			WRITE32(NFS4_SHARE_ACCESS_WRITE);
			break;
		case FMODE_READ|FMODE_WRITE:
			WRITE32(NFS4_SHARE_ACCESS_BOTH);
			break;
		default:
			BUG();
	}
	WRITE32(0);		/* for linux, share_deny = 0 always */
}

static inline void encode_openhdr(struct xdr_stream *xdr, const struct nfs_openargs *arg)
{
Al Viro's avatar
Al Viro committed
	__be32 *p;
Linus Torvalds's avatar
Linus Torvalds committed
 /*
 * opcode 4, seqid 4, share_access 4, share_deny 4, clientid 8, ownerlen 4,
 * owner 4 = 32
 */
	RESERVE_SPACE(8);
	WRITE32(OP_OPEN);
	WRITE32(arg->seqid->sequence->counter);
Linus Torvalds's avatar
Linus Torvalds committed
	encode_share_access(xdr, arg->open_flags);
	RESERVE_SPACE(16);
	WRITE64(arg->clientid);
	WRITE32(4);
	WRITE32(arg->id);
}

static inline void encode_createmode(struct xdr_stream *xdr, const struct nfs_openargs *arg)
{
Al Viro's avatar
Al Viro committed
	__be32 *p;
Linus Torvalds's avatar
Linus Torvalds committed

	RESERVE_SPACE(4);
	switch(arg->open_flags & O_EXCL) {
		case 0:
			WRITE32(NFS4_CREATE_UNCHECKED);
			encode_attrs(xdr, arg->u.attrs, arg->server);
			break;
		default:
			WRITE32(NFS4_CREATE_EXCLUSIVE);
			encode_nfs4_verifier(xdr, &arg->u.verifier);
	}
}

static void encode_opentype(struct xdr_stream *xdr, const struct nfs_openargs *arg)
{
Al Viro's avatar
Al Viro committed
	__be32 *p;
Linus Torvalds's avatar
Linus Torvalds committed

	RESERVE_SPACE(4);
	switch (arg->open_flags & O_CREAT) {
		case 0:
			WRITE32(NFS4_OPEN_NOCREATE);
			break;
		default:
			BUG_ON(arg->claim != NFS4_OPEN_CLAIM_NULL);
			WRITE32(NFS4_OPEN_CREATE);
			encode_createmode(xdr, arg);
	}
}

static inline void encode_delegation_type(struct xdr_stream *xdr, int delegation_type)
{
Al Viro's avatar
Al Viro committed
	__be32 *p;
Linus Torvalds's avatar
Linus Torvalds committed

	RESERVE_SPACE(4);
	switch (delegation_type) {
		case 0:
			WRITE32(NFS4_OPEN_DELEGATE_NONE);
			break;
		case FMODE_READ:
			WRITE32(NFS4_OPEN_DELEGATE_READ);
			break;
		case FMODE_WRITE|FMODE_READ:
			WRITE32(NFS4_OPEN_DELEGATE_WRITE);
			break;
		default:
			BUG();
	}
}

static inline void encode_claim_null(struct xdr_stream *xdr, const struct qstr *name)
{
Al Viro's avatar
Al Viro committed
	__be32 *p;
Linus Torvalds's avatar
Linus Torvalds committed

	RESERVE_SPACE(4);
	WRITE32(NFS4_OPEN_CLAIM_NULL);
	encode_string(xdr, name->len, name->name);
}

static inline void encode_claim_previous(struct xdr_stream *xdr, int type)
{
Al Viro's avatar
Al Viro committed
	__be32 *p;
Linus Torvalds's avatar
Linus Torvalds committed

	RESERVE_SPACE(4);
	WRITE32(NFS4_OPEN_CLAIM_PREVIOUS);
	encode_delegation_type(xdr, type);
}

static inline void encode_claim_delegate_cur(struct xdr_stream *xdr, const struct qstr *name, const nfs4_stateid *stateid)
{
Al Viro's avatar
Al Viro committed
	__be32 *p;
Linus Torvalds's avatar
Linus Torvalds committed

	RESERVE_SPACE(4+NFS4_STATEID_SIZE);
Linus Torvalds's avatar
Linus Torvalds committed
	WRITE32(NFS4_OPEN_CLAIM_DELEGATE_CUR);
	WRITEMEM(stateid->data, NFS4_STATEID_SIZE);