Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 533db9b3 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge branch 'for-3.13' of git://linux-nfs.org/~bfields/linux

Pull nfsd bugfixes from Bruce Fields:
 "A couple nfsd bugfixes"

* 'for-3.13' of git://linux-nfs.org/~bfields/linux:
  nfsd4: fix xdr decoding of large non-write compounds
  nfsd: make sure to balance get/put_write_access
  nfsd: split up nfsd_setattr
parents c85e0727 365da4ad
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -141,8 +141,8 @@ xdr_error: \

static void next_decode_page(struct nfsd4_compoundargs *argp)
{
	argp->pagelist++;
	argp->p = page_address(argp->pagelist[0]);
	argp->pagelist++;
	if (argp->pagelen < PAGE_SIZE) {
		argp->end = argp->p + (argp->pagelen>>2);
		argp->pagelen = 0;
@@ -1229,6 +1229,7 @@ nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write)
		len -= pages * PAGE_SIZE;

		argp->p = (__be32 *)page_address(argp->pagelist[0]);
		argp->pagelist++;
		argp->end = argp->p + XDR_QUADLEN(PAGE_SIZE);
	}
	argp->p += XDR_QUADLEN(len);
+99 −74
Original line number Diff line number Diff line
@@ -298,41 +298,12 @@ commit_metadata(struct svc_fh *fhp)
}

/*
 * Set various file attributes.
 * N.B. After this call fhp needs an fh_put
 * Go over the attributes and take care of the small differences between
 * NFS semantics and what Linux expects.
 */
__be32
nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
	     int check_guard, time_t guardtime)
static void
nfsd_sanitize_attrs(struct inode *inode, struct iattr *iap)
{
	struct dentry	*dentry;
	struct inode	*inode;
	int		accmode = NFSD_MAY_SATTR;
	umode_t		ftype = 0;
	__be32		err;
	int		host_err;
	int		size_change = 0;

	if (iap->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_SIZE))
		accmode |= NFSD_MAY_WRITE|NFSD_MAY_OWNER_OVERRIDE;
	if (iap->ia_valid & ATTR_SIZE)
		ftype = S_IFREG;

	/* Get inode */
	err = fh_verify(rqstp, fhp, ftype, accmode);
	if (err)
		goto out;

	dentry = fhp->fh_dentry;
	inode = dentry->d_inode;

	/* Ignore any mode updates on symlinks */
	if (S_ISLNK(inode->i_mode))
		iap->ia_valid &= ~ATTR_MODE;

	if (!iap->ia_valid)
		goto out;

	/*
	 * NFSv2 does not differentiate between "set-[ac]time-to-now"
	 * which only requires access, and "set-[ac]time-to-X" which
@@ -342,8 +313,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
	 * convert to "set to now" instead of "set to explicit time"
	 *
	 * We only call inode_change_ok as the last test as technically
	 * it is not an interface that we should be using.  It is only
	 * valid if the filesystem does not define it's own i_op->setattr.
	 * it is not an interface that we should be using.
	 */
#define BOTH_TIME_SET (ATTR_ATIME_SET | ATTR_MTIME_SET)
#define	MAX_TOUCH_TIME_ERROR (30*60)
@@ -370,30 +340,6 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
		}
	}

	/*
	 * The size case is special.
	 * It changes the file as well as the attributes.
	 */
	if (iap->ia_valid & ATTR_SIZE) {
		if (iap->ia_size < inode->i_size) {
			err = nfsd_permission(rqstp, fhp->fh_export, dentry,
					NFSD_MAY_TRUNC|NFSD_MAY_OWNER_OVERRIDE);
			if (err)
				goto out;
		}

		host_err = get_write_access(inode);
		if (host_err)
			goto out_nfserr;

		size_change = 1;
		host_err = locks_verify_truncate(inode, NULL, iap->ia_size);
		if (host_err) {
			put_write_access(inode);
			goto out_nfserr;
		}
	}

	/* sanitize the mode change */
	if (iap->ia_valid & ATTR_MODE) {
		iap->ia_mode &= S_IALLUGO;
@@ -415,32 +361,111 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
			iap->ia_valid |= (ATTR_KILL_SUID | ATTR_KILL_SGID);
		}
	}
}

static __be32
nfsd_get_write_access(struct svc_rqst *rqstp, struct svc_fh *fhp,
		struct iattr *iap)
{
	struct inode *inode = fhp->fh_dentry->d_inode;
	int host_err;

	if (iap->ia_size < inode->i_size) {
		__be32 err;

		err = nfsd_permission(rqstp, fhp->fh_export, fhp->fh_dentry,
				NFSD_MAY_TRUNC | NFSD_MAY_OWNER_OVERRIDE);
		if (err)
			return err;
	}

	host_err = get_write_access(inode);
	if (host_err)
		goto out_nfserrno;

	host_err = locks_verify_truncate(inode, NULL, iap->ia_size);
	if (host_err)
		goto out_put_write_access;
	return 0;

out_put_write_access:
	put_write_access(inode);
out_nfserrno:
	return nfserrno(host_err);
}

/*
 * Set various file attributes.  After this call fhp needs an fh_put.
 */
__be32
nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
	     int check_guard, time_t guardtime)
{
	struct dentry	*dentry;
	struct inode	*inode;
	int		accmode = NFSD_MAY_SATTR;
	umode_t		ftype = 0;
	__be32		err;
	int		host_err;
	int		size_change = 0;

	if (iap->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_SIZE))
		accmode |= NFSD_MAY_WRITE|NFSD_MAY_OWNER_OVERRIDE;
	if (iap->ia_valid & ATTR_SIZE)
		ftype = S_IFREG;

	/* Get inode */
	err = fh_verify(rqstp, fhp, ftype, accmode);
	if (err)
		goto out;

	/* Change the attributes. */
	dentry = fhp->fh_dentry;
	inode = dentry->d_inode;

	/* Ignore any mode updates on symlinks */
	if (S_ISLNK(inode->i_mode))
		iap->ia_valid &= ~ATTR_MODE;

	if (!iap->ia_valid)
		goto out;

	nfsd_sanitize_attrs(inode, iap);

	/*
	 * The size case is special, it changes the file in addition to the
	 * attributes.
	 */
	if (iap->ia_valid & ATTR_SIZE) {
		err = nfsd_get_write_access(rqstp, fhp, iap);
		if (err)
			goto out;
		size_change = 1;
	}

	iap->ia_valid |= ATTR_CTIME;

	if (check_guard && guardtime != inode->i_ctime.tv_sec) {
		err = nfserr_notsync;
	if (!check_guard || guardtime == inode->i_ctime.tv_sec) {
		goto out_put_write_access;
	}

	host_err = nfsd_break_lease(inode);
	if (host_err)
			goto out_nfserr;
		fh_lock(fhp);
		goto out_put_write_access_nfserror;

	fh_lock(fhp);
	host_err = notify_change(dentry, iap, NULL);
		err = nfserrno(host_err);
	fh_unlock(fhp);
	}

out_put_write_access_nfserror:
	err = nfserrno(host_err);
out_put_write_access:
	if (size_change)
		put_write_access(inode);
	if (!err)
		commit_metadata(fhp);
out:
	return err;

out_nfserr:
	err = nfserrno(host_err);
	goto out;
}

#if defined(CONFIG_NFSD_V2_ACL) || \