Commit 9c332d7f authored by Jeff Layton's avatar Jeff Layton Committed by Trond Myklebust
Browse files

nfs: update inode ctime after removexattr operation



xfstest generic/728 fails with delegated timestamps. The client does a
removexattr and then a stat to test the ctime, which doesn't change. The
stat() doesn't trigger a GETATTR because of the delegated timestamps, so
it relies on the cached ctime, which is wrong.

The setxattr compound has a trailing GETATTR, which ensures that its
ctime gets updated. Follow the same strategy with removexattr.

Fixes: 3e1f0212 ("NFSv4.2: add client side XDR handling for extended attributes")
Reported-by: default avatarOlga Kornievskaia <aglo@umich.edu>
Signed-off-by: default avatarJeff Layton <jlayton@kernel.org>
Signed-off-by: default avatarTrond Myklebust <trond.myklebust@hammerspace.com>
parent 16d99dce
Loading
Loading
Loading
Loading
+16 −2
Original line number Diff line number Diff line
@@ -1372,11 +1372,15 @@ int nfs42_proc_clone(struct file *src_f, struct file *dst_f,
static int _nfs42_proc_removexattr(struct inode *inode, const char *name)
{
	struct nfs_server *server = NFS_SERVER(inode);
	__u32 bitmask[NFS_BITMASK_SZ];
	struct nfs42_removexattrargs args = {
		.fh = NFS_FH(inode),
		.bitmask = bitmask,
		.xattr_name = name,
	};
	struct nfs42_removexattrres res;
	struct nfs42_removexattrres res = {
		.server = server,
	};
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVEXATTR],
		.rpc_argp = &args,
@@ -1385,12 +1389,22 @@ static int _nfs42_proc_removexattr(struct inode *inode, const char *name)
	int ret;
	unsigned long timestamp = jiffies;

	res.fattr = nfs_alloc_fattr();
	if (!res.fattr)
		return -ENOMEM;

	nfs4_bitmask_set(bitmask, server->cache_consistency_bitmask,
			 inode, NFS_INO_INVALID_CHANGE);

	ret = nfs4_call_sync(server->client, server, &msg, &args.seq_args,
	    &res.seq_res, 1);
	trace_nfs4_removexattr(inode, name, ret);
	if (!ret)
	if (!ret) {
		nfs4_update_changeattr(inode, &res.cinfo, timestamp, 0);
		ret = nfs_post_op_update_inode(inode, res.fattr);
	}

	kfree(res.fattr);
	return ret;
}

+8 −2
Original line number Diff line number Diff line
@@ -263,11 +263,13 @@
#define NFS4_enc_removexattr_sz		(compound_encode_hdr_maxsz + \
					 encode_sequence_maxsz + \
					 encode_putfh_maxsz + \
					 encode_removexattr_maxsz)
					 encode_removexattr_maxsz + \
					 encode_getattr_maxsz)
#define NFS4_dec_removexattr_sz		(compound_decode_hdr_maxsz + \
					 decode_sequence_maxsz + \
					 decode_putfh_maxsz + \
					 decode_removexattr_maxsz)
					 decode_removexattr_maxsz + \
					 decode_getattr_maxsz)

/*
 * These values specify the maximum amount of data that is not
@@ -869,6 +871,7 @@ static void nfs4_xdr_enc_removexattr(struct rpc_rqst *req,
	encode_sequence(xdr, &args->seq_args, &hdr);
	encode_putfh(xdr, args->fh, &hdr);
	encode_removexattr(xdr, args->xattr_name, &hdr);
	encode_getfattr(xdr, args->bitmask, &hdr);
	encode_nops(&hdr);
}

@@ -1818,6 +1821,9 @@ static int nfs4_xdr_dec_removexattr(struct rpc_rqst *req,
		goto out;

	status = decode_removexattr(xdr, &res->cinfo);
	if (status)
		goto out;
	status = decode_getfattr(xdr, res->fattr, res->server);
out:
	return status;
}
+3 −0
Original line number Diff line number Diff line
@@ -1611,12 +1611,15 @@ struct nfs42_listxattrsres {
struct nfs42_removexattrargs {
	struct nfs4_sequence_args	seq_args;
	struct nfs_fh			*fh;
	const u32			*bitmask;
	const char			*xattr_name;
};

struct nfs42_removexattrres {
	struct nfs4_sequence_res	seq_res;
	struct nfs4_change_info		cinfo;
	struct nfs_fattr		*fattr;
	const struct nfs_server		*server;
};

#endif /* CONFIG_NFS_V4_2 */