Skip to content
Snippets Groups Projects
nfs4xdr.c 175 KiB
Newer Older
  • Learn to ignore specific revisions
  • 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[2] = {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_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[2] = {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[2] = {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,
    
    		const struct nfs_server *server, int may_sleep)
    
    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, container_of(fattr,
    
    						struct nfs4_fs_locations,
    
    						fattr));
    	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, may_sleep);
    
    	if (status < 0)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		goto xdr_error;
    
    	status = decode_attr_group(xdr, bitmap, server, &fattr->gid, may_sleep);
    
    	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, const struct nfs_server *server, int may_sleep)
    {
    	__be32 *savep;
    	uint32_t attrlen,
    		 bitmap[2] = {0};
    	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, server, may_sleep);
    	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, int may_sleep)
    {
    	return decode_getfattr_generic(xdr, fattr, NULL, server, may_sleep);
    }
    
    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 "%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;
    }
    
    
    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;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	uint32_t attrlen, bitmap[2];
    	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;
    
    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);
    
    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_remove(struct xdr_stream *xdr, struct nfs4_change_info *cinfo)
    {
    	int status;
    
    	status = decode_op_hdr(xdr, OP_REMOVE);
    	if (status)
    		goto out;
    	status = decode_change_info(xdr, cinfo);
    out:
    	return status;
    }
    
    static int decode_rename(struct xdr_stream *xdr, struct nfs4_change_info *old_cinfo,
    	      struct nfs4_change_info *new_cinfo)
    {
    	int status;
    
    	status = decode_op_hdr(xdr, OP_RENAME);
    	if (status)
    		goto out;
    	if ((status = decode_change_info(xdr, old_cinfo)))
    		goto out;
    	status = decode_change_info(xdr, new_cinfo);
    out:
    	return status;
    }
    
    static int decode_renew(struct xdr_stream *xdr)
    {
    	return decode_op_hdr(xdr, OP_RENEW);
    }
    
    
    static int
    decode_restorefh(struct xdr_stream *xdr)
    {
    	return decode_op_hdr(xdr, OP_RESTOREFH);
    }
    
    
    static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
    		size_t *acl_len)
    {
    
    Al Viro's avatar
    Al Viro committed
    	__be32 *savep;
    
    	uint32_t attrlen,
    		 bitmap[2] = {0};
    	struct kvec *iov = req->rq_rcv_buf.head;
    	int status;
    
    	*acl_len = 0;
    	if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0)
    		goto out;
    	if ((status = decode_attr_bitmap(xdr, bitmap)) != 0)
    		goto out;
    	if ((status = decode_attr_length(xdr, &attrlen, &savep)) != 0)
    		goto out;
    
    	if (unlikely(bitmap[0] & (FATTR4_WORD0_ACL - 1U)))
    		return -EIO;
    	if (likely(bitmap[0] & FATTR4_WORD0_ACL)) {
    
    
    		/* We ignore &savep and don't do consistency checks on
    		 * the attr length.  Let userspace figure it out.... */
    		hdrlen = (u8 *)xdr->p - (u8 *)iov->iov_base;
    		recvd = req->rq_rcv_buf.len - hdrlen;
    		if (attrlen > recvd) {
    
    			dprintk("NFS: server cheating in getattr"
    
    					" acl reply: attrlen %u > recvd %u\n",
    					attrlen, recvd);
    			return -EINVAL;
    		}
    
    		xdr_read_pages(xdr, attrlen);
    
    	} else
    		status = -EOPNOTSUPP;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    static int
    decode_savefh(struct xdr_stream *xdr)
    {
    	return decode_op_hdr(xdr, OP_SAVEFH);
    }
    
    
    static int decode_setattr(struct xdr_stream *xdr)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    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_SETATTR);
    	if (status)
    		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;
    
    static int decode_setclientid(struct xdr_stream *xdr, struct nfs4_setclientid_res *res)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    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 != OP_SETCLIENTID) {
    
    		dprintk("nfs: decode_setclientid: Server returned operation"
    
    			" %d\n", opnum);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		return -EIO;
    	}
    
    	nfserr = be32_to_cpup(p);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (nfserr == NFS_OK) {
    
    		p = xdr_inline_decode(xdr, 8 + NFS4_VERIFIER_SIZE);
    		if (unlikely(!p))
    			goto out_overflow;
    
    		p = xdr_decode_hyper(p, &res->clientid);
    		memcpy(res->confirm.data, p, NFS4_VERIFIER_SIZE);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	} else if (nfserr == NFSERR_CLID_INUSE) {
    		uint32_t len;
    
    		/* skip netid string */
    
    		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
    
    		/* skip uaddr string */
    
    		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
    		return -NFSERR_CLID_INUSE;
    	} else
    
    		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
    }
    
    static int decode_setclientid_confirm(struct xdr_stream *xdr)
    {
    	return decode_op_hdr(xdr, OP_SETCLIENTID_CONFIRM);
    }
    
    static int decode_write(struct xdr_stream *xdr, struct nfs_writeres *res)
    {
    
    Al Viro's avatar
    Al Viro committed
    	__be32 *p;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	int status;
    
    	status = decode_op_hdr(xdr, OP_WRITE);
    	if (status)
    		return status;
    
    
    	p = xdr_inline_decode(xdr, 16);
    	if (unlikely(!p))
    		goto out_overflow;
    
    	res->count = be32_to_cpup(p++);
    	res->verf->committed = be32_to_cpup(p++);
    
    	memcpy(res->verf->verifier, p, 8);
    
    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_delegreturn(struct xdr_stream *xdr)
    {
    	return decode_op_hdr(xdr, OP_DELEGRETURN);
    }
    
    
    static int decode_secinfo_gss(struct xdr_stream *xdr, struct nfs4_secinfo_flavor *flavor)
    {
    	__be32 *p;
    
    	p = xdr_inline_decode(xdr, 4);
    	if (unlikely(!p))
    		goto out_overflow;
    	flavor->gss.sec_oid4.len = be32_to_cpup(p);
    	if (flavor->gss.sec_oid4.len > GSS_OID_MAX_LEN)
    		goto out_err;
    
    	p = xdr_inline_decode(xdr, flavor->gss.sec_oid4.len);
    	if (unlikely(!p))
    		goto out_overflow;
    	memcpy(flavor->gss.sec_oid4.data, p, flavor->gss.sec_oid4.len);
    
    	p = xdr_inline_decode(xdr, 8);
    	if (unlikely(!p))
    		goto out_overflow;
    	flavor->gss.qop4 = be32_to_cpup(p++);
    	flavor->gss.service = be32_to_cpup(p);
    
    	return 0;
    
    out_overflow:
    	print_overflow_msg(__func__, xdr);
    	return -EIO;
    out_err:
    	return -EINVAL;
    }
    
    static int decode_secinfo(struct xdr_stream *xdr, struct nfs4_secinfo_res *res)
    {
    	struct nfs4_secinfo_flavor *sec_flavor;
    	int status;
    	__be32 *p;
    
    
    	status = decode_op_hdr(xdr, OP_SECINFO);
    
    	p = xdr_inline_decode(xdr, 4);
    	if (unlikely(!p))
    		goto out_overflow;
    
    
    	res->flavors->num_flavors = 0;
    	num_flavors = be32_to_cpup(p);
    
    	for (i = 0; i < num_flavors; i++) {
    
    		sec_flavor = &res->flavors->flavors[i];
    
    		if ((char *)&sec_flavor[1] - (char *)res->flavors > PAGE_SIZE)
    
    			break;
    
    		p = xdr_inline_decode(xdr, 4);
    		if (unlikely(!p))
    			goto out_overflow;
    		sec_flavor->flavor = be32_to_cpup(p);
    
    		if (sec_flavor->flavor == RPC_AUTH_GSS) {
    
    			status = decode_secinfo_gss(xdr, sec_flavor);
    			if (status)
    				goto out;
    
    		res->flavors->num_flavors++;
    
    out_overflow:
    	print_overflow_msg(__func__, xdr);
    	return -EIO;
    }
    
    
    #if defined(CONFIG_NFS_V4_1)
    static int decode_exchange_id(struct xdr_stream *xdr,
    			      struct nfs41_exchange_id_res *res)
    {
    	__be32 *p;
    	uint32_t dummy;
    
    	int status;
    	struct nfs_client *clp = res->client;
    
    	status = decode_op_hdr(xdr, OP_EXCHANGE_ID);
    	if (status)
    		return status;
    
    
    	p = xdr_inline_decode(xdr, 8);
    	if (unlikely(!p))
    		goto out_overflow;