fs/nfsd: fix update of inode attrs in CB_GETATTR

Currently, we copy the mtime and ctime to the in-core inode and then
mark the inode dirty. This is fine for certain types of filesystems, but
not all. Some require a real setattr to properly change these values
(e.g. ceph or reexported NFS).

Fix this code to call notify_change() instead, which is the proper way
to effect a setattr. There is one problem though:

In this case, the client is holding a write delegation and has sent us
attributes to update our cache. We don't want to break the delegation
for this since that would defeat the purpose. Add a new ATTR_DELEG flag
that makes notify_change bypass the try_break_deleg call.

Fixes: c5967721e106 ("NFSD: handle GETATTR conflict with write delegation")
Reviewed-by: Christian Brauner <brauner@kernel.org>
Signed-off-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>

authored by Jeff Layton and committed by Chuck Lever 7e8ae848 1116e0e3

+27 -10
+11 -3
fs/attr.c
··· 489 489 error = security_inode_setattr(idmap, dentry, attr); 490 490 if (error) 491 491 return error; 492 - error = try_break_deleg(inode, delegated_inode); 493 - if (error) 494 - return error; 492 + 493 + /* 494 + * If ATTR_DELEG is set, then these attributes are being set on 495 + * behalf of the holder of a write delegation. We want to avoid 496 + * breaking the delegation in this case. 497 + */ 498 + if (!(ia_valid & ATTR_DELEG)) { 499 + error = try_break_deleg(inode, delegated_inode); 500 + if (error) 501 + return error; 502 + } 495 503 496 504 if (inode->i_op->setattr) 497 505 error = inode->i_op->setattr(idmap, dentry, attr);
+13 -5
fs/nfsd/nfs4state.c
··· 8815 8815 /** 8816 8816 * nfsd4_deleg_getattr_conflict - Recall if GETATTR causes conflict 8817 8817 * @rqstp: RPC transaction context 8818 - * @inode: file to be checked for a conflict 8818 + * @dentry: dentry of inode to be checked for a conflict 8819 8819 * @modified: return true if file was modified 8820 8820 * @size: new size of file if modified is true 8821 8821 * ··· 8830 8830 * code is returned. 8831 8831 */ 8832 8832 __be32 8833 - nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct inode *inode, 8833 + nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry, 8834 8834 bool *modified, u64 *size) 8835 8835 { 8836 8836 __be32 status; ··· 8839 8839 struct file_lease *fl; 8840 8840 struct iattr attrs; 8841 8841 struct nfs4_cb_fattr *ncf; 8842 + struct inode *inode = d_inode(dentry); 8842 8843 8843 8844 *modified = false; 8844 8845 ctx = locks_inode_context(inode); ··· 8891 8890 ncf->ncf_cur_fsize != ncf->ncf_cb_fsize)) 8892 8891 ncf->ncf_file_modified = true; 8893 8892 if (ncf->ncf_file_modified) { 8893 + int err; 8894 + 8894 8895 /* 8895 8896 * Per section 10.4.3 of RFC 8881, the server would 8896 8897 * not update the file's metadata with the client's 8897 8898 * modified size 8898 8899 */ 8899 8900 attrs.ia_mtime = attrs.ia_ctime = current_time(inode); 8900 - attrs.ia_valid = ATTR_MTIME | ATTR_CTIME; 8901 - setattr_copy(&nop_mnt_idmap, inode, &attrs); 8902 - mark_inode_dirty(inode); 8901 + attrs.ia_valid = ATTR_MTIME | ATTR_CTIME | ATTR_DELEG; 8902 + inode_lock(inode); 8903 + err = notify_change(&nop_mnt_idmap, dentry, &attrs, NULL); 8904 + inode_unlock(inode); 8905 + if (err) { 8906 + nfs4_put_stid(&dp->dl_stid); 8907 + return nfserrno(err); 8908 + } 8903 8909 ncf->ncf_cur_fsize = ncf->ncf_cb_fsize; 8904 8910 *size = ncf->ncf_cur_fsize; 8905 8911 *modified = true;
+1 -1
fs/nfsd/nfs4xdr.c
··· 3565 3565 } 3566 3566 args.size = 0; 3567 3567 if (attrmask[0] & (FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE)) { 3568 - status = nfsd4_deleg_getattr_conflict(rqstp, d_inode(dentry), 3568 + status = nfsd4_deleg_getattr_conflict(rqstp, dentry, 3569 3569 &file_modified, &size); 3570 3570 if (status) 3571 3571 goto out;
+1 -1
fs/nfsd/state.h
··· 781 781 } 782 782 783 783 extern __be32 nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, 784 - struct inode *inode, bool *file_modified, u64 *size); 784 + struct dentry *dentry, bool *file_modified, u64 *size); 785 785 #endif /* NFSD4_STATE_H */
+1
include/linux/fs.h
··· 208 208 #define ATTR_OPEN (1 << 15) /* Truncating from open(O_TRUNC) */ 209 209 #define ATTR_TIMES_SET (1 << 16) 210 210 #define ATTR_TOUCH (1 << 17) 211 + #define ATTR_DELEG (1 << 18) /* Delegated attrs. Don't break write delegations */ 211 212 212 213 /* 213 214 * Whiteout is represented by a char device. The following constants define the