Skip to content

Commit

Permalink
nfsd: split up nfsd_setattr
Browse files Browse the repository at this point in the history
Split out two helpers to make the code more readable and easier to verify
for correctness.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Cc: stable@vger.kernel.org
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
  • Loading branch information
Christoph Hellwig authored and J. Bruce Fields committed Nov 18, 2013
1 parent 2d3c627 commit 818e5a2
Showing 1 changed file with 84 additions and 60 deletions.
144 changes: 84 additions & 60 deletions fs/nfsd/vfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -369,30 +339,6 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
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 */
if (iap->ia_valid & ATTR_MODE) {
Expand All @@ -415,8 +361,86 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
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;

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;

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;

Expand Down

0 comments on commit 818e5a2

Please sign in to comment.