Skip to content
Snippets Groups Projects
nfs4xdr.c 187 KiB
Newer Older
  • Learn to ignore specific revisions
  • Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    static int decode_attr_space_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[1] & (FATTR4_WORD1_SPACE_FREE - 1U)))
    		return -EIO;
    	if (likely(bitmap[1] & FATTR4_WORD1_SPACE_FREE)) {
    
    		p = xdr_inline_decode(xdr, 8);
    		if (unlikely(!p))
    			goto out_overflow;
    
    		xdr_decode_hyper(p, res);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		bitmap[1] &= ~FATTR4_WORD1_SPACE_FREE;
    	}
    
    	dprintk("%s: space 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_space_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[1] & (FATTR4_WORD1_SPACE_TOTAL - 1U)))
    		return -EIO;
    	if (likely(bitmap[1] & FATTR4_WORD1_SPACE_TOTAL)) {
    
    		p = xdr_inline_decode(xdr, 8);
    		if (unlikely(!p))
    			goto out_overflow;
    
    		xdr_decode_hyper(p, res);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		bitmap[1] &= ~FATTR4_WORD1_SPACE_TOTAL;
    	}
    
    	dprintk("%s: space 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;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    static int decode_attr_space_used(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *used)
    {
    
    Al Viro's avatar
    Al Viro committed
    	__be32 *p;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	*used = 0;
    	if (unlikely(bitmap[1] & (FATTR4_WORD1_SPACE_USED - 1U)))
    		return -EIO;
    	if (likely(bitmap[1] & FATTR4_WORD1_SPACE_USED)) {
    
    		p = xdr_inline_decode(xdr, 8);
    		if (unlikely(!p))
    			goto out_overflow;
    
    		xdr_decode_hyper(p, used);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		bitmap[1] &= ~FATTR4_WORD1_SPACE_USED;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    
    	dprintk("%s: space used=%Lu\n", __func__,
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			(unsigned long long)*used);
    
    out_overflow:
    	print_overflow_msg(__func__, xdr);
    	return -EIO;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    static int decode_attr_time(struct xdr_stream *xdr, struct timespec *time)
    {
    
    Al Viro's avatar
    Al Viro committed
    	__be32 *p;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	uint64_t sec;
    	uint32_t nsec;
    
    
    	p = xdr_inline_decode(xdr, 12);
    	if (unlikely(!p))
    		goto out_overflow;
    
    	p = xdr_decode_hyper(p, &sec);
    
    	nsec = be32_to_cpup(p);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	time->tv_sec = (time_t)sec;
    	time->tv_nsec = (long)nsec;
    	return 0;
    
    out_overflow:
    	print_overflow_msg(__func__, xdr);
    	return -EIO;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    static int decode_attr_time_access(struct xdr_stream *xdr, uint32_t *bitmap, struct timespec *time)
    {
    	int status = 0;
    
    	time->tv_sec = 0;
    	time->tv_nsec = 0;
    	if (unlikely(bitmap[1] & (FATTR4_WORD1_TIME_ACCESS - 1U)))
    		return -EIO;
    	if (likely(bitmap[1] & FATTR4_WORD1_TIME_ACCESS)) {
    		status = decode_attr_time(xdr, time);
    
    		if (status == 0)
    			status = NFS_ATTR_FATTR_ATIME;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		bitmap[1] &= ~FATTR4_WORD1_TIME_ACCESS;
    	}
    
    	dprintk("%s: atime=%ld\n", __func__, (long)time->tv_sec);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return status;
    }
    
    static int decode_attr_time_metadata(struct xdr_stream *xdr, uint32_t *bitmap, struct timespec *time)
    {
    	int status = 0;
    
    	time->tv_sec = 0;
    	time->tv_nsec = 0;
    	if (unlikely(bitmap[1] & (FATTR4_WORD1_TIME_METADATA - 1U)))
    		return -EIO;
    	if (likely(bitmap[1] & FATTR4_WORD1_TIME_METADATA)) {
    		status = decode_attr_time(xdr, time);
    
    		if (status == 0)
    			status = NFS_ATTR_FATTR_CTIME;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		bitmap[1] &= ~FATTR4_WORD1_TIME_METADATA;
    	}
    
    	dprintk("%s: ctime=%ld\n", __func__, (long)time->tv_sec);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return status;
    }
    
    
    static int decode_attr_time_delta(struct xdr_stream *xdr, uint32_t *bitmap,
    				  struct timespec *time)
    {
    	int status = 0;
    
    	time->tv_sec = 0;
    	time->tv_nsec = 0;
    	if (unlikely(bitmap[1] & (FATTR4_WORD1_TIME_DELTA - 1U)))
    		return -EIO;
    	if (likely(bitmap[1] & FATTR4_WORD1_TIME_DELTA)) {
    		status = decode_attr_time(xdr, time);
    		bitmap[1] &= ~FATTR4_WORD1_TIME_DELTA;
    	}
    	dprintk("%s: time_delta=%ld %ld\n", __func__, (long)time->tv_sec,
    		(long)time->tv_nsec);
    	return status;
    }
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    static int decode_attr_time_modify(struct xdr_stream *xdr, uint32_t *bitmap, struct timespec *time)
    {
    	int status = 0;
    
    	time->tv_sec = 0;
    	time->tv_nsec = 0;
    	if (unlikely(bitmap[1] & (FATTR4_WORD1_TIME_MODIFY - 1U)))
    		return -EIO;
    	if (likely(bitmap[1] & FATTR4_WORD1_TIME_MODIFY)) {
    		status = decode_attr_time(xdr, time);
    
    		if (status == 0)
    			status = NFS_ATTR_FATTR_MTIME;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		bitmap[1] &= ~FATTR4_WORD1_TIME_MODIFY;
    	}
    
    	dprintk("%s: mtime=%ld\n", __func__, (long)time->tv_sec);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return status;
    }
    
    
    Al Viro's avatar
    Al Viro committed
    static int verify_attr_len(struct xdr_stream *xdr, __be32 *savep, uint32_t attrlen)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	unsigned int attrwords = XDR_QUADLEN(attrlen);
    	unsigned int nwords = xdr->p - savep;
    
    	if (unlikely(attrwords != nwords)) {
    
    		dprintk("%s: server returned incorrect attribute length: "
    			"%u %c %u\n",
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    				attrwords << 2,
    				(attrwords < nwords) ? '<' : '>',
    				nwords << 2);
    		return -EIO;
    	}
    	return 0;
    }
    
    static int decode_change_info(struct xdr_stream *xdr, struct nfs4_change_info *cinfo)
    {
    
    Al Viro's avatar
    Al Viro committed
    	__be32 *p;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	p = xdr_inline_decode(xdr, 20);
    	if (unlikely(!p))
    		goto out_overflow;
    
    	cinfo->atomic = be32_to_cpup(p++);
    
    	p = xdr_decode_hyper(p, &cinfo->before);
    
    	xdr_decode_hyper(p, &cinfo->after);
    
    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_access(struct xdr_stream *xdr, struct nfs4_accessres *access)
    {
    
    Al Viro's avatar
    Al Viro committed
    	__be32 *p;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	uint32_t supp, acc;
    	int status;
    
    	status = decode_op_hdr(xdr, OP_ACCESS);
    	if (status)
    		return status;
    
    	p = xdr_inline_decode(xdr, 8);
    	if (unlikely(!p))
    		goto out_overflow;
    
    	supp = be32_to_cpup(p++);
    
    	acc = be32_to_cpup(p);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	access->supported = supp;
    	access->access = acc;
    	return 0;
    
    out_overflow:
    	print_overflow_msg(__func__, xdr);
    	return -EIO;
    
    static int decode_opaque_fixed(struct xdr_stream *xdr, void *buf, size_t len)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    Al Viro's avatar
    Al Viro committed
    	__be32 *p;
    
    
    	p = xdr_inline_decode(xdr, len);
    	if (likely(p)) {
    		memcpy(buf, p, len);
    		return 0;
    	}
    	print_overflow_msg(__func__, xdr);
    	return -EIO;
    }
    
    static int decode_stateid(struct xdr_stream *xdr, nfs4_stateid *stateid)
    {
    	return decode_opaque_fixed(xdr, stateid->data, NFS4_STATEID_SIZE);
    }
    
    static int decode_close(struct xdr_stream *xdr, struct nfs_closeres *res)
    {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	int status;
    
    	status = decode_op_hdr(xdr, OP_CLOSE);
    
    	if (status != -EIO)
    		nfs_increment_open_seqid(status, res->seqid);
    
    	if (!status)
    		status = decode_stateid(xdr, &res->stateid);
    	return status;
    
    static int decode_verifier(struct xdr_stream *xdr, void *verifier)
    {
    	return decode_opaque_fixed(xdr, verifier, 8);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    static int decode_commit(struct xdr_stream *xdr, struct nfs_writeres *res)
    {
    	int status;
    
    	status = decode_op_hdr(xdr, OP_COMMIT);
    
    	if (!status)
    		status = decode_verifier(xdr, res->verf->verifier);
    	return status;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    static int decode_create(struct xdr_stream *xdr, struct nfs4_change_info *cinfo)
    {
    
    Al Viro's avatar
    Al Viro committed
    	__be32 *p;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	uint32_t bmlen;
    	int status;
    
    	status = decode_op_hdr(xdr, OP_CREATE);
    	if (status)
    		return status;
    	if ((status = decode_change_info(xdr, cinfo)))
    		return status;
    
    	p = xdr_inline_decode(xdr, 4);
    	if (unlikely(!p))
    		goto out_overflow;
    
    	bmlen = be32_to_cpup(p);
    
    	p = xdr_inline_decode(xdr, bmlen << 2);
    	if (likely(p))
    		return 0;
    out_overflow:
    	print_overflow_msg(__func__, xdr);
    	return -EIO;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    static int decode_server_caps(struct xdr_stream *xdr, struct nfs4_server_caps_res *res)
    {
    
    Al Viro's avatar
    Al Viro committed
    	__be32 *savep;
    
    	uint32_t attrlen, bitmap[3] = {0};
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	int status;
    
    	if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0)
    		goto xdr_error;
    	if ((status = decode_attr_bitmap(xdr, bitmap)) != 0)
    		goto xdr_error;
    	if ((status = decode_attr_length(xdr, &attrlen, &savep)) != 0)
    		goto xdr_error;
    	if ((status = decode_attr_supported(xdr, bitmap, res->attr_bitmask)) != 0)
    		goto xdr_error;
    
    	if ((status = decode_attr_fh_expire_type(xdr, bitmap,
    						 &res->fh_expire_type)) != 0)
    		goto xdr_error;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if ((status = decode_attr_link_support(xdr, bitmap, &res->has_links)) != 0)
    		goto xdr_error;
    	if ((status = decode_attr_symlink_support(xdr, bitmap, &res->has_symlinks)) != 0)
    		goto xdr_error;
    	if ((status = decode_attr_aclsupport(xdr, bitmap, &res->acl_bitmask)) != 0)
    		goto xdr_error;
    	status = verify_attr_len(xdr, savep, attrlen);
    xdr_error:
    
    	dprintk("%s: xdr returned %d!\n", __func__, -status);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return status;
    }
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    static int decode_statfs(struct xdr_stream *xdr, struct nfs_fsstat *fsstat)
    {
    
    Al Viro's avatar
    Al Viro committed
    	__be32 *savep;
    
    	uint32_t attrlen, bitmap[3] = {0};
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	int status;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0)
    		goto xdr_error;
    	if ((status = decode_attr_bitmap(xdr, bitmap)) != 0)
    		goto xdr_error;
    	if ((status = decode_attr_length(xdr, &attrlen, &savep)) != 0)
    		goto xdr_error;
    
    	if ((status = decode_attr_files_avail(xdr, bitmap, &fsstat->afiles)) != 0)
    		goto xdr_error;
    	if ((status = decode_attr_files_free(xdr, bitmap, &fsstat->ffiles)) != 0)
    		goto xdr_error;
    	if ((status = decode_attr_files_total(xdr, bitmap, &fsstat->tfiles)) != 0)
    		goto xdr_error;
    	if ((status = decode_attr_space_avail(xdr, bitmap, &fsstat->abytes)) != 0)
    		goto xdr_error;
    	if ((status = decode_attr_space_free(xdr, bitmap, &fsstat->fbytes)) != 0)
    		goto xdr_error;
    	if ((status = decode_attr_space_total(xdr, bitmap, &fsstat->tbytes)) != 0)
    		goto xdr_error;
    
    	status = verify_attr_len(xdr, savep, attrlen);
    xdr_error:
    
    	dprintk("%s: xdr returned %d!\n", __func__, -status);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return status;
    }
    
    static int decode_pathconf(struct xdr_stream *xdr, struct nfs_pathconf *pathconf)
    {
    
    Al Viro's avatar
    Al Viro committed
    	__be32 *savep;
    
    	uint32_t attrlen, bitmap[3] = {0};
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	int status;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0)
    		goto xdr_error;
    	if ((status = decode_attr_bitmap(xdr, bitmap)) != 0)
    		goto xdr_error;
    	if ((status = decode_attr_length(xdr, &attrlen, &savep)) != 0)
    		goto xdr_error;
    
    	if ((status = decode_attr_maxlink(xdr, bitmap, &pathconf->max_link)) != 0)
    		goto xdr_error;
    	if ((status = decode_attr_maxname(xdr, bitmap, &pathconf->max_namelen)) != 0)
    		goto xdr_error;
    
    	status = verify_attr_len(xdr, savep, attrlen);
    xdr_error:
    
    	dprintk("%s: xdr returned %d!\n", __func__, -status);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return status;
    }
    
    
    static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap,
    		struct nfs_fattr *fattr, struct nfs_fh *fh,
    
    		struct nfs4_fs_locations *fs_loc,
    
    		const struct nfs_server *server)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	int status;
    	umode_t fmode = 0;
    
    	int32_t err;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	status = decode_attr_type(xdr, bitmap, &type);
    	if (status < 0)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		goto xdr_error;
    
    	fattr->mode = 0;
    	if (status != 0) {
    		fattr->mode |= nfs_type2fmt[type];
    		fattr->valid |= status;
    	}
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	status = decode_attr_change(xdr, bitmap, &fattr->change_attr);
    	if (status < 0)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		goto xdr_error;
    
    
    	status = decode_attr_size(xdr, bitmap, &fattr->size);
    	if (status < 0)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		goto xdr_error;
    
    
    	status = decode_attr_fsid(xdr, bitmap, &fattr->fsid);
    	if (status < 0)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		goto xdr_error;
    
    	err = 0;
    	status = decode_attr_error(xdr, bitmap, &err);
    
    	if (status < 0)
    		goto xdr_error;
    
    	if (err == -NFS4ERR_WRONGSEC)
    		nfs_fixup_secinfo_attributes(fattr, fh);
    
    
    	status = decode_attr_filehandle(xdr, bitmap, fh);
    	if (status < 0)
    		goto xdr_error;
    
    
    	status = decode_attr_fileid(xdr, bitmap, &fattr->fileid);
    	if (status < 0)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		goto xdr_error;
    
    	status = decode_attr_fs_locations(xdr, bitmap, fs_loc);
    
    	if (status < 0)
    
    
    	status = decode_attr_mode(xdr, bitmap, &fmode);
    	if (status < 0)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		goto xdr_error;
    
    	if (status != 0) {
    		fattr->mode |= fmode;
    		fattr->valid |= status;
    	}
    
    
    	status = decode_attr_nlink(xdr, bitmap, &fattr->nlink);
    	if (status < 0)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		goto xdr_error;
    
    	status = decode_attr_owner(xdr, bitmap, server, &fattr->uid, fattr->owner_name);
    
    	if (status < 0)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		goto xdr_error;
    
    	status = decode_attr_group(xdr, bitmap, server, &fattr->gid, fattr->group_name);
    
    	if (status < 0)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		goto xdr_error;
    
    
    	status = decode_attr_rdev(xdr, bitmap, &fattr->rdev);
    	if (status < 0)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		goto xdr_error;
    
    
    	status = decode_attr_space_used(xdr, bitmap, &fattr->du.nfs3.used);
    	if (status < 0)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		goto xdr_error;
    
    
    	status = decode_attr_time_access(xdr, bitmap, &fattr->atime);
    	if (status < 0)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		goto xdr_error;
    
    
    	status = decode_attr_time_metadata(xdr, bitmap, &fattr->ctime);
    	if (status < 0)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		goto xdr_error;
    
    
    	status = decode_attr_time_modify(xdr, bitmap, &fattr->mtime);
    	if (status < 0)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		goto xdr_error;
    
    	status = decode_attr_mounted_on_fileid(xdr, bitmap, &fattr->mounted_on_fileid);
    
    	if (status < 0)
    
    xdr_error:
    	dprintk("%s: xdr returned %d\n", __func__, -status);
    	return status;
    }
    
    static int decode_getfattr_generic(struct xdr_stream *xdr, struct nfs_fattr *fattr,
    
    		struct nfs_fh *fh, struct nfs4_fs_locations *fs_loc,
    		const struct nfs_server *server)
    
    {
    	__be32 *savep;
    	uint32_t attrlen,
    
    	int status;
    
    	status = decode_op_hdr(xdr, OP_GETATTR);
    	if (status < 0)
    		goto xdr_error;
    
    	status = decode_attr_bitmap(xdr, bitmap);
    	if (status < 0)
    		goto xdr_error;
    
    	status = decode_attr_length(xdr, &attrlen, &savep);
    	if (status < 0)
    		goto xdr_error;
    
    
    	status = decode_getfattr_attrs(xdr, bitmap, fattr, fh, fs_loc, server);
    
    	if (status < 0)
    		goto xdr_error;
    
    
    	status = verify_attr_len(xdr, savep, attrlen);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    xdr_error:
    
    	dprintk("%s: xdr returned %d\n", __func__, -status);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return status;
    }
    
    
    static int decode_getfattr(struct xdr_stream *xdr, struct nfs_fattr *fattr,
    
    		const struct nfs_server *server)
    
    	return decode_getfattr_generic(xdr, fattr, NULL, NULL, server);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    /*
     * Decode potentially multiple layout types. Currently we only support
     * one layout driver per file system.
     */
    static int decode_first_pnfs_layout_type(struct xdr_stream *xdr,
    					 uint32_t *layouttype)
    {
    	uint32_t *p;
    	int num;
    
    	p = xdr_inline_decode(xdr, 4);
    	if (unlikely(!p))
    		goto out_overflow;
    	num = be32_to_cpup(p);
    
    	/* pNFS is not supported by the underlying file system */
    	if (num == 0) {
    		*layouttype = 0;
    		return 0;
    	}
    	if (num > 1)
    
    		printk(KERN_INFO "NFS: %s: Warning: Multiple pNFS layout "
    			"drivers per filesystem not supported\n", __func__);
    
    
    	/* Decode and set first layout type, move xdr->p past unused types */
    	p = xdr_inline_decode(xdr, num * 4);
    	if (unlikely(!p))
    		goto out_overflow;
    	*layouttype = be32_to_cpup(p);
    	return 0;
    out_overflow:
    	print_overflow_msg(__func__, xdr);
    	return -EIO;
    }
    
    /*
     * The type of file system exported.
     * Note we must ensure that layouttype is set in any non-error case.
     */
    static int decode_attr_pnfstype(struct xdr_stream *xdr, uint32_t *bitmap,
    				uint32_t *layouttype)
    {
    	int status = 0;
    
    	dprintk("%s: bitmap is %x\n", __func__, bitmap[1]);
    	if (unlikely(bitmap[1] & (FATTR4_WORD1_FS_LAYOUT_TYPES - 1U)))
    		return -EIO;
    	if (bitmap[1] & FATTR4_WORD1_FS_LAYOUT_TYPES) {
    		status = decode_first_pnfs_layout_type(xdr, layouttype);
    		bitmap[1] &= ~FATTR4_WORD1_FS_LAYOUT_TYPES;
    	} else
    		*layouttype = 0;
    	return status;
    }
    
    
    /*
     * The prefered block size for layout directed io
     */
    static int decode_attr_layout_blksize(struct xdr_stream *xdr, uint32_t *bitmap,
    				      uint32_t *res)
    {
    	__be32 *p;
    
    	dprintk("%s: bitmap is %x\n", __func__, bitmap[2]);
    	*res = 0;
    	if (bitmap[2] & FATTR4_WORD2_LAYOUT_BLKSIZE) {
    		p = xdr_inline_decode(xdr, 4);
    		if (unlikely(!p)) {
    			print_overflow_msg(__func__, xdr);
    			return -EIO;
    		}
    		*res = be32_to_cpup(p);
    		bitmap[2] &= ~FATTR4_WORD2_LAYOUT_BLKSIZE;
    	}
    	return 0;
    }
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    static int decode_fsinfo(struct xdr_stream *xdr, struct nfs_fsinfo *fsinfo)
    {
    
    Al Viro's avatar
    Al Viro committed
    	__be32 *savep;
    
    	uint32_t attrlen, bitmap[3];
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	int status;
    
    	if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0)
    		goto xdr_error;
    	if ((status = decode_attr_bitmap(xdr, bitmap)) != 0)
    		goto xdr_error;
    	if ((status = decode_attr_length(xdr, &attrlen, &savep)) != 0)
    		goto xdr_error;
    
    	fsinfo->rtmult = fsinfo->wtmult = 512;	/* ??? */
    
    	if ((status = decode_attr_lease_time(xdr, bitmap, &fsinfo->lease_time)) != 0)
    		goto xdr_error;
    	if ((status = decode_attr_maxfilesize(xdr, bitmap, &fsinfo->maxfilesize)) != 0)
    		goto xdr_error;
    	if ((status = decode_attr_maxread(xdr, bitmap, &fsinfo->rtmax)) != 0)
    		goto xdr_error;
    	fsinfo->rtpref = fsinfo->dtpref = fsinfo->rtmax;
    	if ((status = decode_attr_maxwrite(xdr, bitmap, &fsinfo->wtmax)) != 0)
    		goto xdr_error;
    	fsinfo->wtpref = fsinfo->wtmax;
    
    	status = decode_attr_time_delta(xdr, bitmap, &fsinfo->time_delta);
    
    	if (status != 0)
    		goto xdr_error;
    	status = decode_attr_pnfstype(xdr, bitmap, &fsinfo->layouttype);
    
    	if (status != 0)
    		goto xdr_error;
    
    	status = decode_attr_layout_blksize(xdr, bitmap, &fsinfo->blksize);
    	if (status)
    		goto xdr_error;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	status = verify_attr_len(xdr, savep, attrlen);
    xdr_error:
    
    	dprintk("%s: xdr returned %d!\n", __func__, -status);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return status;
    }
    
    static int decode_getfh(struct xdr_stream *xdr, struct nfs_fh *fh)
    {
    
    Al Viro's avatar
    Al Viro committed
    	__be32 *p;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	uint32_t len;
    	int status;
    
    
    	/* Zero handle first to allow comparisons */
    	memset(fh, 0, sizeof(*fh));
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	status = decode_op_hdr(xdr, OP_GETFH);
    	if (status)
    		return status;
    
    
    	p = xdr_inline_decode(xdr, 4);
    	if (unlikely(!p))
    		goto out_overflow;
    
    	len = be32_to_cpup(p);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (len > NFS4_FHSIZE)
    		return -EIO;
    	fh->size = len;
    
    	p = xdr_inline_decode(xdr, len);
    	if (unlikely(!p))
    		goto out_overflow;
    
    	memcpy(fh->data, p, len);
    
    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_link(struct xdr_stream *xdr, struct nfs4_change_info *cinfo)
    {
    	int status;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	status = decode_op_hdr(xdr, OP_LINK);
    	if (status)
    		return status;
    	return decode_change_info(xdr, cinfo);
    }
    
    /*
     * We create the owner, so we know a proper owner.id length is 4.
     */
    
    static int decode_lock_denied (struct xdr_stream *xdr, struct file_lock *fl)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	uint64_t offset, length, clientid;
    
    Al Viro's avatar
    Al Viro committed
    	__be32 *p;
    
    	uint32_t namelen, type;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	p = xdr_inline_decode(xdr, 32); /* read 32 bytes */
    
    	if (unlikely(!p))
    		goto out_overflow;
    
    	p = xdr_decode_hyper(p, &offset); /* read 2 8-byte long words */
    
    	p = xdr_decode_hyper(p, &length);
    
    	type = be32_to_cpup(p++); /* 4 byte read */
    	if (fl != NULL) { /* manipulate file lock */
    
    		fl->fl_start = (loff_t)offset;
    		fl->fl_end = fl->fl_start + (loff_t)length - 1;
    		if (length == ~(uint64_t)0)
    			fl->fl_end = OFFSET_MAX;
    		fl->fl_type = F_WRLCK;
    		if (type & 1)
    			fl->fl_type = F_RDLCK;
    		fl->fl_pid = 0;
    	}
    
    	p = xdr_decode_hyper(p, &clientid); /* read 8 bytes */
    	namelen = be32_to_cpup(p); /* read 4 bytes */  /* have read all 32 bytes now */
    	p = xdr_inline_decode(xdr, namelen); /* variable size field */
    
    	if (likely(p))
    		return -NFS4ERR_DENIED;
    out_overflow:
    	print_overflow_msg(__func__, xdr);
    	return -EIO;
    
    static int decode_lock(struct xdr_stream *xdr, struct nfs_lock_res *res)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	int status;
    
    	status = decode_op_hdr(xdr, OP_LOCK);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (status == 0) {
    
    		status = decode_stateid(xdr, &res->stateid);
    		if (unlikely(status))
    			goto out;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	} else if (status == -NFS4ERR_DENIED)
    
    		status = decode_lock_denied(xdr, NULL);
    	if (res->open_seqid != NULL)
    		nfs_increment_open_seqid(status, res->open_seqid);
    	nfs_increment_lock_seqid(status, res->lock_seqid);
    out:
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return status;
    }
    
    
    static int decode_lockt(struct xdr_stream *xdr, struct nfs_lockt_res *res)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	int status;
    	status = decode_op_hdr(xdr, OP_LOCKT);
    	if (status == -NFS4ERR_DENIED)
    
    		return decode_lock_denied(xdr, res->denied);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return status;
    }
    
    
    static int decode_locku(struct xdr_stream *xdr, struct nfs_locku_res *res)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	int status;
    
    	status = decode_op_hdr(xdr, OP_LOCKU);
    
    	if (status != -EIO)
    		nfs_increment_lock_seqid(status, res->seqid);
    
    	if (status == 0)
    		status = decode_stateid(xdr, &res->stateid);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return status;
    }
    
    
    static int decode_release_lockowner(struct xdr_stream *xdr)
    {
    	return decode_op_hdr(xdr, OP_RELEASE_LOCKOWNER);
    }
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    static int decode_lookup(struct xdr_stream *xdr)
    {
    	return decode_op_hdr(xdr, OP_LOOKUP);
    }
    
    /* This is too sick! */
    static int decode_space_limit(struct xdr_stream *xdr, u64 *maxsize)
    {
    
    Andy Adamson's avatar
    Andy Adamson committed
    	__be32 *p;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	uint32_t limit_type, nblocks, blocksize;
    
    
    	p = xdr_inline_decode(xdr, 12);
    	if (unlikely(!p))
    		goto out_overflow;
    
    	limit_type = be32_to_cpup(p++);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	switch (limit_type) {
    
    Andy Adamson's avatar
    Andy Adamson committed
    	case 1:
    
    		xdr_decode_hyper(p, maxsize);
    
    Andy Adamson's avatar
    Andy Adamson committed
    		break;
    	case 2:
    
    		nblocks = be32_to_cpup(p++);
    
    		blocksize = be32_to_cpup(p);
    
    Andy Adamson's avatar
    Andy Adamson committed
    		*maxsize = (uint64_t)nblocks * (uint64_t)blocksize;
    
    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_delegation(struct xdr_stream *xdr, struct nfs_openres *res)
    {
    
    Andy Adamson's avatar
    Andy Adamson committed
    	__be32 *p;
    	uint32_t delegation_type;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	p = xdr_inline_decode(xdr, 4);
    	if (unlikely(!p))
    		goto out_overflow;
    
    	delegation_type = be32_to_cpup(p);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (delegation_type == NFS4_OPEN_DELEGATE_NONE) {
    		res->delegation_type = 0;
    		return 0;
    	}
    
    	status = decode_stateid(xdr, &res->delegation);
    	if (unlikely(status))
    		return status;
    
    	p = xdr_inline_decode(xdr, 4);
    	if (unlikely(!p))
    		goto out_overflow;
    
    	res->do_recall = be32_to_cpup(p);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	switch (delegation_type) {
    
    Andy Adamson's avatar
    Andy Adamson committed
    	case NFS4_OPEN_DELEGATE_READ:
    		res->delegation_type = FMODE_READ;
    		break;
    	case NFS4_OPEN_DELEGATE_WRITE:
    		res->delegation_type = FMODE_WRITE|FMODE_READ;
    		if (decode_space_limit(xdr, &res->maxsize) < 0)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    				return -EIO;
    	}
    
    	return decode_ace(xdr, NULL, res->server->nfs_client);
    
    out_overflow:
    	print_overflow_msg(__func__, xdr);
    	return -EIO;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    static int decode_open(struct xdr_stream *xdr, struct nfs_openres *res)
    {
    
    Andy Adamson's avatar
    Andy Adamson committed
    	__be32 *p;
    
    Andy Adamson's avatar
    Andy Adamson committed
    	int status;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    Andy Adamson's avatar
    Andy Adamson committed
    	status = decode_op_hdr(xdr, OP_OPEN);
    
    	if (status != -EIO)
    		nfs_increment_open_seqid(status, res->seqid);
    
    	if (!status)
    		status = decode_stateid(xdr, &res->stateid);
    	if (unlikely(status))
    
    Andy Adamson's avatar
    Andy Adamson committed
    		return status;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    Andy Adamson's avatar
    Andy Adamson committed
    	decode_change_info(xdr, &res->cinfo);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	p = xdr_inline_decode(xdr, 8);
    	if (unlikely(!p))
    		goto out_overflow;
    
    	res->rflags = be32_to_cpup(p++);
    
    	bmlen = be32_to_cpup(p);
    
    Andy Adamson's avatar
    Andy Adamson committed
    	if (bmlen > 10)
    		goto xdr_error;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	p = xdr_inline_decode(xdr, bmlen << 2);
    	if (unlikely(!p))
    		goto out_overflow;
    
    	savewords = min_t(uint32_t, bmlen, NFS4_BITMAP_SIZE);
    	for (i = 0; i < savewords; ++i)
    
    		res->attrset[i] = be32_to_cpup(p++);
    
    	for (; i < NFS4_BITMAP_SIZE; i++)
    		res->attrset[i] = 0;
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return decode_delegation(xdr, res);
    xdr_error:
    
    	dprintk("%s: Bitmap too large! Length = %u\n", __func__, bmlen);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return -EIO;
    
    out_overflow:
    	print_overflow_msg(__func__, xdr);
    	return -EIO;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    static int decode_open_confirm(struct xdr_stream *xdr, struct nfs_open_confirmres *res)
    {
    	int status;
    
    
    Andy Adamson's avatar
    Andy Adamson committed
    	status = decode_op_hdr(xdr, OP_OPEN_CONFIRM);
    
    	if (status != -EIO)
    		nfs_increment_open_seqid(status, res->seqid);
    
    	if (!status)
    		status = decode_stateid(xdr, &res->stateid);
    	return status;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    static int decode_open_downgrade(struct xdr_stream *xdr, struct nfs_closeres *res)
    {
    	int status;
    
    	status = decode_op_hdr(xdr, OP_OPEN_DOWNGRADE);
    
    	if (status != -EIO)
    		nfs_increment_open_seqid(status, res->seqid);
    
    	if (!status)
    		status = decode_stateid(xdr, &res->stateid);
    	return status;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    static int decode_putfh(struct xdr_stream *xdr)
    {
    	return decode_op_hdr(xdr, OP_PUTFH);
    }
    
    static int decode_putrootfh(struct xdr_stream *xdr)
    {
    	return decode_op_hdr(xdr, OP_PUTROOTFH);
    }
    
    static int decode_read(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs_readres *res)
    {
    	struct kvec *iov = req->rq_rcv_buf.head;
    
    Al Viro's avatar
    Al Viro committed
    	__be32 *p;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	uint32_t count, eof, recvd, hdrlen;
    	int status;
    
    	status = decode_op_hdr(xdr, OP_READ);
    	if (status)
    		return status;
    
    	p = xdr_inline_decode(xdr, 8);
    	if (unlikely(!p))
    		goto out_overflow;
    
    	eof = be32_to_cpup(p++);
    
    	count = be32_to_cpup(p);
    
    	hdrlen = (u8 *) xdr->p - (u8 *) iov->iov_base;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	recvd = req->rq_rcv_buf.len - hdrlen;
    	if (count > recvd) {
    
    		dprintk("NFS: server cheating in read reply: "
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    				"count %u > recvd %u\n", count, recvd);
    		count = recvd;
    		eof = 0;
    	}
    	xdr_read_pages(xdr, count);
    	res->eof = eof;
    	res->count = count;
    	return 0;
    
    out_overflow:
    	print_overflow_msg(__func__, xdr);
    	return -EIO;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs4_readdir_res *readdir)
    {
    	struct xdr_buf	*rcvbuf = &req->rq_rcv_buf;
    	struct kvec	*iov = rcvbuf->head;
    
    	size_t		hdrlen;
    	u32		recvd, pglen = rcvbuf->page_len;
    	int		status;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	status = decode_op_hdr(xdr, OP_READDIR);
    
    	if (!status)
    		status = decode_verifier(xdr, readdir->verifier.data);
    	if (unlikely(status))
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		return status;
    
    	dprintk("%s: verifier = %08x:%08x\n",
    			__func__,
    
    			((u32 *)readdir->verifier.data)[0],
    			((u32 *)readdir->verifier.data)[1]);
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	hdrlen = (char *) xdr->p - (char *) iov->iov_base;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	recvd = rcvbuf->len - hdrlen;
    	if (pglen > recvd)
    		pglen = recvd;
    	xdr_read_pages(xdr, pglen);
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    static int decode_readlink(struct xdr_stream *xdr, struct rpc_rqst *req)
    {
    	struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
    	struct kvec *iov = rcvbuf->head;
    
    Al Viro's avatar
    Al Viro committed
    	__be32 *p;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	int status;
    
    	status = decode_op_hdr(xdr, OP_READLINK);
    	if (status)
    		return status;
    
    	/* Convert length of symlink */
    
    	p = xdr_inline_decode(xdr, 4);
    	if (unlikely(!p))
    		goto out_overflow;
    
    	len = be32_to_cpup(p);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (len >= rcvbuf->page_len || len <= 0) {
    
    		dprintk("nfs: server returned giant symlink!\n");
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		return -ENAMETOOLONG;
    	}
    	hdrlen = (char *) xdr->p - (char *) iov->iov_base;
    	recvd = req->rq_rcv_buf.len - hdrlen;
    	if (recvd < len) {
    
    		dprintk("NFS: server cheating in readlink reply: "
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    				"count %u > recvd %u\n", len, recvd);
    		return -EIO;
    	}
    	xdr_read_pages(xdr, len);
    	/*
    	 * The XDR encode routine has set things up so that
    	 * the link text will be copied directly into the
    	 * buffer.  We just have to do overflow-checking,
    	 * and and null-terminate the text (the VFS expects
    	 * null-termination).
    	 */
    
    	xdr_terminate_string(rcvbuf, len);