Skip to content
Snippets Groups Projects
nfs4xdr.c 115 KiB
Newer Older
  • Learn to ignore specific revisions
  • Linus Torvalds's avatar
    Linus Torvalds committed
    		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", __FUNCTION__, -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)
    {
    	uint32_t *savep;
    	uint32_t attrlen,
    		 bitmap[2] = {0},
    		 type;
    	int status, fmode = 0;
    
    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;
    
    	fattr->bitmap[0] = bitmap[0];
    	fattr->bitmap[1] = bitmap[1];
    
    	if ((status = decode_attr_length(xdr, &attrlen, &savep)) != 0)
    		goto xdr_error;
    
    
    	if ((status = decode_attr_type(xdr, bitmap, &type)) != 0)
    		goto xdr_error;
    	fattr->type = nfs_type2fmt[type].nfs2type;
    	fmode = nfs_type2fmt[type].mode;
    
    	if ((status = decode_attr_change(xdr, bitmap, &fattr->change_attr)) != 0)
    		goto xdr_error;
    	if ((status = decode_attr_size(xdr, bitmap, &fattr->size)) != 0)
    		goto xdr_error;
    
    	if ((status = decode_attr_fsid(xdr, bitmap, &fattr->fsid)) != 0)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		goto xdr_error;
    	if ((status = decode_attr_fileid(xdr, bitmap, &fattr->fileid)) != 0)
    		goto xdr_error;
    
    	if ((status = decode_attr_fs_locations(xdr, bitmap, container_of(fattr,
    
    						struct nfs4_fs_locations,
    
    						fattr))) != 0)
    		goto xdr_error;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if ((status = decode_attr_mode(xdr, bitmap, &fattr->mode)) != 0)
    		goto xdr_error;
    	fattr->mode |= fmode;
    	if ((status = decode_attr_nlink(xdr, bitmap, &fattr->nlink)) != 0)
    		goto xdr_error;
    
    	if ((status = decode_attr_owner(xdr, bitmap, server->nfs_client, &fattr->uid)) != 0)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		goto xdr_error;
    
    	if ((status = decode_attr_group(xdr, bitmap, server->nfs_client, &fattr->gid)) != 0)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		goto xdr_error;
    	if ((status = decode_attr_rdev(xdr, bitmap, &fattr->rdev)) != 0)
    		goto xdr_error;
    	if ((status = decode_attr_space_used(xdr, bitmap, &fattr->du.nfs3.used)) != 0)
    		goto xdr_error;
    	if ((status = decode_attr_time_access(xdr, bitmap, &fattr->atime)) != 0)
    		goto xdr_error;
    	if ((status = decode_attr_time_metadata(xdr, bitmap, &fattr->ctime)) != 0)
    		goto xdr_error;
    	if ((status = decode_attr_time_modify(xdr, bitmap, &fattr->mtime)) != 0)
    		goto xdr_error;
    
    	if ((status = decode_attr_mounted_on_fileid(xdr, bitmap, &fileid)) != 0)
    		goto xdr_error;
    	if (fattr->fileid == 0 && fileid != 0)
    		fattr->fileid = fileid;
    
    	if ((status = verify_attr_len(xdr, savep, attrlen)) == 0)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		fattr->valid = NFS_ATTR_FATTR | NFS_ATTR_FATTR_V3 | NFS_ATTR_FATTR_V4;
    xdr_error:
    
    	dprintk("%s: xdr returned %d\n", __FUNCTION__, -status);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return status;
    }
    
    
    static int decode_fsinfo(struct xdr_stream *xdr, struct nfs_fsinfo *fsinfo)
    {
    	uint32_t *savep;
    	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 = verify_attr_len(xdr, savep, attrlen);
    xdr_error:
    
    	dprintk("%s: xdr returned %d!\n", __FUNCTION__, -status);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return status;
    }
    
    static int decode_getfh(struct xdr_stream *xdr, struct nfs_fh *fh)
    {
    	uint32_t *p;
    	uint32_t len;
    	int status;
    
    	status = decode_op_hdr(xdr, OP_GETFH);
    	if (status)
    		return status;
    	/* Zero handle first to allow comparisons */
    	memset(fh, 0, sizeof(*fh));
    
    	READ_BUF(4);
    	READ32(len);
    	if (len > NFS4_FHSIZE)
    		return -EIO;
    	fh->size = len;
    	READ_BUF(len);
    	COPYMEM(fh->data, len);
    	return 0;
    }
    
    static int decode_link(struct xdr_stream *xdr, struct nfs4_change_info *cinfo)
    {
    	int status;
    	
    	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;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	uint32_t *p;
    
    	uint32_t namelen, type;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	READ_BUF(32);
    
    	READ64(offset);
    	READ64(length);
    	READ32(type);
    	if (fl != NULL) {
    		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;
    	}
    	READ64(clientid);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	READ32(namelen);
    	READ_BUF(namelen);
    	return -NFS4ERR_DENIED;
    }
    
    
    static int decode_lock(struct xdr_stream *xdr, struct nfs_lock_res *res)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	uint32_t *p;
    	int status;
    
    	status = decode_op_hdr(xdr, OP_LOCK);
    	if (status == 0) {
    
    		READ_BUF(sizeof(res->stateid.data));
    		COPYMEM(res->stateid.data, sizeof(res->stateid.data));
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	} else if (status == -NFS4ERR_DENIED)
    
    		return decode_lock_denied(xdr, NULL);
    
    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
    {
    	uint32_t *p;
    	int status;
    
    	status = decode_op_hdr(xdr, OP_LOCKU);
    	if (status == 0) {
    
    		READ_BUF(sizeof(res->stateid.data));
    		COPYMEM(res->stateid.data, sizeof(res->stateid.data));
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    	return status;
    }
    
    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)
    {
            uint32_t *p;
    	uint32_t limit_type, nblocks, blocksize;
    
    	READ_BUF(12);
    	READ32(limit_type);
    	switch (limit_type) {
    		case 1:
    			READ64(*maxsize);
    			break;
    		case 2:
    			READ32(nblocks);
    			READ32(blocksize);
    			*maxsize = (uint64_t)nblocks * (uint64_t)blocksize;
    	}
    	return 0;
    }
    
    static int decode_delegation(struct xdr_stream *xdr, struct nfs_openres *res)
    {
            uint32_t *p;
            uint32_t delegation_type;
    
    	READ_BUF(4);
    	READ32(delegation_type);
    	if (delegation_type == NFS4_OPEN_DELEGATE_NONE) {
    		res->delegation_type = 0;
    		return 0;
    	}
    	READ_BUF(20);
    	COPYMEM(res->delegation.data, sizeof(res->delegation.data));
    	READ32(res->do_recall);
    	switch (delegation_type) {
    		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)
    				return -EIO;
    	}
    
    	return decode_ace(xdr, NULL, res->server->nfs_client);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    static int decode_open(struct xdr_stream *xdr, struct nfs_openres *res)
    {
            uint32_t *p;
            uint32_t bmlen;
            int status;
    
            status = decode_op_hdr(xdr, OP_OPEN);
            if (status)
                    return status;
            READ_BUF(sizeof(res->stateid.data));
            COPYMEM(res->stateid.data, sizeof(res->stateid.data));
    
            decode_change_info(xdr, &res->cinfo);
    
            READ_BUF(8);
            READ32(res->rflags);
            READ32(bmlen);
            if (bmlen > 10)
                    goto xdr_error;
    
            READ_BUF(bmlen << 2);
            p += bmlen;
    	return decode_delegation(xdr, res);
    xdr_error:
    
    	dprintk("%s: Bitmap too large! Length = %u\n", __FUNCTION__, bmlen);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return -EIO;
    }
    
    static int decode_open_confirm(struct xdr_stream *xdr, struct nfs_open_confirmres *res)
    {
            uint32_t *p;
    	int status;
    
            status = decode_op_hdr(xdr, OP_OPEN_CONFIRM);
            if (status)
                    return status;
            READ_BUF(sizeof(res->stateid.data));
            COPYMEM(res->stateid.data, sizeof(res->stateid.data));
            return 0;
    }
    
    static int decode_open_downgrade(struct xdr_stream *xdr, struct nfs_closeres *res)
    {
    	uint32_t *p;
    	int status;
    
    	status = decode_op_hdr(xdr, OP_OPEN_DOWNGRADE);
    	if (status)
    		return status;
    	READ_BUF(sizeof(res->stateid.data));
    	COPYMEM(res->stateid.data, sizeof(res->stateid.data));
    	return 0;
    }
    
    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;
    	uint32_t *p;
    	uint32_t count, eof, recvd, hdrlen;
    	int status;
    
    	status = decode_op_hdr(xdr, OP_READ);
    	if (status)
    		return status;
    	READ_BUF(8);
    	READ32(eof);
    	READ32(count);
    	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
    	recvd = req->rq_rcv_buf.len - hdrlen;
    	if (count > recvd) {
    		printk(KERN_WARNING "NFS: server cheating in read reply: "
    				"count %u > recvd %u\n", count, recvd);
    		count = recvd;
    		eof = 0;
    	}
    	xdr_read_pages(xdr, count);
    	res->eof = eof;
    	res->count = count;
    	return 0;
    }
    
    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 page	*page = *rcvbuf->pages;
    	struct kvec	*iov = rcvbuf->head;
    	unsigned int	nr, pglen = rcvbuf->page_len;
    	uint32_t	*end, *entry, *p, *kaddr;
    
    	uint32_t	len, attrlen, xlen;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	int 		hdrlen, recvd, status;
    
    	status = decode_op_hdr(xdr, OP_READDIR);
    	if (status)
    		return status;
    	READ_BUF(8);
    	COPYMEM(readdir->verifier.data, 8);
    
    	dprintk("%s: verifier = 0x%x%x\n",
    			__FUNCTION__,
    			((u32 *)readdir->verifier.data)[0],
    			((u32 *)readdir->verifier.data)[1]);
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	hdrlen = (char *) p - (char *) iov->iov_base;
    	recvd = rcvbuf->len - hdrlen;
    	if (pglen > recvd)
    		pglen = recvd;
    	xdr_read_pages(xdr, pglen);
    
    	BUG_ON(pglen + readdir->pgbase > PAGE_CACHE_SIZE);
    	kaddr = p = (uint32_t *) kmap_atomic(page, KM_USER0);
    
    	end = p + ((pglen + readdir->pgbase) >> 2);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	entry = p;
    	for (nr = 0; *p++; nr++) {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			goto short_pkt;
    
    		dprintk("cookie = %Lu, ", *((unsigned long long *)p));
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		p += 2;			/* cookie */
    		len = ntohl(*p++);	/* filename length */
    		if (len > NFS4_MAXNAMLEN) {
    			printk(KERN_WARNING "NFS: giant filename in readdir (len 0x%x)\n", len);
    			goto err_unmap;
    		}
    
    		xlen = XDR_QUADLEN(len);
    		if (end - p < xlen + 1)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			goto short_pkt;
    
    		dprintk("filename = %*s\n", len, (char *)p);
    		p += xlen;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		len = ntohl(*p++);	/* bitmap length */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			goto short_pkt;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		attrlen = XDR_QUADLEN(ntohl(*p++));
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			goto short_pkt;
    
    		p += attrlen;		/* attributes */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		entry = p;
    	}
    	if (!nr && (entry[0] != 0 || entry[1] == 0))
    		goto short_pkt;
    out:	
    	kunmap_atomic(kaddr, KM_USER0);
    	return 0;
    short_pkt:
    
    	dprintk("%s: short packet at entry %d\n", __FUNCTION__, nr);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	entry[0] = entry[1] = 0;
    	/* truncate listing ? */
    	if (!nr) {
    		printk(KERN_NOTICE "NFS: readdir reply truncated!\n");
    		entry[1] = 1;
    	}
    	goto out;
    err_unmap:
    	kunmap_atomic(kaddr, KM_USER0);
    	return -errno_NFSERR_IO;
    }
    
    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;
    	int hdrlen, len, recvd;
    	uint32_t *p;
    	char *kaddr;
    	int status;
    
    	status = decode_op_hdr(xdr, OP_READLINK);
    	if (status)
    		return status;
    
    	/* Convert length of symlink */
    	READ_BUF(4);
    	READ32(len);
    	if (len >= rcvbuf->page_len || len <= 0) {
    		dprintk(KERN_WARNING "nfs: server returned giant symlink!\n");
    		return -ENAMETOOLONG;
    	}
    	hdrlen = (char *) xdr->p - (char *) iov->iov_base;
    	recvd = req->rq_rcv_buf.len - hdrlen;
    	if (recvd < len) {
    		printk(KERN_WARNING "NFS: server cheating in readlink reply: "
    				"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).
    	 */
    	kaddr = (char *)kmap_atomic(rcvbuf->pages[0], KM_USER0);
    	kaddr[len+rcvbuf->page_base] = '\0';
    	kunmap_atomic(kaddr, KM_USER0);
    	return 0;
    }
    
    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)
    {
    	uint32_t *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)) {
    		int hdrlen, recvd;
    
    		/* 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) {
    			printk(KERN_WARNING "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, struct nfs_setattrres *res)
    {
    	uint32_t *p;
    	uint32_t bmlen;
    	int status;
    
            
    	status = decode_op_hdr(xdr, OP_SETATTR);
    	if (status)
    		return status;
    	READ_BUF(4);
    	READ32(bmlen);
    	READ_BUF(bmlen << 2);
    	return 0;
    }
    
    
    static int decode_setclientid(struct xdr_stream *xdr, struct nfs_client *clp)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	uint32_t *p;
    	uint32_t opnum;
    	int32_t nfserr;
    
    	READ_BUF(8);
    	READ32(opnum);
    	if (opnum != OP_SETCLIENTID) {
    		printk(KERN_NOTICE
    				"nfs4_decode_setclientid: Server returned operation"
    			       	" %d\n", opnum);
    		return -EIO;
    	}
    	READ32(nfserr);
    	if (nfserr == NFS_OK) {
    		READ_BUF(8 + sizeof(clp->cl_confirm.data));
    		READ64(clp->cl_clientid);
    		COPYMEM(clp->cl_confirm.data, sizeof(clp->cl_confirm.data));
    	} else if (nfserr == NFSERR_CLID_INUSE) {
    		uint32_t len;
    
    		/* skip netid string */
    		READ_BUF(4);
    		READ32(len);
    		READ_BUF(len);
    
    		/* skip uaddr string */
    		READ_BUF(4);
    		READ32(len);
    		READ_BUF(len);
    		return -NFSERR_CLID_INUSE;
    	} else
    
    		return -nfs4_stat_to_errno(nfserr);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	return 0;
    }
    
    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)
    {
    	uint32_t *p;
    	int status;
    
    	status = decode_op_hdr(xdr, OP_WRITE);
    	if (status)
    		return status;
    
    	READ_BUF(16);
    	READ32(res->count);
    	READ32(res->verf->committed);
    	COPYMEM(res->verf->verifier, 8);
    	return 0;
    }
    
    static int decode_delegreturn(struct xdr_stream *xdr)
    {
    	return decode_op_hdr(xdr, OP_DELEGRETURN);
    }
    
    /*
     * Decode OPEN_DOWNGRADE response
     */
    static int nfs4_xdr_dec_open_downgrade(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_closeres *res)
    {
            struct xdr_stream xdr;
            struct compound_hdr hdr;
            int status;
    
            xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
            status = decode_compound_hdr(&xdr, &hdr);
            if (status)
                    goto out;
            status = decode_putfh(&xdr);
            if (status)
                    goto out;
            status = decode_open_downgrade(&xdr, res);
    
    	if (status != 0)
    		goto out;
    	decode_getfattr(&xdr, res->fattr, res->server);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    out:
            return status;
    }
    
    /*
     * END OF "GENERIC" DECODE ROUTINES.
     */
    
    /*
     * Decode ACCESS response
     */
    static int nfs4_xdr_dec_access(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_accessres *res)
    {
    	struct xdr_stream xdr;
    	struct compound_hdr hdr;
    	int status;
    	
    	xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
    	if ((status = decode_compound_hdr(&xdr, &hdr)) != 0)
    		goto out;
    	if ((status = decode_putfh(&xdr)) == 0)
    		status = decode_access(&xdr, res);
    out:
    	return status;
    }
    
    /*
     * Decode LOOKUP response
     */
    static int nfs4_xdr_dec_lookup(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_lookup_res *res)
    {
    	struct xdr_stream xdr;
    	struct compound_hdr hdr;
    	int status;
    	
    	xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
    	if ((status = decode_compound_hdr(&xdr, &hdr)) != 0)
    		goto out;
    	if ((status = decode_putfh(&xdr)) != 0)
    		goto out;
    	if ((status = decode_lookup(&xdr)) != 0)
    		goto out;
    	if ((status = decode_getfh(&xdr, res->fh)) != 0)
    		goto out;
    	status = decode_getfattr(&xdr, res->fattr, res->server);
    out:
    	return status;
    }
    
    /*
     * Decode LOOKUP_ROOT response
     */
    static int nfs4_xdr_dec_lookup_root(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_lookup_res *res)
    {
    	struct xdr_stream xdr;
    	struct compound_hdr hdr;
    	int status;
    	
    	xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
    	if ((status = decode_compound_hdr(&xdr, &hdr)) != 0)
    		goto out;
    	if ((status = decode_putrootfh(&xdr)) != 0)
    		goto out;
    	if ((status = decode_getfh(&xdr, res->fh)) == 0)
    		status = decode_getfattr(&xdr, res->fattr, res->server);
    out:
    	return status;
    }
    
    /*
     * Decode REMOVE response
     */
    
    static int nfs4_xdr_dec_remove(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_remove_res *res)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	struct xdr_stream xdr;
    	struct compound_hdr hdr;
    	int status;
    	
    	xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
    	if ((status = decode_compound_hdr(&xdr, &hdr)) != 0)
    		goto out;
    
    	if ((status = decode_putfh(&xdr)) != 0)
    		goto out;
    	if ((status = decode_remove(&xdr, &res->cinfo)) != 0)
    		goto out;
    	decode_getfattr(&xdr, res->dir_attr, res->server);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    out:
    	return status;
    }
    
    /*
     * Decode RENAME response
     */
    static int nfs4_xdr_dec_rename(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_rename_res *res)
    {
    	struct xdr_stream xdr;
    	struct compound_hdr hdr;
    	int status;
    	
    	xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
    	if ((status = decode_compound_hdr(&xdr, &hdr)) != 0)
    		goto out;
    	if ((status = decode_putfh(&xdr)) != 0)
    		goto out;
    	if ((status = decode_savefh(&xdr)) != 0)
    		goto out;
    	if ((status = decode_putfh(&xdr)) != 0)
    		goto out;
    
    	if ((status = decode_rename(&xdr, &res->old_cinfo, &res->new_cinfo)) != 0)
    		goto out;
    	/* Current FH is target directory */
    	if (decode_getfattr(&xdr, res->new_fattr, res->server) != 0)
    		goto out;
    	if ((status = decode_restorefh(&xdr)) != 0)
    		goto out;
    	decode_getfattr(&xdr, res->old_fattr, res->server);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    out:
    	return status;
    }
    
    /*
     * Decode LINK response
     */
    
    static int nfs4_xdr_dec_link(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_link_res *res)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	struct xdr_stream xdr;
    	struct compound_hdr hdr;
    	int status;
    	
    	xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
    	if ((status = decode_compound_hdr(&xdr, &hdr)) != 0)
    		goto out;
    	if ((status = decode_putfh(&xdr)) != 0)
    		goto out;
    	if ((status = decode_savefh(&xdr)) != 0)
    		goto out;
    	if ((status = decode_putfh(&xdr)) != 0)
    		goto out;
    
    	if ((status = decode_link(&xdr, &res->cinfo)) != 0)
    		goto out;
    	/*
    	 * Note order: OP_LINK leaves the directory as the current
    	 *             filehandle.
    	 */
    	if (decode_getfattr(&xdr, res->dir_attr, res->server) != 0)
    		goto out;
    	if ((status = decode_restorefh(&xdr)) != 0)
    		goto out;
    	decode_getfattr(&xdr, res->fattr, res->server);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    out:
    	return status;
    }
    
    /*
     * Decode CREATE response
     */
    static int nfs4_xdr_dec_create(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_create_res *res)
    {
    	struct xdr_stream xdr;
    	struct compound_hdr hdr;
    	int status;
    	
    	xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
    	if ((status = decode_compound_hdr(&xdr, &hdr)) != 0)
    		goto out;
    	if ((status = decode_putfh(&xdr)) != 0)
    		goto out;
    
    	if ((status = decode_savefh(&xdr)) != 0)
    		goto out;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if ((status = decode_create(&xdr,&res->dir_cinfo)) != 0)
    		goto out;
    	if ((status = decode_getfh(&xdr, res->fh)) != 0)
    		goto out;
    
    	if (decode_getfattr(&xdr, res->fattr, res->server) != 0)
    		goto out;
    	if ((status = decode_restorefh(&xdr)) != 0)
    		goto out;
    	decode_getfattr(&xdr, res->dir_fattr, res->server);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    out:
    	return status;
    }
    
    /*
     * Decode SYMLINK response
     */
    static int nfs4_xdr_dec_symlink(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_create_res *res)
    {
    	return nfs4_xdr_dec_create(rqstp, p, res);
    }
    
    /*
     * Decode GETATTR response
     */
    static int nfs4_xdr_dec_getattr(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_getattr_res *res)
    {
    	struct xdr_stream xdr;
    	struct compound_hdr hdr;
    	int status;
    	
    	xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
    	status = decode_compound_hdr(&xdr, &hdr);
    	if (status)
    		goto out;
    	status = decode_putfh(&xdr);
    	if (status)
    		goto out;
    	status = decode_getfattr(&xdr, res->fattr, res->server);
    out:
    	return status;
    
    }
    
    
    /*
     * Encode an SETACL request
     */
    static int
    nfs4_xdr_enc_setacl(struct rpc_rqst *req, uint32_t *p, struct nfs_setaclargs *args)
    {
            struct xdr_stream xdr;
            struct compound_hdr hdr = {
                    .nops   = 2,
            };
            int status;
    
            xdr_init_encode(&xdr, &req->rq_snd_buf, p);
            encode_compound_hdr(&xdr, &hdr);
            status = encode_putfh(&xdr, args->fh);
            if (status)
                    goto out;
            status = encode_setacl(&xdr, args);
    out:
            return status;
    }
    /*
     * Decode SETACL response
     */
    static int
    nfs4_xdr_dec_setacl(struct rpc_rqst *rqstp, uint32_t *p, void *res)
    {
    	struct xdr_stream xdr;
    	struct compound_hdr hdr;
    	int status;
    
    	xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
    	status = decode_compound_hdr(&xdr, &hdr);
    	if (status)
    		goto out;
    	status = decode_putfh(&xdr);
    	if (status)
    		goto out;
    	status = decode_setattr(&xdr, res);
    out:
    	return status;
    }
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    /*
     * Decode GETACL response
     */
    static int
    nfs4_xdr_dec_getacl(struct rpc_rqst *rqstp, uint32_t *p, size_t *acl_len)
    {
    	struct xdr_stream xdr;
    	struct compound_hdr hdr;
    	int status;
    
    	xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
    	status = decode_compound_hdr(&xdr, &hdr);
    	if (status)
    		goto out;
    	status = decode_putfh(&xdr);
    	if (status)
    		goto out;
    	status = decode_getacl(&xdr, rqstp, acl_len);
    
    out:
    	return status;
    }
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    /*
     * Decode CLOSE response
     */
    static int nfs4_xdr_dec_close(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_closeres *res)
    {
            struct xdr_stream xdr;
            struct compound_hdr hdr;
            int status;
    
            xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
            status = decode_compound_hdr(&xdr, &hdr);
            if (status)
                    goto out;
            status = decode_putfh(&xdr);
            if (status)
                    goto out;
            status = decode_close(&xdr, res);
    
    	if (status != 0)
    		goto out;
    	/*
    	 * Note: Server may do delete on close for this file
    	 * 	in which case the getattr call will fail with
    	 * 	an ESTALE error. Shouldn't be a problem,
    	 * 	though, since fattr->valid will remain unset.
    	 */
    	decode_getfattr(&xdr, res->fattr, res->server);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    out:
            return status;
    }
    
    /*
     * Decode OPEN response
     */
    static int nfs4_xdr_dec_open(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_openres *res)
    {
            struct xdr_stream xdr;
            struct compound_hdr hdr;
            int status;
    
            xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
            status = decode_compound_hdr(&xdr, &hdr);
            if (status)
                    goto out;
            status = decode_putfh(&xdr);
            if (status)
                    goto out;
    
            status = decode_savefh(&xdr);
    	if (status)
    		goto out;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
            status = decode_open(&xdr, res);
            if (status)
                    goto out;
    	status = decode_getfh(&xdr, &res->fh);
            if (status)
    		goto out;
    
    	if (decode_getfattr(&xdr, res->f_attr, res->server) != 0)
    		goto out;
    	if ((status = decode_restorefh(&xdr)) != 0)
    		goto out;
    	decode_getfattr(&xdr, res->dir_attr, res->server);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    out:
            return status;
    }
    
    /*
     * Decode OPEN_CONFIRM response
     */
    static int nfs4_xdr_dec_open_confirm(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_open_confirmres *res)
    {
            struct xdr_stream xdr;
            struct compound_hdr hdr;