Skip to content
Snippets Groups Projects
nfs4xdr.c 151 KiB
Newer Older
  • Learn to ignore specific revisions
  • Linus Torvalds's avatar
    Linus Torvalds committed
    	};
    
    	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
    
    	encode_compound_hdr(&xdr, req, &hdr);
    
    	encode_sequence(&xdr, &args->seq_args, &hdr);
    
    	encode_putfh(&xdr, args->fh, &hdr);
    	encode_readlink(&xdr, args, req, &hdr);
    
    	xdr_inline_pages(&req->rq_rcv_buf, hdr.replen << 2, args->pages,
    
    			args->pgbase, args->pglen);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    /*
     * Encode a READDIR request
     */
    
    Al Viro's avatar
    Al Viro committed
    static int nfs4_xdr_enc_readdir(struct rpc_rqst *req, __be32 *p, const struct nfs4_readdir_arg *args)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	struct xdr_stream xdr;
    	struct compound_hdr hdr = {
    
    		.minorversion = nfs4_xdr_minorversion(&args->seq_args),
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	};
    
    	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
    
    	encode_compound_hdr(&xdr, req, &hdr);
    
    	encode_sequence(&xdr, &args->seq_args, &hdr);
    
    	encode_putfh(&xdr, args->fh, &hdr);
    	encode_readdir(&xdr, args, req, &hdr);
    
    	xdr_inline_pages(&req->rq_rcv_buf, hdr.replen << 2, args->pages,
    
    			 args->pgbase, args->count);
    	dprintk("%s: inlined page args = (%u, %p, %u, %u)\n",
    
    			__func__, hdr.replen << 2, args->pages,
    
    			args->pgbase, args->count);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    /*
     * Encode a READ request
     */
    
    Al Viro's avatar
    Al Viro committed
    static int nfs4_xdr_enc_read(struct rpc_rqst *req, __be32 *p, struct nfs_readargs *args)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	struct xdr_stream xdr;
    	struct compound_hdr hdr = {
    
    		.minorversion = nfs4_xdr_minorversion(&args->seq_args),
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	};
    
    	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
    
    	encode_compound_hdr(&xdr, req, &hdr);
    
    	encode_sequence(&xdr, &args->seq_args, &hdr);
    
    	encode_putfh(&xdr, args->fh, &hdr);
    	encode_read(&xdr, args, &hdr);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	xdr_inline_pages(&req->rq_rcv_buf, hdr.replen << 2,
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			 args->pages, args->pgbase, args->count);
    
    	req->rq_rcv_buf.flags |= XDRBUF_READ;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    /*
     * Encode an SETATTR request
     */
    
    Al Viro's avatar
    Al Viro committed
    static int nfs4_xdr_enc_setattr(struct rpc_rqst *req, __be32 *p, struct nfs_setattrargs *args)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    Andy Adamson's avatar
    Andy Adamson committed
    	struct xdr_stream xdr;
    	struct compound_hdr hdr = {
    
    		.minorversion = nfs4_xdr_minorversion(&args->seq_args),
    
    Andy Adamson's avatar
    Andy Adamson committed
    	};
    
    	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
    
    	encode_compound_hdr(&xdr, req, &hdr);
    
    	encode_sequence(&xdr, &args->seq_args, &hdr);
    
    	encode_putfh(&xdr, args->fh, &hdr);
    	encode_setattr(&xdr, args, args->server, &hdr);
    	encode_getfattr(&xdr, args->bitmask, &hdr);
    
    /*
     * Encode a GETACL request
     */
    static int
    
    Al Viro's avatar
    Al Viro committed
    nfs4_xdr_enc_getacl(struct rpc_rqst *req, __be32 *p,
    
    		struct nfs_getaclargs *args)
    {
    	struct xdr_stream xdr;
    	struct compound_hdr hdr = {
    
    		.minorversion = nfs4_xdr_minorversion(&args->seq_args),
    
    
    	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
    
    	encode_compound_hdr(&xdr, req, &hdr);
    
    	encode_sequence(&xdr, &args->seq_args, &hdr);
    
    	encode_putfh(&xdr, args->fh, &hdr);
    
    	replen = hdr.replen + nfs4_fattr_bitmap_maxsz + 1;
    
    	encode_getattr_two(&xdr, FATTR4_WORD0_ACL, 0, &hdr);
    
    
    	xdr_inline_pages(&req->rq_rcv_buf, replen << 2,
    
    		args->acl_pages, args->acl_pgbase, args->acl_len);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    /*
     * Encode a WRITE request
     */
    
    Al Viro's avatar
    Al Viro committed
    static int nfs4_xdr_enc_write(struct rpc_rqst *req, __be32 *p, struct nfs_writeargs *args)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	struct xdr_stream xdr;
    	struct compound_hdr hdr = {
    
    		.minorversion = nfs4_xdr_minorversion(&args->seq_args),
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	};
    
    	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
    
    	encode_compound_hdr(&xdr, req, &hdr);
    
    	encode_sequence(&xdr, &args->seq_args, &hdr);
    
    	encode_putfh(&xdr, args->fh, &hdr);
    	encode_write(&xdr, args, &hdr);
    
    	req->rq_snd_buf.flags |= XDRBUF_WRITE;
    
    	encode_getfattr(&xdr, args->bitmask, &hdr);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    /*
     *  a COMMIT request
     */
    
    Al Viro's avatar
    Al Viro committed
    static int nfs4_xdr_enc_commit(struct rpc_rqst *req, __be32 *p, struct nfs_writeargs *args)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	struct xdr_stream xdr;
    	struct compound_hdr hdr = {
    
    		.minorversion = nfs4_xdr_minorversion(&args->seq_args),
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	};
    
    	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
    
    	encode_compound_hdr(&xdr, req, &hdr);
    
    	encode_sequence(&xdr, &args->seq_args, &hdr);
    
    	encode_putfh(&xdr, args->fh, &hdr);
    	encode_commit(&xdr, args, &hdr);
    	encode_getfattr(&xdr, args->bitmask, &hdr);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    /*
     * FSINFO request
     */
    
    Al Viro's avatar
    Al Viro committed
    static int nfs4_xdr_enc_fsinfo(struct rpc_rqst *req, __be32 *p, struct nfs4_fsinfo_arg *args)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	struct xdr_stream xdr;
    	struct compound_hdr hdr = {
    
    		.minorversion = nfs4_xdr_minorversion(&args->seq_args),
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	};
    
    	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
    
    	encode_compound_hdr(&xdr, req, &hdr);
    
    	encode_sequence(&xdr, &args->seq_args, &hdr);
    
    	encode_putfh(&xdr, args->fh, &hdr);
    	encode_fsinfo(&xdr, args->bitmask, &hdr);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    /*
     * a PATHCONF request
     */
    
    Al Viro's avatar
    Al Viro committed
    static int nfs4_xdr_enc_pathconf(struct rpc_rqst *req, __be32 *p, const struct nfs4_pathconf_arg *args)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	struct xdr_stream xdr;
    	struct compound_hdr hdr = {
    
    		.minorversion = nfs4_xdr_minorversion(&args->seq_args),
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	};
    
    	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
    
    	encode_compound_hdr(&xdr, req, &hdr);
    
    	encode_sequence(&xdr, &args->seq_args, &hdr);
    
    	encode_putfh(&xdr, args->fh, &hdr);
    	encode_getattr_one(&xdr, args->bitmask[0] & nfs4_pathconf_bitmap[0],
    			   &hdr);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    /*
     * a STATFS request
     */
    
    Al Viro's avatar
    Al Viro committed
    static int nfs4_xdr_enc_statfs(struct rpc_rqst *req, __be32 *p, const struct nfs4_statfs_arg *args)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	struct xdr_stream xdr;
    	struct compound_hdr hdr = {
    
    		.minorversion = nfs4_xdr_minorversion(&args->seq_args),
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	};
    
    	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
    
    	encode_compound_hdr(&xdr, req, &hdr);
    
    	encode_sequence(&xdr, &args->seq_args, &hdr);
    
    	encode_putfh(&xdr, args->fh, &hdr);
    	encode_getattr_two(&xdr, args->bitmask[0] & nfs4_statfs_bitmap[0],
    			   args->bitmask[1] & nfs4_statfs_bitmap[1], &hdr);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    /*
     * GETATTR_BITMAP request
     */
    
    static int nfs4_xdr_enc_server_caps(struct rpc_rqst *req, __be32 *p,
    				    struct nfs4_server_caps_arg *args)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	struct xdr_stream xdr;
    	struct compound_hdr hdr = {
    
    		.minorversion = nfs4_xdr_minorversion(&args->seq_args),
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	};
    
    	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
    
    	encode_compound_hdr(&xdr, req, &hdr);
    
    	encode_sequence(&xdr, &args->seq_args, &hdr);
    
    	encode_putfh(&xdr, args->fhandle, &hdr);
    
    	encode_getattr_one(&xdr, FATTR4_WORD0_SUPPORTED_ATTRS|
    			   FATTR4_WORD0_LINK_SUPPORT|
    			   FATTR4_WORD0_SYMLINK_SUPPORT|
    			   FATTR4_WORD0_ACLSUPPORT, &hdr);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    /*
     * a RENEW request
     */
    
    Al Viro's avatar
    Al Viro committed
    static int nfs4_xdr_enc_renew(struct rpc_rqst *req, __be32 *p, struct nfs_client *clp)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	struct xdr_stream xdr;
    	struct compound_hdr hdr = {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	};
    
    	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
    
    	encode_compound_hdr(&xdr, req, &hdr);
    
    	encode_renew(&xdr, clp, &hdr);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    /*
     * a SETCLIENTID request
     */
    
    Al Viro's avatar
    Al Viro committed
    static int nfs4_xdr_enc_setclientid(struct rpc_rqst *req, __be32 *p, struct nfs4_setclientid *sc)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	struct xdr_stream xdr;
    	struct compound_hdr hdr = {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	};
    
    	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
    
    	encode_compound_hdr(&xdr, req, &hdr);
    
    	encode_setclientid(&xdr, sc, &hdr);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    /*
     * a SETCLIENTID_CONFIRM request
     */
    
    Al Viro's avatar
    Al Viro committed
    static int nfs4_xdr_enc_setclientid_confirm(struct rpc_rqst *req, __be32 *p, struct nfs_client *clp)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	struct xdr_stream xdr;
    	struct compound_hdr hdr = {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	};
    	const u32 lease_bitmap[2] = { FATTR4_WORD0_LEASE_TIME, 0 };
    
    	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
    
    	encode_compound_hdr(&xdr, req, &hdr);
    
    	encode_setclientid_confirm(&xdr, clp, &hdr);
    	encode_putrootfh(&xdr, &hdr);
    	encode_fsinfo(&xdr, lease_bitmap, &hdr);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    /*
     * DELEGRETURN request
     */
    
    Al Viro's avatar
    Al Viro committed
    static int nfs4_xdr_enc_delegreturn(struct rpc_rqst *req, __be32 *p, const struct nfs4_delegreturnargs *args)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	struct xdr_stream xdr;
    	struct compound_hdr hdr = {
    
    		.minorversion = nfs4_xdr_minorversion(&args->seq_args),
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	};
    
    	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
    
    	encode_compound_hdr(&xdr, req, &hdr);
    
    	encode_sequence(&xdr, &args->seq_args, &hdr);
    
    	encode_putfh(&xdr, args->fhandle, &hdr);
    	encode_delegreturn(&xdr, args->stateid, &hdr);
    	encode_getfattr(&xdr, args->bitmask, &hdr);
    
    /*
     * Encode FS_LOCATIONS request
     */
    
    Al Viro's avatar
    Al Viro committed
    static int nfs4_xdr_enc_fs_locations(struct rpc_rqst *req, __be32 *p, struct nfs4_fs_locations_arg *args)
    
    {
    	struct xdr_stream xdr;
    	struct compound_hdr hdr = {
    
    		.minorversion = nfs4_xdr_minorversion(&args->seq_args),
    
    
    	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
    
    	encode_compound_hdr(&xdr, req, &hdr);
    
    	encode_sequence(&xdr, &args->seq_args, &hdr);
    
    	encode_putfh(&xdr, args->dir_fh, &hdr);
    	encode_lookup(&xdr, args->name, &hdr);
    
    	replen = hdr.replen;	/* get the attribute into args->page */
    
    	encode_fs_locations(&xdr, args->bitmask, &hdr);
    
    
    	xdr_inline_pages(&req->rq_rcv_buf, replen << 2, &args->page,
    
    #if defined(CONFIG_NFS_V4_1)
    /*
     * EXCHANGE_ID request
     */
    static int nfs4_xdr_enc_exchange_id(struct rpc_rqst *req, uint32_t *p,
    				    struct nfs41_exchange_id_args *args)
    {
    	struct xdr_stream xdr;
    	struct compound_hdr hdr = {
    		.minorversion = args->client->cl_minorversion,
    	};
    
    	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
    	encode_compound_hdr(&xdr, req, &hdr);
    	encode_exchange_id(&xdr, args, &hdr);
    	encode_nops(&hdr);
    	return 0;
    }
    
    /*
     * a CREATE_SESSION request
     */
    static int nfs4_xdr_enc_create_session(struct rpc_rqst *req, uint32_t *p,
    				       struct nfs41_create_session_args *args)
    {
    	struct xdr_stream xdr;
    	struct compound_hdr hdr = {
    		.minorversion = args->client->cl_minorversion,
    	};
    
    	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
    	encode_compound_hdr(&xdr, req, &hdr);
    	encode_create_session(&xdr, args, &hdr);
    	encode_nops(&hdr);
    	return 0;
    }
    
    
    /*
     * a DESTROY_SESSION request
     */
    static int nfs4_xdr_enc_destroy_session(struct rpc_rqst *req, uint32_t *p,
    					struct nfs4_session *session)
    {
    	struct xdr_stream xdr;
    	struct compound_hdr hdr = {
    		.minorversion = session->clp->cl_minorversion,
    	};
    
    	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
    	encode_compound_hdr(&xdr, req, &hdr);
    	encode_destroy_session(&xdr, session, &hdr);
    	encode_nops(&hdr);
    	return 0;
    }
    
    
    Andy Adamson's avatar
    Andy Adamson committed
    /*
     * a SEQUENCE request
     */
    static int nfs4_xdr_enc_sequence(struct rpc_rqst *req, uint32_t *p,
    				 struct nfs4_sequence_args *args)
    {
    	struct xdr_stream xdr;
    	struct compound_hdr hdr = {
    		.minorversion = nfs4_xdr_minorversion(args),
    	};
    
    	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
    	encode_compound_hdr(&xdr, req, &hdr);
    	encode_sequence(&xdr, args, &hdr);
    	encode_nops(&hdr);
    	return 0;
    }
    
    
    Andy Adamson's avatar
    Andy Adamson committed
    /*
     * a GET_LEASE_TIME request
     */
    static int nfs4_xdr_enc_get_lease_time(struct rpc_rqst *req, uint32_t *p,
    				       struct nfs4_get_lease_time_args *args)
    {
    	struct xdr_stream xdr;
    	struct compound_hdr hdr = {
    		.minorversion = nfs4_xdr_minorversion(&args->la_seq_args),
    	};
    	const u32 lease_bitmap[2] = { FATTR4_WORD0_LEASE_TIME, 0 };
    
    	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
    	encode_compound_hdr(&xdr, req, &hdr);
    	encode_sequence(&xdr, &args->la_seq_args, &hdr);
    	encode_putrootfh(&xdr, &hdr);
    	encode_fsinfo(&xdr, lease_bitmap, &hdr);
    	encode_nops(&hdr);
    	return 0;
    }
    
    #endif /* CONFIG_NFS_V4_1 */
    
    
    static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
    {
    	dprintk("nfs: %s: prematurely hit end of receive buffer. "
    		"Remaining buffer length is %tu words.\n",
    		func, xdr->end - xdr->p);
    }
    
    
    static int decode_opaque_inline(struct xdr_stream *xdr, unsigned int *len, char **string)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    Al Viro's avatar
    Al Viro committed
    	__be32 *p;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	p = xdr_inline_decode(xdr, 4);
    	if (unlikely(!p))
    		goto out_overflow;
    
    	*len = be32_to_cpup(p++);
    
    	p = xdr_inline_decode(xdr, *len);
    	if (unlikely(!p))
    		goto out_overflow;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	*string = (char *)p;
    	return 0;
    
    out_overflow:
    	print_overflow_msg(__func__, xdr);
    	return -EIO;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    static int decode_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
    
    
    	p = xdr_inline_decode(xdr, 8);
    	if (unlikely(!p))
    		goto out_overflow;
    
    	hdr->status = be32_to_cpup(p++);
    	hdr->taglen = be32_to_cpup(p++);
    
    	p = xdr_inline_decode(xdr, hdr->taglen + 4);
    	if (unlikely(!p))
    		goto out_overflow;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	hdr->tag = (char *)p;
    	p += XDR_QUADLEN(hdr->taglen);
    
    	hdr->nops = be32_to_cpup(p++);
    
    	if (unlikely(hdr->nops < 1))
    		return nfs4_stat_to_errno(hdr->status);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return 0;
    
    out_overflow:
    	print_overflow_msg(__func__, xdr);
    	return -EIO;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    static int decode_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 expected)
    {
    
    Al Viro's avatar
    Al Viro committed
    	__be32 *p;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	uint32_t opnum;
    	int32_t nfserr;
    
    
    	p = xdr_inline_decode(xdr, 8);
    	if (unlikely(!p))
    		goto out_overflow;
    
    	opnum = be32_to_cpup(p++);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (opnum != expected) {
    
    		dprintk("nfs: Server returned operation"
    			" %d but we issued a request for %d\n",
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    				opnum, expected);
    		return -EIO;
    	}
    
    	nfserr = be32_to_cpup(p++);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (nfserr != NFS_OK)
    
    		return nfs4_stat_to_errno(nfserr);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return 0;
    
    out_overflow:
    	print_overflow_msg(__func__, xdr);
    	return -EIO;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    /* Dummy routine */
    
    static int decode_ace(struct xdr_stream *xdr, void *ace, struct nfs_client *clp)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    Al Viro's avatar
    Al Viro committed
    	__be32 *p;
    
    	unsigned int strlen;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	char *str;
    
    
    	p = xdr_inline_decode(xdr, 12);
    	if (likely(p))
    		return decode_opaque_inline(xdr, &strlen, &str);
    	print_overflow_msg(__func__, xdr);
    	return -EIO;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    static int decode_attr_bitmap(struct xdr_stream *xdr, uint32_t *bitmap)
    {
    
    Al Viro's avatar
    Al Viro committed
    	uint32_t bmlen;
    	__be32 *p;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	p = xdr_inline_decode(xdr, 4);
    	if (unlikely(!p))
    		goto out_overflow;
    
    	bmlen = be32_to_cpup(p++);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	bitmap[0] = bitmap[1] = 0;
    
    	p = xdr_inline_decode(xdr, (bmlen << 2));
    	if (unlikely(!p))
    		goto out_overflow;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (bmlen > 0) {
    
    		bitmap[0] = be32_to_cpup(p++);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		if (bmlen > 1)
    
    			bitmap[1] = be32_to_cpup(p++);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    	return 0;
    
    out_overflow:
    	print_overflow_msg(__func__, xdr);
    	return -EIO;
    
    Al Viro's avatar
    Al Viro committed
    static inline int decode_attr_length(struct xdr_stream *xdr, uint32_t *attrlen, __be32 **savep)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    Al Viro's avatar
    Al Viro committed
    	__be32 *p;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	p = xdr_inline_decode(xdr, 4);
    	if (unlikely(!p))
    		goto out_overflow;
    
    	*attrlen = be32_to_cpup(p++);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	*savep = xdr->p;
    	return 0;
    
    out_overflow:
    	print_overflow_msg(__func__, xdr);
    	return -EIO;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    static int decode_attr_supported(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *bitmask)
    {
    	if (likely(bitmap[0] & FATTR4_WORD0_SUPPORTED_ATTRS)) {
    		decode_attr_bitmap(xdr, bitmask);
    		bitmap[0] &= ~FATTR4_WORD0_SUPPORTED_ATTRS;
    	} else
    		bitmask[0] = bitmask[1] = 0;
    
    	dprintk("%s: bitmask=%08x:%08x\n", __func__, bitmask[0], bitmask[1]);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return 0;
    }
    
    static int decode_attr_type(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *type)
    {
    
    Al Viro's avatar
    Al Viro committed
    	__be32 *p;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	*type = 0;
    	if (unlikely(bitmap[0] & (FATTR4_WORD0_TYPE - 1U)))
    		return -EIO;
    	if (likely(bitmap[0] & FATTR4_WORD0_TYPE)) {
    
    		p = xdr_inline_decode(xdr, 4);
    		if (unlikely(!p))
    			goto out_overflow;
    
    		*type = be32_to_cpup(p++);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		if (*type < NF4REG || *type > NF4NAMEDATTR) {
    
    			dprintk("%s: bad type %d\n", __func__, *type);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			return -EIO;
    		}
    		bitmap[0] &= ~FATTR4_WORD0_TYPE;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    
    	dprintk("%s: type=0%o\n", __func__, nfs_type2fmt[*type]);
    
    out_overflow:
    	print_overflow_msg(__func__, xdr);
    	return -EIO;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    static int decode_attr_change(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *change)
    {
    
    Al Viro's avatar
    Al Viro committed
    	__be32 *p;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	*change = 0;
    	if (unlikely(bitmap[0] & (FATTR4_WORD0_CHANGE - 1U)))
    		return -EIO;
    	if (likely(bitmap[0] & FATTR4_WORD0_CHANGE)) {
    
    		p = xdr_inline_decode(xdr, 8);
    		if (unlikely(!p))
    			goto out_overflow;
    
    		p = xdr_decode_hyper(p, change);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		bitmap[0] &= ~FATTR4_WORD0_CHANGE;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    
    	dprintk("%s: change attribute=%Lu\n", __func__,
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			(unsigned long long)*change);
    
    out_overflow:
    	print_overflow_msg(__func__, xdr);
    	return -EIO;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    static int decode_attr_size(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *size)
    {
    
    Al Viro's avatar
    Al Viro committed
    	__be32 *p;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	*size = 0;
    	if (unlikely(bitmap[0] & (FATTR4_WORD0_SIZE - 1U)))
    		return -EIO;
    	if (likely(bitmap[0] & FATTR4_WORD0_SIZE)) {
    
    		p = xdr_inline_decode(xdr, 8);
    		if (unlikely(!p))
    			goto out_overflow;
    
    		p = xdr_decode_hyper(p, size);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		bitmap[0] &= ~FATTR4_WORD0_SIZE;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    
    	dprintk("%s: file size=%Lu\n", __func__, (unsigned long long)*size);
    
    out_overflow:
    	print_overflow_msg(__func__, xdr);
    	return -EIO;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    static int decode_attr_link_support(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res)
    {
    
    Al Viro's avatar
    Al Viro committed
    	__be32 *p;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	*res = 0;
    	if (unlikely(bitmap[0] & (FATTR4_WORD0_LINK_SUPPORT - 1U)))
    		return -EIO;
    	if (likely(bitmap[0] & FATTR4_WORD0_LINK_SUPPORT)) {
    
    		p = xdr_inline_decode(xdr, 4);
    		if (unlikely(!p))
    			goto out_overflow;
    
    		*res = be32_to_cpup(p++);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		bitmap[0] &= ~FATTR4_WORD0_LINK_SUPPORT;
    	}
    
    	dprintk("%s: link support=%s\n", __func__, *res == 0 ? "false" : "true");
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return 0;
    
    out_overflow:
    	print_overflow_msg(__func__, xdr);
    	return -EIO;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    static int decode_attr_symlink_support(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res)
    {
    
    Al Viro's avatar
    Al Viro committed
    	__be32 *p;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	*res = 0;
    	if (unlikely(bitmap[0] & (FATTR4_WORD0_SYMLINK_SUPPORT - 1U)))
    		return -EIO;
    	if (likely(bitmap[0] & FATTR4_WORD0_SYMLINK_SUPPORT)) {
    
    		p = xdr_inline_decode(xdr, 4);
    		if (unlikely(!p))
    			goto out_overflow;
    
    		*res = be32_to_cpup(p++);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		bitmap[0] &= ~FATTR4_WORD0_SYMLINK_SUPPORT;
    	}
    
    	dprintk("%s: symlink support=%s\n", __func__, *res == 0 ? "false" : "true");
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return 0;
    
    out_overflow:
    	print_overflow_msg(__func__, xdr);
    	return -EIO;
    
    static int decode_attr_fsid(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs_fsid *fsid)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    Al Viro's avatar
    Al Viro committed
    	__be32 *p;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	fsid->major = 0;
    	fsid->minor = 0;
    	if (unlikely(bitmap[0] & (FATTR4_WORD0_FSID - 1U)))
    		return -EIO;
    	if (likely(bitmap[0] & FATTR4_WORD0_FSID)) {
    
    		p = xdr_inline_decode(xdr, 16);
    		if (unlikely(!p))
    			goto out_overflow;
    
    		p = xdr_decode_hyper(p, &fsid->major);
    		p = xdr_decode_hyper(p, &fsid->minor);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		bitmap[0] &= ~FATTR4_WORD0_FSID;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    
    	dprintk("%s: fsid=(0x%Lx/0x%Lx)\n", __func__,
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			(unsigned long long)fsid->major,
    			(unsigned long long)fsid->minor);
    
    out_overflow:
    	print_overflow_msg(__func__, xdr);
    	return -EIO;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    static int decode_attr_lease_time(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res)
    {
    
    Al Viro's avatar
    Al Viro committed
    	__be32 *p;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	*res = 60;
    	if (unlikely(bitmap[0] & (FATTR4_WORD0_LEASE_TIME - 1U)))
    		return -EIO;
    	if (likely(bitmap[0] & FATTR4_WORD0_LEASE_TIME)) {
    
    		p = xdr_inline_decode(xdr, 4);
    		if (unlikely(!p))
    			goto out_overflow;
    
    		*res = be32_to_cpup(p++);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		bitmap[0] &= ~FATTR4_WORD0_LEASE_TIME;
    	}
    
    	dprintk("%s: file size=%u\n", __func__, (unsigned int)*res);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return 0;
    
    out_overflow:
    	print_overflow_msg(__func__, xdr);
    	return -EIO;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    static int decode_attr_aclsupport(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res)
    {
    
    Al Viro's avatar
    Al Viro committed
    	__be32 *p;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	*res = ACL4_SUPPORT_ALLOW_ACL|ACL4_SUPPORT_DENY_ACL;
    	if (unlikely(bitmap[0] & (FATTR4_WORD0_ACLSUPPORT - 1U)))
    		return -EIO;
    	if (likely(bitmap[0] & FATTR4_WORD0_ACLSUPPORT)) {
    
    		p = xdr_inline_decode(xdr, 4);
    		if (unlikely(!p))
    			goto out_overflow;
    
    		*res = be32_to_cpup(p++);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		bitmap[0] &= ~FATTR4_WORD0_ACLSUPPORT;
    	}
    
    	dprintk("%s: ACLs supported=%u\n", __func__, (unsigned int)*res);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return 0;
    
    out_overflow:
    	print_overflow_msg(__func__, xdr);
    	return -EIO;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    static int decode_attr_fileid(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *fileid)
    {
    
    Al Viro's avatar
    Al Viro committed
    	__be32 *p;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	*fileid = 0;
    	if (unlikely(bitmap[0] & (FATTR4_WORD0_FILEID - 1U)))
    		return -EIO;
    	if (likely(bitmap[0] & FATTR4_WORD0_FILEID)) {
    
    		p = xdr_inline_decode(xdr, 8);
    		if (unlikely(!p))
    			goto out_overflow;
    
    		p = xdr_decode_hyper(p, fileid);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		bitmap[0] &= ~FATTR4_WORD0_FILEID;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    
    	dprintk("%s: fileid=%Lu\n", __func__, (unsigned long long)*fileid);
    
    out_overflow:
    	print_overflow_msg(__func__, xdr);
    	return -EIO;
    
    static int decode_attr_mounted_on_fileid(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *fileid)
    {
    
    Al Viro's avatar
    Al Viro committed
    	__be32 *p;
    
    
    	*fileid = 0;
    	if (unlikely(bitmap[1] & (FATTR4_WORD1_MOUNTED_ON_FILEID - 1U)))
    		return -EIO;
    	if (likely(bitmap[1] & FATTR4_WORD1_MOUNTED_ON_FILEID)) {
    
    		p = xdr_inline_decode(xdr, 8);
    		if (unlikely(!p))
    			goto out_overflow;
    
    		p = xdr_decode_hyper(p, fileid);
    
    		bitmap[1] &= ~FATTR4_WORD1_MOUNTED_ON_FILEID;
    
    	dprintk("%s: fileid=%Lu\n", __func__, (unsigned long long)*fileid);
    
    out_overflow:
    	print_overflow_msg(__func__, xdr);
    	return -EIO;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    static int decode_attr_files_avail(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res)
    {
    
    Al Viro's avatar
    Al Viro committed
    	__be32 *p;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	int status = 0;
    
    	*res = 0;
    	if (unlikely(bitmap[0] & (FATTR4_WORD0_FILES_AVAIL - 1U)))
    		return -EIO;
    	if (likely(bitmap[0] & FATTR4_WORD0_FILES_AVAIL)) {
    
    		p = xdr_inline_decode(xdr, 8);
    		if (unlikely(!p))
    			goto out_overflow;
    
    		p = xdr_decode_hyper(p, res);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		bitmap[0] &= ~FATTR4_WORD0_FILES_AVAIL;
    	}
    
    	dprintk("%s: files avail=%Lu\n", __func__, (unsigned long long)*res);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return status;
    
    out_overflow:
    	print_overflow_msg(__func__, xdr);
    	return -EIO;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    static int decode_attr_files_free(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res)
    {
    
    Al Viro's avatar
    Al Viro committed
    	__be32 *p;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	int status = 0;
    
    	*res = 0;
    	if (unlikely(bitmap[0] & (FATTR4_WORD0_FILES_FREE - 1U)))
    		return -EIO;
    	if (likely(bitmap[0] & FATTR4_WORD0_FILES_FREE)) {
    
    		p = xdr_inline_decode(xdr, 8);
    		if (unlikely(!p))
    			goto out_overflow;
    
    		p = xdr_decode_hyper(p, res);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		bitmap[0] &= ~FATTR4_WORD0_FILES_FREE;
    	}
    
    	dprintk("%s: files free=%Lu\n", __func__, (unsigned long long)*res);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return status;
    
    out_overflow:
    	print_overflow_msg(__func__, xdr);
    	return -EIO;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    static int decode_attr_files_total(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res)
    {
    
    Al Viro's avatar
    Al Viro committed
    	__be32 *p;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	int status = 0;
    
    	*res = 0;
    	if (unlikely(bitmap[0] & (FATTR4_WORD0_FILES_TOTAL - 1U)))
    		return -EIO;
    	if (likely(bitmap[0] & FATTR4_WORD0_FILES_TOTAL)) {
    
    		p = xdr_inline_decode(xdr, 8);
    		if (unlikely(!p))
    			goto out_overflow;
    
    		p = xdr_decode_hyper(p, res);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		bitmap[0] &= ~FATTR4_WORD0_FILES_TOTAL;
    	}
    
    	dprintk("%s: files total=%Lu\n", __func__, (unsigned long long)*res);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return status;
    
    out_overflow:
    	print_overflow_msg(__func__, xdr);
    	return -EIO;
    
    static int decode_pathname(struct xdr_stream *xdr, struct nfs4_pathname *path)
    {
    
    Al Viro's avatar
    Al Viro committed
    	__be32 *p;
    
    	p = xdr_inline_decode(xdr, 4);
    	if (unlikely(!p))
    		goto out_overflow;
    
    	n = be32_to_cpup(p++);
    
    	if (n == 0)
    		goto root_path;
    
    	dprintk("path ");
    	path->ncomponents = 0;
    	while (path->ncomponents < n) {
    		struct nfs4_string *component = &path->components[path->ncomponents];
    		status = decode_opaque_inline(xdr, &component->len, &component->data);
    		if (unlikely(status != 0))
    			goto out_eio;
    		if (path->ncomponents != n)
    			dprintk("/");
    		dprintk("%s", component->data);
    		if (path->ncomponents < NFS4_PATHNAME_MAXCOMPONENTS)
    			path->ncomponents++;
    		else {
    			dprintk("cannot parse %d components in path\n", n);
    			goto out_eio;
    		}
    	}
    out:
    	dprintk("\n");
    	return status;
    
    root_path:
    /* a root pathname is sent as a zero component4 */
    	path->ncomponents = 1;
    	path->components[0].len=0;
    	path->components[0].data=NULL;
    	dprintk("path /\n");
    	goto out;
    
    out_eio:
    	dprintk(" status %d", status);
    	status = -EIO;
    	goto out;
    
    out_overflow:
    	print_overflow_msg(__func__, xdr);
    	return -EIO;
    
    }
    
    static int decode_attr_fs_locations(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs4_fs_locations *res)
    
    Al Viro's avatar
    Al Viro committed
    	__be32 *p;
    
    	int status = -EIO;
    
    	if (unlikely(bitmap[0] & (FATTR4_WORD0_FS_LOCATIONS -1U)))
    		goto out;
    	status = 0;
    	if (unlikely(!(bitmap[0] & FATTR4_WORD0_FS_LOCATIONS)))
    		goto out;
    
    	dprintk("%s: fsroot ", __func__);
    
    	status = decode_pathname(xdr, &res->fs_path);
    
    	if (unlikely(status != 0))
    		goto out;
    
    	p = xdr_inline_decode(xdr, 4);
    	if (unlikely(!p))
    		goto out_overflow;
    
    	n = be32_to_cpup(p++);
    
    	if (n <= 0)
    		goto out_eio;
    	res->nlocations = 0;
    	while (res->nlocations < n) {
    
    		struct nfs4_fs_location *loc = &res->locations[res->nlocations];
    
    		p = xdr_inline_decode(xdr, 4);
    		if (unlikely(!p))
    			goto out_overflow;
    
    		m = be32_to_cpup(p++);
    
    		dprintk("%s: servers ", __func__);
    
    		while (loc->nservers < m) {
    			struct nfs4_string *server = &loc->servers[loc->nservers];
    			status = decode_opaque_inline(xdr, &server->len, &server->data);
    			if (unlikely(status != 0))
    				goto out_eio;
    			dprintk("%s ", server->data);
    			if (loc->nservers < NFS4_FS_LOCATION_MAXSERVERS)
    				loc->nservers++;
    			else {
    
    				unsigned int i;
    				dprintk("%s: using first %u of %u servers "
    					"returned for location %u\n",
    
    						NFS4_FS_LOCATION_MAXSERVERS,
    						m, res->nlocations);
    
    				for (i = loc->nservers; i < m; i++) {
    
    					unsigned int len;
    
    					char *data;
    					status = decode_opaque_inline(xdr, &len, &data);
    					if (unlikely(status != 0))
    						goto out_eio;
    				}
    			}
    		}
    		status = decode_pathname(xdr, &loc->rootpath);
    
    		if (unlikely(status != 0))
    			goto out_eio;
    
    		if (res->nlocations < NFS4_FS_LOCATIONS_MAXENTRIES)
    
    	if (res->nlocations != 0)
    		status = NFS_ATTR_FATTR_V4_REFERRAL;
    
    	dprintk("%s: fs_locations done, error = %d\n", __func__, status);
    
    out_overflow:
    	print_overflow_msg(__func__, xdr);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    static int decode_attr_maxfilesize(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res)
    {
    
    Al Viro's avatar
    Al Viro committed
    	__be32 *p;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	int status = 0;
    
    	*res = 0;
    	if (unlikely(bitmap[0] & (FATTR4_WORD0_MAXFILESIZE - 1U)))
    		return -EIO;
    	if (likely(bitmap[0] & FATTR4_WORD0_MAXFILESIZE)) {
    
    		p = xdr_inline_decode(xdr, 8);
    		if (unlikely(!p))
    			goto out_overflow;
    
    		p = xdr_decode_hyper(p, res);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		bitmap[0] &= ~FATTR4_WORD0_MAXFILESIZE;
    	}
    
    	dprintk("%s: maxfilesize=%Lu\n", __func__, (unsigned long long)*res);