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

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
...@@ -141,8 +141,8 @@ xdr_error: \ ...@@ -141,8 +141,8 @@ xdr_error: \
static void next_decode_page(struct nfsd4_compoundargs *argp) static void next_decode_page(struct nfsd4_compoundargs *argp)
{ {
argp->pagelist++;
argp->p = page_address(argp->pagelist[0]); argp->p = page_address(argp->pagelist[0]);
argp->pagelist++;
if (argp->pagelen < PAGE_SIZE) { if (argp->pagelen < PAGE_SIZE) {
argp->end = argp->p + (argp->pagelen>>2); argp->end = argp->p + (argp->pagelen>>2);
argp->pagelen = 0; argp->pagelen = 0;
...@@ -1229,6 +1229,7 @@ nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write) ...@@ -1229,6 +1229,7 @@ nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write)
len -= pages * PAGE_SIZE; len -= pages * PAGE_SIZE;
argp->p = (__be32 *)page_address(argp->pagelist[0]); argp->p = (__be32 *)page_address(argp->pagelist[0]);
argp->pagelist++;
argp->end = argp->p + XDR_QUADLEN(PAGE_SIZE); argp->end = argp->p + XDR_QUADLEN(PAGE_SIZE);
} }
argp->p += XDR_QUADLEN(len); argp->p += XDR_QUADLEN(len);
......
...@@ -298,41 +298,12 @@ commit_metadata(struct svc_fh *fhp) ...@@ -298,41 +298,12 @@ commit_metadata(struct svc_fh *fhp)
} }
/* /*
* Set various file attributes. * Go over the attributes and take care of the small differences between
* N.B. After this call fhp needs an fh_put * NFS semantics and what Linux expects.
*/ */
__be32 static void
nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, nfsd_sanitize_attrs(struct inode *inode, 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;
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" * NFSv2 does not differentiate between "set-[ac]time-to-now"
* which only requires access, and "set-[ac]time-to-X" which * 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, ...@@ -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" * convert to "set to now" instead of "set to explicit time"
* *
* We only call inode_change_ok as the last test as technically * 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 * it is not an interface that we should be using.
* valid if the filesystem does not define it's own i_op->setattr.
*/ */
#define BOTH_TIME_SET (ATTR_ATIME_SET | ATTR_MTIME_SET) #define BOTH_TIME_SET (ATTR_ATIME_SET | ATTR_MTIME_SET)
#define MAX_TOUCH_TIME_ERROR (30*60) #define MAX_TOUCH_TIME_ERROR (30*60)
...@@ -369,30 +339,6 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, ...@@ -369,30 +339,6 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
iap->ia_valid &= ~BOTH_TIME_SET; iap->ia_valid &= ~BOTH_TIME_SET;
} }
} }
/*
* 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 */ /* sanitize the mode change */
if (iap->ia_valid & ATTR_MODE) { if (iap->ia_valid & ATTR_MODE) {
...@@ -415,32 +361,111 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, ...@@ -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); iap->ia_valid |= (ATTR_KILL_SUID | ATTR_KILL_SGID);
} }
} }
}
/* Change the attributes. */ 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;
iap->ia_valid |= ATTR_CTIME; if (iap->ia_size < inode->i_size) {
__be32 err;
err = nfserr_notsync; err = nfsd_permission(rqstp, fhp->fh_export, fhp->fh_dentry,
if (!check_guard || guardtime == inode->i_ctime.tv_sec) { NFSD_MAY_TRUNC | NFSD_MAY_OWNER_OVERRIDE);
host_err = nfsd_break_lease(inode); if (err)
if (host_err) return err;
goto out_nfserr; }
fh_lock(fhp);
host_err = notify_change(dentry, iap, NULL); host_err = get_write_access(inode);
err = nfserrno(host_err); if (host_err)
fh_unlock(fhp); 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;
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;
goto out_put_write_access;
}
host_err = nfsd_break_lease(inode);
if (host_err)
goto out_put_write_access_nfserror;
fh_lock(fhp);
host_err = notify_change(dentry, iap, NULL);
fh_unlock(fhp);
out_put_write_access_nfserror:
err = nfserrno(host_err);
out_put_write_access:
if (size_change) if (size_change)
put_write_access(inode); put_write_access(inode);
if (!err) if (!err)
commit_metadata(fhp); commit_metadata(fhp);
out: out:
return err; return err;
out_nfserr:
err = nfserrno(host_err);
goto out;
} }
#if defined(CONFIG_NFSD_V2_ACL) || \ #if defined(CONFIG_NFSD_V2_ACL) || \
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment