All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/8] Support btime and other NFSv4 specific attributes
@ 2021-12-17 20:48 trondmy
  2021-12-17 20:48 ` [PATCH 1/8] NFS: Expand the type of nfs_fattr->valid trondmy
  2022-01-03 20:51 ` [PATCH 0/8] Support btime and other NFSv4 specific attributes J. Bruce Fields
  0 siblings, 2 replies; 30+ messages in thread
From: trondmy @ 2021-12-17 20:48 UTC (permalink / raw)
  To: Anna Schumaker; +Cc: linux-nfs

From: Trond Myklebust <trond.myklebust@hammerspace.com>

NFSv4 has support for a number of extra attributes that are of interest
to Samba when it is used to re-export a filesystem to Windows clients.
Aside from the btime, which is of interest in statx(), Windows clients
have an interest in determining the status of the 'hidden', and 'system'
flags.
Backup programs want to read the 'archive' flags and the 'time backup'
attribute.
Finally, the 'offline' flag can tell whether or not a file needs to be
staged by an HSM system before it can be read or written to.

The patch series also adds an ioctl() to allow userspace retrieval and
setting of these attributes where appropriate. It also adds an ioctl()
to allow retrieval of the raw NFSv4 ACCESS information, to allow more
fine grained determination of the user's access rights to a file or
directory. All of this information is of use for Samba.

Anne Marie Merritt (3):
  nfs: Add timecreate to nfs inode
  nfs: Add 'archive', 'hidden' and 'system' fields to nfs inode
  nfs: Add 'time backup' to nfs inode

Richard Sharpe (1):
  NFS: Support statx_get and statx_set ioctls

Trond Myklebust (4):
  NFS: Expand the type of nfs_fattr->valid
  NFS: Return the file btime in the statx results when appropriate
  NFSv4: Support the offline bit
  NFSv4: Add an ioctl to allow retrieval of the NFS raw ACCESS mask

 fs/nfs/dir.c              |  71 ++---
 fs/nfs/getroot.c          |   3 +-
 fs/nfs/inode.c            | 147 +++++++++-
 fs/nfs/internal.h         |  10 +
 fs/nfs/nfs3proc.c         |   1 +
 fs/nfs/nfs4_fs.h          |  31 +++
 fs/nfs/nfs4file.c         | 550 ++++++++++++++++++++++++++++++++++++++
 fs/nfs/nfs4proc.c         | 175 +++++++++++-
 fs/nfs/nfs4trace.h        |   8 +-
 fs/nfs/nfs4xdr.c          | 240 +++++++++++++++--
 fs/nfs/nfstrace.c         |   5 +
 fs/nfs/nfstrace.h         |   9 +-
 fs/nfs/proc.c             |   1 +
 include/linux/nfs4.h      |   1 +
 include/linux/nfs_fs.h    |  15 ++
 include/linux/nfs_fs_sb.h |   2 +-
 include/linux/nfs_xdr.h   |  80 ++++--
 include/uapi/linux/nfs.h  | 101 +++++++
 18 files changed, 1356 insertions(+), 94 deletions(-)

-- 
2.33.1


^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH 1/8] NFS: Expand the type of nfs_fattr->valid
  2021-12-17 20:48 [PATCH 0/8] Support btime and other NFSv4 specific attributes trondmy
@ 2021-12-17 20:48 ` trondmy
  2021-12-17 20:48   ` [PATCH 2/8] nfs: Add timecreate to nfs inode trondmy
  2022-01-03 20:51 ` [PATCH 0/8] Support btime and other NFSv4 specific attributes J. Bruce Fields
  1 sibling, 1 reply; 30+ messages in thread
From: trondmy @ 2021-12-17 20:48 UTC (permalink / raw)
  To: Anna Schumaker; +Cc: linux-nfs

From: Trond Myklebust <trond.myklebust@primarydata.com>

We need to be able to track more than 32 attributes per inode.

Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
Signed-off-by: Lance Shelton <lance.shelton@hammerspace.com>
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
---
 fs/nfs/inode.c            |  5 ++--
 include/linux/nfs_fs_sb.h |  2 +-
 include/linux/nfs_xdr.h   | 54 +++++++++++++++++++--------------------
 3 files changed, 31 insertions(+), 30 deletions(-)

diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index fda530d5e764..897dec07cf4b 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -2001,10 +2001,11 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
 	bool attr_changed = false;
 	bool have_delegation;
 
-	dfprintk(VFS, "NFS: %s(%s/%lu fh_crc=0x%08x ct=%d info=0x%x)\n",
+	dfprintk(VFS, "NFS: %s(%s/%lu fh_crc=0x%08x ct=%d info=0x%lx)\n",
 			__func__, inode->i_sb->s_id, inode->i_ino,
 			nfs_display_fhandle_hash(NFS_FH(inode)),
-			atomic_read(&inode->i_count), fattr->valid);
+			atomic_read(&inode->i_count),
+			(unsigned long)fattr->valid);
 
 	if (!(fattr->valid & NFS_ATTR_FATTR_FILEID)) {
 		/* Only a mounted-on-fileid? Just exit */
diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
index 2a9acbfe00f0..8468206fb535 100644
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -157,8 +157,8 @@ struct nfs_server {
 #define NFS_MOUNT_WRITE_EAGER		0x01000000
 #define NFS_MOUNT_WRITE_WAIT		0x02000000
 
-	unsigned int		fattr_valid;	/* Valid attributes */
 	unsigned int		caps;		/* server capabilities */
+	__u64			fattr_valid;	/* Valid attributes */
 	unsigned int		rsize;		/* read size */
 	unsigned int		rpages;		/* read size (in pages) */
 	unsigned int		wsize;		/* write size */
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 967a0098f0a9..d0722269b392 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -45,7 +45,7 @@ struct nfs4_threshold {
 };
 
 struct nfs_fattr {
-	unsigned int		valid;		/* which fields are valid */
+	__u64			valid;		/* which fields are valid */
 	umode_t			mode;
 	__u32			nlink;
 	kuid_t			uid;
@@ -80,32 +80,32 @@ struct nfs_fattr {
 	struct nfs4_label	*label;
 };
 
-#define NFS_ATTR_FATTR_TYPE		(1U << 0)
-#define NFS_ATTR_FATTR_MODE		(1U << 1)
-#define NFS_ATTR_FATTR_NLINK		(1U << 2)
-#define NFS_ATTR_FATTR_OWNER		(1U << 3)
-#define NFS_ATTR_FATTR_GROUP		(1U << 4)
-#define NFS_ATTR_FATTR_RDEV		(1U << 5)
-#define NFS_ATTR_FATTR_SIZE		(1U << 6)
-#define NFS_ATTR_FATTR_PRESIZE		(1U << 7)
-#define NFS_ATTR_FATTR_BLOCKS_USED	(1U << 8)
-#define NFS_ATTR_FATTR_SPACE_USED	(1U << 9)
-#define NFS_ATTR_FATTR_FSID		(1U << 10)
-#define NFS_ATTR_FATTR_FILEID		(1U << 11)
-#define NFS_ATTR_FATTR_ATIME		(1U << 12)
-#define NFS_ATTR_FATTR_MTIME		(1U << 13)
-#define NFS_ATTR_FATTR_CTIME		(1U << 14)
-#define NFS_ATTR_FATTR_PREMTIME		(1U << 15)
-#define NFS_ATTR_FATTR_PRECTIME		(1U << 16)
-#define NFS_ATTR_FATTR_CHANGE		(1U << 17)
-#define NFS_ATTR_FATTR_PRECHANGE	(1U << 18)
-#define NFS_ATTR_FATTR_V4_LOCATIONS	(1U << 19)
-#define NFS_ATTR_FATTR_V4_REFERRAL	(1U << 20)
-#define NFS_ATTR_FATTR_MOUNTPOINT	(1U << 21)
-#define NFS_ATTR_FATTR_MOUNTED_ON_FILEID (1U << 22)
-#define NFS_ATTR_FATTR_OWNER_NAME	(1U << 23)
-#define NFS_ATTR_FATTR_GROUP_NAME	(1U << 24)
-#define NFS_ATTR_FATTR_V4_SECURITY_LABEL (1U << 25)
+#define NFS_ATTR_FATTR_TYPE		BIT_ULL(0)
+#define NFS_ATTR_FATTR_MODE		BIT_ULL(1)
+#define NFS_ATTR_FATTR_NLINK		BIT_ULL(2)
+#define NFS_ATTR_FATTR_OWNER		BIT_ULL(3)
+#define NFS_ATTR_FATTR_GROUP		BIT_ULL(4)
+#define NFS_ATTR_FATTR_RDEV		BIT_ULL(5)
+#define NFS_ATTR_FATTR_SIZE		BIT_ULL(6)
+#define NFS_ATTR_FATTR_PRESIZE		BIT_ULL(7)
+#define NFS_ATTR_FATTR_BLOCKS_USED	BIT_ULL(8)
+#define NFS_ATTR_FATTR_SPACE_USED	BIT_ULL(9)
+#define NFS_ATTR_FATTR_FSID		BIT_ULL(10)
+#define NFS_ATTR_FATTR_FILEID		BIT_ULL(11)
+#define NFS_ATTR_FATTR_ATIME		BIT_ULL(12)
+#define NFS_ATTR_FATTR_MTIME		BIT_ULL(13)
+#define NFS_ATTR_FATTR_CTIME		BIT_ULL(14)
+#define NFS_ATTR_FATTR_PREMTIME		BIT_ULL(15)
+#define NFS_ATTR_FATTR_PRECTIME		BIT_ULL(16)
+#define NFS_ATTR_FATTR_CHANGE		BIT_ULL(17)
+#define NFS_ATTR_FATTR_PRECHANGE	BIT_ULL(18)
+#define NFS_ATTR_FATTR_V4_LOCATIONS	BIT_ULL(19)
+#define NFS_ATTR_FATTR_V4_REFERRAL	BIT_ULL(20)
+#define NFS_ATTR_FATTR_MOUNTPOINT	BIT_ULL(21)
+#define NFS_ATTR_FATTR_MOUNTED_ON_FILEID BIT_ULL(22)
+#define NFS_ATTR_FATTR_OWNER_NAME	BIT_ULL(23)
+#define NFS_ATTR_FATTR_GROUP_NAME	BIT_ULL(24)
+#define NFS_ATTR_FATTR_V4_SECURITY_LABEL BIT_ULL(25)
 
 #define NFS_ATTR_FATTR (NFS_ATTR_FATTR_TYPE \
 		| NFS_ATTR_FATTR_MODE \
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH 2/8] nfs: Add timecreate to nfs inode
  2021-12-17 20:48 ` [PATCH 1/8] NFS: Expand the type of nfs_fattr->valid trondmy
@ 2021-12-17 20:48   ` trondmy
  2021-12-17 20:48     ` [PATCH 3/8] NFS: Return the file btime in the statx results when appropriate trondmy
  0 siblings, 1 reply; 30+ messages in thread
From: trondmy @ 2021-12-17 20:48 UTC (permalink / raw)
  To: Anna Schumaker; +Cc: linux-nfs

From: Anne Marie Merritt <annemarie.merritt@primarydata.com>

Add tracking of the create time (a.k.a. btime) along with corresponding
bitfields, request, and decode xdr routines.

Signed-off-by: Anne Marie Merritt <annemarie.merritt@primarydata.com>
Signed-off-by: Lance Shelton <lance.shelton@hammerspace.com>
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
---
 fs/nfs/inode.c          | 28 ++++++++++++++++++++++------
 fs/nfs/nfs4proc.c       | 15 +++++++++++++--
 fs/nfs/nfs4xdr.c        | 24 ++++++++++++++++++++++++
 fs/nfs/nfstrace.h       |  3 ++-
 include/linux/nfs_fs.h  |  7 +++++++
 include/linux/nfs_xdr.h |  3 +++
 6 files changed, 71 insertions(+), 9 deletions(-)

diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 897dec07cf4b..908a62d6a29c 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -201,6 +201,7 @@ void nfs_set_cache_invalid(struct inode *inode, unsigned long flags)
 		if (!(flags & NFS_INO_REVAL_FORCED))
 			flags &= ~(NFS_INO_INVALID_MODE |
 				   NFS_INO_INVALID_OTHER |
+				   NFS_INO_INVALID_BTIME |
 				   NFS_INO_INVALID_XATTR);
 		flags &= ~(NFS_INO_INVALID_CHANGE | NFS_INO_INVALID_SIZE);
 	} else if (flags & NFS_INO_REVAL_PAGECACHE)
@@ -521,6 +522,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
 		memset(&inode->i_atime, 0, sizeof(inode->i_atime));
 		memset(&inode->i_mtime, 0, sizeof(inode->i_mtime));
 		memset(&inode->i_ctime, 0, sizeof(inode->i_ctime));
+		memset(&nfsi->btime, 0, sizeof(nfsi->btime));
 		inode_set_iversion_raw(inode, 0);
 		inode->i_size = 0;
 		clear_nlink(inode);
@@ -544,6 +546,10 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
 			inode->i_ctime = fattr->ctime;
 		else if (fattr_supported & NFS_ATTR_FATTR_CTIME)
 			nfs_set_cache_invalid(inode, NFS_INO_INVALID_CTIME);
+		if (fattr->valid & NFS_ATTR_FATTR_BTIME)
+			nfsi->btime = fattr->btime;
+		else if (fattr_supported & NFS_ATTR_FATTR_BTIME)
+			nfs_set_cache_invalid(inode, NFS_INO_INVALID_BTIME);
 		if (fattr->valid & NFS_ATTR_FATTR_CHANGE)
 			inode_set_iversion_raw(inode, fattr->change_attr);
 		else
@@ -1801,7 +1807,7 @@ static int nfs_inode_finish_partial_attr_update(const struct nfs_fattr *fattr,
 		NFS_INO_INVALID_ATIME | NFS_INO_INVALID_CTIME |
 		NFS_INO_INVALID_MTIME | NFS_INO_INVALID_SIZE |
 		NFS_INO_INVALID_BLOCKS | NFS_INO_INVALID_OTHER |
-		NFS_INO_INVALID_NLINK;
+		NFS_INO_INVALID_NLINK | NFS_INO_INVALID_BTIME;
 	unsigned long cache_validity = NFS_I(inode)->cache_validity;
 	enum nfs4_change_attr_type ctype = NFS_SERVER(inode)->change_attr_type;
 
@@ -2052,10 +2058,13 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
 	nfsi->read_cache_jiffies = fattr->time_start;
 
 	save_cache_validity = nfsi->cache_validity;
-	nfsi->cache_validity &= ~(NFS_INO_INVALID_ATTR
-			| NFS_INO_INVALID_ATIME
-			| NFS_INO_REVAL_FORCED
-			| NFS_INO_INVALID_BLOCKS);
+	nfsi->cache_validity &=
+		~(NFS_INO_INVALID_ATIME | NFS_INO_REVAL_FORCED |
+		  NFS_INO_INVALID_CHANGE | NFS_INO_INVALID_CTIME |
+		  NFS_INO_INVALID_MTIME | NFS_INO_INVALID_SIZE |
+		  NFS_INO_INVALID_OTHER | NFS_INO_INVALID_BLOCKS |
+		  NFS_INO_INVALID_NLINK | NFS_INO_INVALID_MODE |
+		  NFS_INO_INVALID_BTIME);
 
 	/* Do atomic weak cache consistency updates */
 	nfs_wcc_update_inode(inode, fattr);
@@ -2085,7 +2094,8 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
 					| NFS_INO_INVALID_BLOCKS
 					| NFS_INO_INVALID_NLINK
 					| NFS_INO_INVALID_MODE
-					| NFS_INO_INVALID_OTHER;
+					| NFS_INO_INVALID_OTHER
+					| NFS_INO_INVALID_BTIME;
 				if (S_ISDIR(inode->i_mode))
 					nfs_force_lookup_revalidate(inode);
 				attr_changed = true;
@@ -2116,6 +2126,12 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
 		nfsi->cache_validity |=
 			save_cache_validity & NFS_INO_INVALID_CTIME;
 
+	if (fattr->valid & NFS_ATTR_FATTR_BTIME)
+		nfsi->btime = fattr->btime;
+	else if (fattr_supported & NFS_ATTR_FATTR_BTIME)
+		nfsi->cache_validity |=
+			save_cache_validity & NFS_INO_INVALID_BTIME;
+
 	/* Check if our cached file size is stale */
 	if (fattr->valid & NFS_ATTR_FATTR_SIZE) {
 		new_isize = nfs_size_to_loff_t(fattr->size);
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 535436dbdc9a..aabf14e5f8c8 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -207,6 +207,7 @@ const u32 nfs4_fattr_bitmap[3] = {
 	| FATTR4_WORD1_RAWDEV
 	| FATTR4_WORD1_SPACE_USED
 	| FATTR4_WORD1_TIME_ACCESS
+	| FATTR4_WORD1_TIME_CREATE
 	| FATTR4_WORD1_TIME_METADATA
 	| FATTR4_WORD1_TIME_MODIFY
 	| FATTR4_WORD1_MOUNTED_ON_FILEID,
@@ -228,6 +229,7 @@ static const u32 nfs4_pnfs_open_bitmap[3] = {
 	| FATTR4_WORD1_RAWDEV
 	| FATTR4_WORD1_SPACE_USED
 	| FATTR4_WORD1_TIME_ACCESS
+	| FATTR4_WORD1_TIME_CREATE
 	| FATTR4_WORD1_TIME_METADATA
 	| FATTR4_WORD1_TIME_MODIFY,
 	FATTR4_WORD2_MDSTHRESHOLD
@@ -307,6 +309,9 @@ static void nfs4_bitmap_copy_adjust(__u32 *dst, const __u32 *src,
 		dst[1] &= ~FATTR4_WORD1_MODE;
 	if (!(cache_validity & NFS_INO_INVALID_OTHER))
 		dst[1] &= ~(FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP);
+
+	if (!(cache_validity & NFS_INO_INVALID_BTIME))
+		dst[1] &= ~FATTR4_WORD1_TIME_CREATE;
 }
 
 static void nfs4_setup_readdir(u64 cookie, __be32 *verifier, struct dentry *dentry,
@@ -1232,8 +1237,8 @@ nfs4_update_changeattr_locked(struct inode *inode,
 				NFS_INO_INVALID_ACCESS | NFS_INO_INVALID_ACL |
 				NFS_INO_INVALID_SIZE | NFS_INO_INVALID_OTHER |
 				NFS_INO_INVALID_BLOCKS | NFS_INO_INVALID_NLINK |
-				NFS_INO_INVALID_MODE | NFS_INO_INVALID_XATTR |
-				NFS_INO_REVAL_PAGECACHE;
+				NFS_INO_INVALID_MODE | NFS_INO_INVALID_BTIME |
+				NFS_INO_INVALID_XATTR | NFS_INO_REVAL_PAGECACHE;
 		nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
 	}
 	nfsi->attrtimeo_timestamp = jiffies;
@@ -3893,6 +3898,10 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f
 			server->fattr_valid &= ~NFS_ATTR_FATTR_CTIME;
 		if (!(res.attr_bitmask[1] & FATTR4_WORD1_TIME_MODIFY))
 			server->fattr_valid &= ~NFS_ATTR_FATTR_MTIME;
+		if (!(res.attr_bitmask[1] & FATTR4_WORD1_TIME_MODIFY))
+			server->fattr_valid &= ~NFS_ATTR_FATTR_MTIME;
+		if (!(res.attr_bitmask[1] & FATTR4_WORD1_TIME_CREATE))
+			server->fattr_valid &= ~NFS_ATTR_FATTR_BTIME;
 		memcpy(server->attr_bitmask_nl, res.attr_bitmask,
 				sizeof(server->attr_bitmask));
 		server->attr_bitmask_nl[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
@@ -5448,6 +5457,8 @@ static void nfs4_bitmask_set(__u32 bitmask[NFS4_BITMASK_SZ], const __u32 *src,
 		bitmask[1] |= FATTR4_WORD1_TIME_MODIFY;
 	if (cache_validity & NFS_INO_INVALID_BLOCKS)
 		bitmask[1] |= FATTR4_WORD1_SPACE_USED;
+	if (cache_validity & NFS_INO_INVALID_BTIME)
+		bitmask[1] |= FATTR4_WORD1_TIME_CREATE;
 
 	if (cache_validity & NFS_INO_INVALID_SIZE)
 		bitmask[0] |= FATTR4_WORD0_SIZE;
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 69862bf6db00..b60b6b8f83ad 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -1616,6 +1616,7 @@ static void encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg
 		attrs[1] |= FATTR4_WORD1_MODE|FATTR4_WORD1_NUMLINKS|FATTR4_WORD1_OWNER|
 			FATTR4_WORD1_OWNER_GROUP|FATTR4_WORD1_RAWDEV|
 			FATTR4_WORD1_SPACE_USED|FATTR4_WORD1_TIME_ACCESS|
+			FATTR4_WORD1_TIME_CREATE |
 			FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY;
 		attrs[2] |= FATTR4_WORD2_SECURITY_LABEL;
 		dircount >>= 1;
@@ -4121,6 +4122,24 @@ static int decode_attr_time_access(struct xdr_stream *xdr, uint32_t *bitmap, str
 	return status;
 }
 
+static int decode_attr_time_create(struct xdr_stream *xdr, uint32_t *bitmap, struct timespec64 *time)
+{
+	int status = 0;
+
+	time->tv_sec = 0;
+	time->tv_nsec = 0;
+	if (unlikely(bitmap[1] & (FATTR4_WORD1_TIME_CREATE - 1U)))
+		return -EIO;
+	if (likely(bitmap[1] & FATTR4_WORD1_TIME_CREATE)) {
+		status = decode_attr_time(xdr, time);
+		if (status == 0)
+			status = NFS_ATTR_FATTR_BTIME;
+		bitmap[1] &= ~FATTR4_WORD1_TIME_CREATE;
+	}
+	dprintk("%s: btime=%lld\n", __func__, time->tv_sec);
+	return status;
+}
+
 static int decode_attr_time_metadata(struct xdr_stream *xdr, uint32_t *bitmap, struct timespec64 *time)
 {
 	int status = 0;
@@ -4677,6 +4696,11 @@ static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap,
 		goto xdr_error;
 	fattr->valid |= status;
 
+	status = decode_attr_time_create(xdr, bitmap, &fattr->btime);
+	if (status < 0)
+		goto xdr_error;
+	fattr->valid |= status;
+
 	status = decode_attr_time_metadata(xdr, bitmap, &fattr->ctime);
 	if (status < 0)
 		goto xdr_error;
diff --git a/fs/nfs/nfstrace.h b/fs/nfs/nfstrace.h
index b3aee261801e..cba86d167c38 100644
--- a/fs/nfs/nfstrace.h
+++ b/fs/nfs/nfstrace.h
@@ -33,7 +33,8 @@
 			{ NFS_INO_INVALID_BLOCKS, "INVALID_BLOCKS" }, \
 			{ NFS_INO_INVALID_XATTR, "INVALID_XATTR" }, \
 			{ NFS_INO_INVALID_NLINK, "INVALID_NLINK" }, \
-			{ NFS_INO_INVALID_MODE, "INVALID_MODE" })
+			{ NFS_INO_INVALID_MODE, "INVALID_MODE" }, \
+			{ NFS_INO_INVALID_BTIME, "INVALID_BTIME" })
 
 #define nfs_show_nfsi_flags(v) \
 	__print_flags(v, "|", \
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 05f249f20f55..18f027ce5b4b 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -136,6 +136,12 @@ struct nfs_inode {
 	unsigned long		flags;			/* atomic bit ops */
 	unsigned long		cache_validity;		/* bit mask */
 
+	/*
+	 * NFS Attributes not included in struct inode
+	 */
+
+	struct timespec64	btime;
+
 	/*
 	 * read_cache_jiffies is when we started read-caching this inode.
 	 * attrtimeo is for how long the cached information is assumed
@@ -258,6 +264,7 @@ struct nfs4_copy_state {
 #define NFS_INO_INVALID_XATTR	BIT(15)		/* xattrs are invalid */
 #define NFS_INO_INVALID_NLINK	BIT(16)		/* cached nlinks is invalid */
 #define NFS_INO_INVALID_MODE	BIT(17)		/* cached mode is invalid */
+#define NFS_INO_INVALID_BTIME	BIT(18)		/* cached btime is invalid */
 
 #define NFS_INO_INVALID_ATTR	(NFS_INO_INVALID_CHANGE \
 		| NFS_INO_INVALID_CTIME \
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index d0722269b392..6b9e802ddac0 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -67,6 +67,7 @@ struct nfs_fattr {
 	struct timespec64	atime;
 	struct timespec64	mtime;
 	struct timespec64	ctime;
+	struct timespec64	btime;
 	__u64			change_attr;	/* NFSv4 change attribute */
 	__u64			pre_change_attr;/* pre-op NFSv4 change attribute */
 	__u64			pre_size;	/* pre_op_attr.size	  */
@@ -106,6 +107,7 @@ struct nfs_fattr {
 #define NFS_ATTR_FATTR_OWNER_NAME	BIT_ULL(23)
 #define NFS_ATTR_FATTR_GROUP_NAME	BIT_ULL(24)
 #define NFS_ATTR_FATTR_V4_SECURITY_LABEL BIT_ULL(25)
+#define NFS_ATTR_FATTR_BTIME		BIT_ULL(26)
 
 #define NFS_ATTR_FATTR (NFS_ATTR_FATTR_TYPE \
 		| NFS_ATTR_FATTR_MODE \
@@ -126,6 +128,7 @@ struct nfs_fattr {
 		| NFS_ATTR_FATTR_SPACE_USED)
 #define NFS_ATTR_FATTR_V4 (NFS_ATTR_FATTR \
 		| NFS_ATTR_FATTR_SPACE_USED \
+		| NFS_ATTR_FATTR_BTIME \
 		| NFS_ATTR_FATTR_V4_SECURITY_LABEL)
 
 /*
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH 3/8] NFS: Return the file btime in the statx results when appropriate
  2021-12-17 20:48   ` [PATCH 2/8] nfs: Add timecreate to nfs inode trondmy
@ 2021-12-17 20:48     ` trondmy
  2021-12-17 20:48       ` [PATCH 4/8] nfs: Add 'archive', 'hidden' and 'system' fields to nfs inode trondmy
  0 siblings, 1 reply; 30+ messages in thread
From: trondmy @ 2021-12-17 20:48 UTC (permalink / raw)
  To: Anna Schumaker; +Cc: linux-nfs

From: Trond Myklebust <trond.myklebust@hammerspace.com>

If the server supports the NFSv4.x "create_time" attribute, then return
it as part of the statx results.

Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
---
 fs/nfs/inode.c     | 15 +++++++++++++--
 fs/nfs/nfs4trace.h |  3 ++-
 2 files changed, 15 insertions(+), 3 deletions(-)

diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 908a62d6a29c..94268cab7613 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -815,6 +815,7 @@ static void nfs_readdirplus_parent_cache_hit(struct dentry *dentry)
 
 static u32 nfs_get_valid_attrmask(struct inode *inode)
 {
+	u64 fattr_valid = NFS_SERVER(inode)->fattr_valid;
 	unsigned long cache_validity = READ_ONCE(NFS_I(inode)->cache_validity);
 	u32 reply_mask = STATX_INO | STATX_TYPE;
 
@@ -834,6 +835,9 @@ static u32 nfs_get_valid_attrmask(struct inode *inode)
 		reply_mask |= STATX_UID | STATX_GID;
 	if (!(cache_validity & NFS_INO_INVALID_BLOCKS))
 		reply_mask |= STATX_BLOCKS;
+	if (!(cache_validity & NFS_INO_INVALID_BTIME) &&
+	    (fattr_valid & NFS_ATTR_FATTR_BTIME))
+		reply_mask |= STATX_BTIME;
 	return reply_mask;
 }
 
@@ -842,6 +846,7 @@ int nfs_getattr(struct user_namespace *mnt_userns, const struct path *path,
 {
 	struct inode *inode = d_inode(path->dentry);
 	struct nfs_server *server = NFS_SERVER(inode);
+	u64 fattr_valid = server->fattr_valid;
 	unsigned long cache_validity;
 	int err = 0;
 	bool force_sync = query_flags & AT_STATX_FORCE_SYNC;
@@ -851,7 +856,10 @@ int nfs_getattr(struct user_namespace *mnt_userns, const struct path *path,
 
 	request_mask &= STATX_TYPE | STATX_MODE | STATX_NLINK | STATX_UID |
 			STATX_GID | STATX_ATIME | STATX_MTIME | STATX_CTIME |
-			STATX_INO | STATX_SIZE | STATX_BLOCKS;
+			STATX_INO | STATX_SIZE | STATX_BLOCKS | STATX_BTIME;
+
+	if (!(fattr_valid & NFS_ATTR_FATTR_BTIME))
+		request_mask &= ~STATX_BTIME;
 
 	if ((query_flags & AT_STATX_DONT_SYNC) && !force_sync) {
 		nfs_readdirplus_parent_cache_hit(path->dentry);
@@ -882,7 +890,7 @@ int nfs_getattr(struct user_namespace *mnt_userns, const struct path *path,
 	/* Is the user requesting attributes that might need revalidation? */
 	if (!(request_mask & (STATX_MODE|STATX_NLINK|STATX_ATIME|STATX_CTIME|
 					STATX_MTIME|STATX_UID|STATX_GID|
-					STATX_SIZE|STATX_BLOCKS)))
+					STATX_SIZE|STATX_BLOCKS|STATX_BTIME)))
 		goto out_no_revalidate;
 
 	/* Check whether the cached attributes are stale */
@@ -905,6 +913,8 @@ int nfs_getattr(struct user_namespace *mnt_userns, const struct path *path,
 		do_update |= cache_validity & NFS_INO_INVALID_OTHER;
 	if (request_mask & STATX_BLOCKS)
 		do_update |= cache_validity & NFS_INO_INVALID_BLOCKS;
+	if (request_mask & STATX_BTIME)
+		do_update |= cache_validity & NFS_INO_INVALID_BTIME;
 
 	if (do_update) {
 		/* Update the attribute cache */
@@ -925,6 +935,7 @@ int nfs_getattr(struct user_namespace *mnt_userns, const struct path *path,
 	stat->ino = nfs_compat_user_ino64(NFS_FILEID(inode));
 	if (S_ISDIR(inode->i_mode))
 		stat->blksize = NFS_SERVER(inode)->dtsize;
+	stat->btime = NFS_I(inode)->btime;
 out:
 	trace_nfs_getattr_exit(inode, err);
 	return err;
diff --git a/fs/nfs/nfs4trace.h b/fs/nfs/nfs4trace.h
index 6ee6ad3674a2..186b851be5ba 100644
--- a/fs/nfs/nfs4trace.h
+++ b/fs/nfs/nfs4trace.h
@@ -30,7 +30,8 @@
 		{ NFS_ATTR_FATTR_CTIME, "CTIME" }, \
 		{ NFS_ATTR_FATTR_CHANGE, "CHANGE" }, \
 		{ NFS_ATTR_FATTR_OWNER_NAME, "OWNER_NAME" }, \
-		{ NFS_ATTR_FATTR_GROUP_NAME, "GROUP_NAME" })
+		{ NFS_ATTR_FATTR_GROUP_NAME, "GROUP_NAME" }, \
+		{ NFS_ATTR_FATTR_BTIME, "BTIME" })
 
 DECLARE_EVENT_CLASS(nfs4_clientid_event,
 		TP_PROTO(
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH 4/8] nfs: Add 'archive', 'hidden' and 'system' fields to nfs inode
  2021-12-17 20:48     ` [PATCH 3/8] NFS: Return the file btime in the statx results when appropriate trondmy
@ 2021-12-17 20:48       ` trondmy
  2021-12-17 20:48         ` [PATCH 5/8] nfs: Add 'time backup' " trondmy
  0 siblings, 1 reply; 30+ messages in thread
From: trondmy @ 2021-12-17 20:48 UTC (permalink / raw)
  To: Anna Schumaker; +Cc: linux-nfs

From: Anne Marie Merritt <annemarie.merritt@primarydata.com>

Add tracking of the Windows 'archive', 'hidden' and 'system' attributes,
along with corresponding bitfields, request, and decode xdr routines.

Signed-off-by: Anne Marie Merritt <annemarie.merritt@primarydata.com>
Signed-off-by: Lance Shelton <lance.shelton@hammerspace.com>
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
---
 fs/nfs/inode.c          |  42 +++++++++++++++--
 fs/nfs/nfs4proc.c       |  26 ++++++++++-
 fs/nfs/nfs4trace.h      |   5 +-
 fs/nfs/nfs4xdr.c        | 100 +++++++++++++++++++++++++++++++++++++---
 fs/nfs/nfstrace.h       |   3 +-
 include/linux/nfs_fs.h  |   5 ++
 include/linux/nfs_xdr.h |  12 +++++
 7 files changed, 179 insertions(+), 14 deletions(-)

diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 94268cab7613..9f138dc1880d 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -202,6 +202,7 @@ void nfs_set_cache_invalid(struct inode *inode, unsigned long flags)
 			flags &= ~(NFS_INO_INVALID_MODE |
 				   NFS_INO_INVALID_OTHER |
 				   NFS_INO_INVALID_BTIME |
+				   NFS_INO_INVALID_WINATTR |
 				   NFS_INO_INVALID_XATTR);
 		flags &= ~(NFS_INO_INVALID_CHANGE | NFS_INO_INVALID_SIZE);
 	} else if (flags & NFS_INO_REVAL_PAGECACHE)
@@ -523,6 +524,9 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
 		memset(&inode->i_mtime, 0, sizeof(inode->i_mtime));
 		memset(&inode->i_ctime, 0, sizeof(inode->i_ctime));
 		memset(&nfsi->btime, 0, sizeof(nfsi->btime));
+		nfsi->archive = 0;
+		nfsi->hidden = 0;
+		nfsi->system = 0;
 		inode_set_iversion_raw(inode, 0);
 		inode->i_size = 0;
 		clear_nlink(inode);
@@ -550,6 +554,18 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
 			nfsi->btime = fattr->btime;
 		else if (fattr_supported & NFS_ATTR_FATTR_BTIME)
 			nfs_set_cache_invalid(inode, NFS_INO_INVALID_BTIME);
+		if (fattr->valid & NFS_ATTR_FATTR_ARCHIVE)
+			nfsi->archive = (fattr->hsa_flags & NFS_HSA_ARCHIVE) ? 1 : 0;
+		else if (fattr_supported & NFS_ATTR_FATTR_ARCHIVE)
+			nfs_set_cache_invalid(inode, NFS_INO_INVALID_WINATTR);
+		if (fattr->valid & NFS_ATTR_FATTR_HIDDEN)
+			nfsi->hidden = (fattr->hsa_flags & NFS_HSA_HIDDEN) ? 1 : 0;
+		else if (fattr_supported & NFS_ATTR_FATTR_HIDDEN)
+			nfs_set_cache_invalid(inode, NFS_INO_INVALID_WINATTR);
+		if (fattr->valid & NFS_ATTR_FATTR_SYSTEM)
+			nfsi->system = (fattr->hsa_flags & NFS_HSA_SYSTEM) ? 1 : 0;
+		else if (fattr_supported & NFS_ATTR_FATTR_SYSTEM)
+			nfs_set_cache_invalid(inode, NFS_INO_INVALID_WINATTR);
 		if (fattr->valid & NFS_ATTR_FATTR_CHANGE)
 			inode_set_iversion_raw(inode, fattr->change_attr);
 		else
@@ -1818,7 +1834,8 @@ static int nfs_inode_finish_partial_attr_update(const struct nfs_fattr *fattr,
 		NFS_INO_INVALID_ATIME | NFS_INO_INVALID_CTIME |
 		NFS_INO_INVALID_MTIME | NFS_INO_INVALID_SIZE |
 		NFS_INO_INVALID_BLOCKS | NFS_INO_INVALID_OTHER |
-		NFS_INO_INVALID_NLINK | NFS_INO_INVALID_BTIME;
+		NFS_INO_INVALID_NLINK | NFS_INO_INVALID_BTIME |
+		NFS_INO_INVALID_WINATTR;
 	unsigned long cache_validity = NFS_I(inode)->cache_validity;
 	enum nfs4_change_attr_type ctype = NFS_SERVER(inode)->change_attr_type;
 
@@ -2075,7 +2092,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
 		  NFS_INO_INVALID_MTIME | NFS_INO_INVALID_SIZE |
 		  NFS_INO_INVALID_OTHER | NFS_INO_INVALID_BLOCKS |
 		  NFS_INO_INVALID_NLINK | NFS_INO_INVALID_MODE |
-		  NFS_INO_INVALID_BTIME);
+		  NFS_INO_INVALID_BTIME | NFS_INO_INVALID_WINATTR);
 
 	/* Do atomic weak cache consistency updates */
 	nfs_wcc_update_inode(inode, fattr);
@@ -2106,7 +2123,8 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
 					| NFS_INO_INVALID_NLINK
 					| NFS_INO_INVALID_MODE
 					| NFS_INO_INVALID_OTHER
-					| NFS_INO_INVALID_BTIME;
+					| NFS_INO_INVALID_BTIME
+					| NFS_INO_INVALID_WINATTR;
 				if (S_ISDIR(inode->i_mode))
 					nfs_force_lookup_revalidate(inode);
 				attr_changed = true;
@@ -2143,6 +2161,24 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
 		nfsi->cache_validity |=
 			save_cache_validity & NFS_INO_INVALID_BTIME;
 
+	if (fattr->valid & NFS_ATTR_FATTR_ARCHIVE)
+		nfsi->archive = (fattr->hsa_flags & NFS_HSA_ARCHIVE) ? 1 : 0;
+	else if (fattr_supported & NFS_ATTR_FATTR_ARCHIVE)
+		nfsi->cache_validity |=
+			save_cache_validity & NFS_INO_INVALID_WINATTR;
+
+	if (fattr->valid & NFS_ATTR_FATTR_HIDDEN)
+		nfsi->hidden = (fattr->hsa_flags & NFS_HSA_HIDDEN) ? 1 : 0;
+	else if (fattr_supported & NFS_ATTR_FATTR_HIDDEN)
+		nfsi->cache_validity |=
+			save_cache_validity & NFS_INO_INVALID_WINATTR;
+
+	if (fattr->valid & NFS_ATTR_FATTR_SYSTEM)
+		nfsi->system = (fattr->hsa_flags & NFS_HSA_SYSTEM) ? 1 : 0;
+	else if (fattr_supported & NFS_ATTR_FATTR_SYSTEM)
+		nfsi->cache_validity |=
+			save_cache_validity & NFS_INO_INVALID_WINATTR;
+
 	/* Check if our cached file size is stale */
 	if (fattr->valid & NFS_ATTR_FATTR_SIZE) {
 		new_isize = nfs_size_to_loff_t(fattr->size);
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index aabf14e5f8c8..06df71f6371b 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -199,13 +199,16 @@ const u32 nfs4_fattr_bitmap[3] = {
 	| FATTR4_WORD0_CHANGE
 	| FATTR4_WORD0_SIZE
 	| FATTR4_WORD0_FSID
-	| FATTR4_WORD0_FILEID,
+	| FATTR4_WORD0_ARCHIVE
+	| FATTR4_WORD0_FILEID
+	| FATTR4_WORD0_HIDDEN,
 	FATTR4_WORD1_MODE
 	| FATTR4_WORD1_NUMLINKS
 	| FATTR4_WORD1_OWNER
 	| FATTR4_WORD1_OWNER_GROUP
 	| FATTR4_WORD1_RAWDEV
 	| FATTR4_WORD1_SPACE_USED
+	| FATTR4_WORD1_SYSTEM
 	| FATTR4_WORD1_TIME_ACCESS
 	| FATTR4_WORD1_TIME_CREATE
 	| FATTR4_WORD1_TIME_METADATA
@@ -221,13 +224,16 @@ static const u32 nfs4_pnfs_open_bitmap[3] = {
 	| FATTR4_WORD0_CHANGE
 	| FATTR4_WORD0_SIZE
 	| FATTR4_WORD0_FSID
-	| FATTR4_WORD0_FILEID,
+	| FATTR4_WORD0_ARCHIVE
+	| FATTR4_WORD0_FILEID
+	| FATTR4_WORD0_HIDDEN,
 	FATTR4_WORD1_MODE
 	| FATTR4_WORD1_NUMLINKS
 	| FATTR4_WORD1_OWNER
 	| FATTR4_WORD1_OWNER_GROUP
 	| FATTR4_WORD1_RAWDEV
 	| FATTR4_WORD1_SPACE_USED
+	| FATTR4_WORD1_SYSTEM
 	| FATTR4_WORD1_TIME_ACCESS
 	| FATTR4_WORD1_TIME_CREATE
 	| FATTR4_WORD1_TIME_METADATA
@@ -312,6 +318,11 @@ static void nfs4_bitmap_copy_adjust(__u32 *dst, const __u32 *src,
 
 	if (!(cache_validity & NFS_INO_INVALID_BTIME))
 		dst[1] &= ~FATTR4_WORD1_TIME_CREATE;
+
+	if (!(cache_validity & NFS_INO_INVALID_WINATTR)) {
+		dst[0] &= ~(FATTR4_WORD0_ARCHIVE | FATTR4_WORD0_HIDDEN);
+		dst[1] &= ~FATTR4_WORD1_SYSTEM;
+	}
 }
 
 static void nfs4_setup_readdir(u64 cookie, __be32 *verifier, struct dentry *dentry,
@@ -1238,6 +1249,7 @@ nfs4_update_changeattr_locked(struct inode *inode,
 				NFS_INO_INVALID_SIZE | NFS_INO_INVALID_OTHER |
 				NFS_INO_INVALID_BLOCKS | NFS_INO_INVALID_NLINK |
 				NFS_INO_INVALID_MODE | NFS_INO_INVALID_BTIME |
+				NFS_INO_INVALID_WINATTR |
 				NFS_INO_INVALID_XATTR | NFS_INO_REVAL_PAGECACHE;
 		nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
 	}
@@ -3878,8 +3890,12 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f
 		if (res.attr_bitmask[2] & FATTR4_WORD2_SECURITY_LABEL)
 			server->caps |= NFS_CAP_SECURITY_LABEL;
 #endif
+		if (!(res.attr_bitmask[0] & FATTR4_WORD0_ARCHIVE))
+			server->fattr_valid &= ~NFS_ATTR_FATTR_ARCHIVE;
 		if (!(res.attr_bitmask[0] & FATTR4_WORD0_FILEID))
 			server->fattr_valid &= ~NFS_ATTR_FATTR_FILEID;
+		if (!(res.attr_bitmask[0] & FATTR4_WORD0_HIDDEN))
+			server->fattr_valid &= ~NFS_ATTR_FATTR_HIDDEN;
 		if (!(res.attr_bitmask[1] & FATTR4_WORD1_MODE))
 			server->fattr_valid &= ~NFS_ATTR_FATTR_MODE;
 		if (!(res.attr_bitmask[1] & FATTR4_WORD1_NUMLINKS))
@@ -3902,6 +3918,8 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f
 			server->fattr_valid &= ~NFS_ATTR_FATTR_MTIME;
 		if (!(res.attr_bitmask[1] & FATTR4_WORD1_TIME_CREATE))
 			server->fattr_valid &= ~NFS_ATTR_FATTR_BTIME;
+		if (!(res.attr_bitmask[1] & FATTR4_WORD1_SYSTEM))
+			server->fattr_valid &= ~NFS_ATTR_FATTR_SYSTEM;
 		memcpy(server->attr_bitmask_nl, res.attr_bitmask,
 				sizeof(server->attr_bitmask));
 		server->attr_bitmask_nl[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
@@ -5459,6 +5477,10 @@ static void nfs4_bitmask_set(__u32 bitmask[NFS4_BITMASK_SZ], const __u32 *src,
 		bitmask[1] |= FATTR4_WORD1_SPACE_USED;
 	if (cache_validity & NFS_INO_INVALID_BTIME)
 		bitmask[1] |= FATTR4_WORD1_TIME_CREATE;
+	if (cache_validity & NFS_INO_INVALID_WINATTR) {
+		bitmask[0] |= FATTR4_WORD0_ARCHIVE | FATTR4_WORD0_HIDDEN;
+		bitmask[1] |= FATTR4_WORD1_SYSTEM;
+	}
 
 	if (cache_validity & NFS_INO_INVALID_SIZE)
 		bitmask[0] |= FATTR4_WORD0_SIZE;
diff --git a/fs/nfs/nfs4trace.h b/fs/nfs/nfs4trace.h
index 186b851be5ba..babcd3207e8f 100644
--- a/fs/nfs/nfs4trace.h
+++ b/fs/nfs/nfs4trace.h
@@ -31,7 +31,10 @@
 		{ NFS_ATTR_FATTR_CHANGE, "CHANGE" }, \
 		{ NFS_ATTR_FATTR_OWNER_NAME, "OWNER_NAME" }, \
 		{ NFS_ATTR_FATTR_GROUP_NAME, "GROUP_NAME" }, \
-		{ NFS_ATTR_FATTR_BTIME, "BTIME" })
+		{ NFS_ATTR_FATTR_BTIME, "BTIME" }, \
+		{ NFS_ATTR_FATTR_HIDDEN, "HIDDEN" }, \
+		{ NFS_ATTR_FATTR_SYSTEM, "SYSTEM" }, \
+		{ NFS_ATTR_FATTR_ARCHIVE, "ARCHIVE" })
 
 DECLARE_EVENT_CLASS(nfs4_clientid_event,
 		TP_PROTO(
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index b60b6b8f83ad..ca3f1bcd321c 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -1611,13 +1611,17 @@ static void encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg
 	unsigned int i;
 
 	if (readdir->plus) {
-		attrs[0] |= FATTR4_WORD0_TYPE|FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE|
-			FATTR4_WORD0_FSID|FATTR4_WORD0_FILEHANDLE|FATTR4_WORD0_FILEID;
-		attrs[1] |= FATTR4_WORD1_MODE|FATTR4_WORD1_NUMLINKS|FATTR4_WORD1_OWNER|
-			FATTR4_WORD1_OWNER_GROUP|FATTR4_WORD1_RAWDEV|
-			FATTR4_WORD1_SPACE_USED|FATTR4_WORD1_TIME_ACCESS|
-			FATTR4_WORD1_TIME_CREATE |
-			FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY;
+		attrs[0] |= FATTR4_WORD0_TYPE | FATTR4_WORD0_CHANGE |
+			    FATTR4_WORD0_SIZE| FATTR4_WORD0_FSID |
+			    FATTR4_WORD0_ARCHIVE | FATTR4_WORD0_FILEHANDLE |
+			    FATTR4_WORD0_FILEID | FATTR4_WORD0_HIDDEN;
+		attrs[1] |= FATTR4_WORD1_MODE | FATTR4_WORD1_NUMLINKS |
+			    FATTR4_WORD1_OWNER| FATTR4_WORD1_OWNER_GROUP |
+			    FATTR4_WORD1_RAWDEV | FATTR4_WORD1_SPACE_USED |
+			    FATTR4_WORD1_SYSTEM | FATTR4_WORD1_TIME_ACCESS |
+			    FATTR4_WORD1_TIME_CREATE |
+			    FATTR4_WORD1_TIME_METADATA |
+			    FATTR4_WORD1_TIME_MODIFY;
 		attrs[2] |= FATTR4_WORD2_SECURITY_LABEL;
 		dircount >>= 1;
 	}
@@ -3534,6 +3538,28 @@ static int decode_attr_aclsupport(struct xdr_stream *xdr, uint32_t *bitmap, uint
 	return 0;
 }
 
+static int decode_attr_archive(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res)
+{
+	int status = 0;
+	__be32 *p;
+
+	if (unlikely(bitmap[0] & (FATTR4_WORD0_ARCHIVE - 1U)))
+		return -EIO;
+	if (likely(bitmap[0] & FATTR4_WORD0_ARCHIVE)) {
+		p = xdr_inline_decode(xdr, 4);
+		if (unlikely(!p))
+			return -EIO;
+		if (be32_to_cpup(p))
+			*res |= NFS_HSA_ARCHIVE;
+		else
+			*res &= ~NFS_HSA_ARCHIVE;
+		bitmap[0] &= ~FATTR4_WORD0_ARCHIVE;
+		status = NFS_ATTR_FATTR_ARCHIVE;
+	}
+	dprintk("%s: archive file: =%s\n", __func__, (*res & NFS_HSA_ARCHIVE) == 0 ? "false" : "true");
+	return status;
+}
+
 static int decode_attr_fileid(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *fileid)
 {
 	__be32 *p;
@@ -3751,6 +3777,28 @@ static int decode_attr_fs_locations(struct xdr_stream *xdr, uint32_t *bitmap, st
 	goto out;
 }
 
+static int decode_attr_hidden(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res)
+{
+	int status = 0;
+	__be32 *p;
+
+	if (unlikely(bitmap[0] & (FATTR4_WORD0_HIDDEN - 1U)))
+		return -EIO;
+	if (likely(bitmap[0] & FATTR4_WORD0_HIDDEN)) {
+		p = xdr_inline_decode(xdr, 4);
+		if (unlikely(!p))
+			return -EIO;
+		if (be32_to_cpup(p))
+			*res |= NFS_HSA_HIDDEN;
+		else
+			*res &= ~NFS_HSA_HIDDEN;
+		bitmap[0] &= ~FATTR4_WORD0_HIDDEN;
+		status = NFS_ATTR_FATTR_HIDDEN;
+	}
+	dprintk("%s: hidden file: =%s\n", __func__, (*res & NFS_HSA_HIDDEN) == 0 ? "false" : "true");
+	return status;
+}
+
 static int decode_attr_maxfilesize(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res)
 {
 	__be32 *p;
@@ -4082,6 +4130,28 @@ static int decode_attr_space_used(struct xdr_stream *xdr, uint32_t *bitmap, uint
 	return ret;
 }
 
+static int decode_attr_system(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res)
+{
+	int status = 0;
+	__be32 *p;
+
+	if (unlikely(bitmap[1] & (FATTR4_WORD1_SYSTEM - 1U)))
+		return -EIO;
+	if (likely(bitmap[1] & FATTR4_WORD1_SYSTEM)) {
+		p = xdr_inline_decode(xdr, 4);
+		if (unlikely(!p))
+			return -EIO;
+		if (be32_to_cpup(p))
+			*res |= NFS_HSA_SYSTEM;
+		else
+			*res &= ~NFS_HSA_SYSTEM;
+		bitmap[1] &= ~FATTR4_WORD1_SYSTEM;
+		status = NFS_ATTR_FATTR_SYSTEM;
+	}
+	dprintk("%s: system file: =%s\n", __func__, (*res & NFS_HSA_HIDDEN) == 0 ? "false" : "true");
+	return status;
+}
+
 static __be32 *
 xdr_decode_nfstime4(__be32 *p, struct timespec64 *t)
 {
@@ -4640,6 +4710,12 @@ static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap,
 	if (status < 0)
 		goto xdr_error;
 
+	fattr->hsa_flags = 0;
+	status = decode_attr_archive(xdr, bitmap, &fattr->hsa_flags);
+	if (status < 0)
+		goto xdr_error;
+	fattr->valid |= status;
+
 	status = decode_attr_filehandle(xdr, bitmap, fh);
 	if (status < 0)
 		goto xdr_error;
@@ -4654,6 +4730,11 @@ static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap,
 		goto xdr_error;
 	fattr->valid |= status;
 
+	status = decode_attr_hidden(xdr, bitmap, &fattr->hsa_flags);
+	if (status < 0)
+		goto xdr_error;
+	fattr->valid |= status;
+
 	status = -EIO;
 	if (unlikely(bitmap[0]))
 		goto xdr_error;
@@ -4691,6 +4772,11 @@ static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap,
 		goto xdr_error;
 	fattr->valid |= status;
 
+	status = decode_attr_system(xdr, bitmap, &fattr->hsa_flags);
+	if (status < 0)
+		goto xdr_error;
+	fattr->valid |= status;
+
 	status = decode_attr_time_access(xdr, bitmap, &fattr->atime);
 	if (status < 0)
 		goto xdr_error;
diff --git a/fs/nfs/nfstrace.h b/fs/nfs/nfstrace.h
index cba86d167c38..2ef7cff8a4ba 100644
--- a/fs/nfs/nfstrace.h
+++ b/fs/nfs/nfstrace.h
@@ -34,7 +34,8 @@
 			{ NFS_INO_INVALID_XATTR, "INVALID_XATTR" }, \
 			{ NFS_INO_INVALID_NLINK, "INVALID_NLINK" }, \
 			{ NFS_INO_INVALID_MODE, "INVALID_MODE" }, \
-			{ NFS_INO_INVALID_BTIME, "INVALID_BTIME" })
+			{ NFS_INO_INVALID_BTIME, "INVALID_BTIME" }, \
+			{ NFS_INO_INVALID_WINATTR, "INVALID_WINATTR" })
 
 #define nfs_show_nfsi_flags(v) \
 	__print_flags(v, "|", \
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 18f027ce5b4b..337a3a17dbaf 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -142,6 +142,10 @@ struct nfs_inode {
 
 	struct timespec64	btime;
 
+	unsigned char		archive : 1;
+	unsigned char		hidden : 1;
+	unsigned char		system : 1;
+
 	/*
 	 * read_cache_jiffies is when we started read-caching this inode.
 	 * attrtimeo is for how long the cached information is assumed
@@ -265,6 +269,7 @@ struct nfs4_copy_state {
 #define NFS_INO_INVALID_NLINK	BIT(16)		/* cached nlinks is invalid */
 #define NFS_INO_INVALID_MODE	BIT(17)		/* cached mode is invalid */
 #define NFS_INO_INVALID_BTIME	BIT(18)		/* cached btime is invalid */
+#define NFS_INO_INVALID_WINATTR	BIT(19)		/* cached windows attr is invalid */
 
 #define NFS_INO_INVALID_ATTR	(NFS_INO_INVALID_CHANGE \
 		| NFS_INO_INVALID_CTIME \
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 6b9e802ddac0..8563162fd7e1 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -17,6 +17,11 @@
 
 #define NFS_BITMASK_SZ		3
 
+/* HIDDEN, SYSTEM bitfields in hsa_flags in nfs_fattr */
+#define NFS_HSA_HIDDEN		BIT(0)
+#define NFS_HSA_SYSTEM		BIT(1)
+#define NFS_HSA_ARCHIVE		BIT(2)
+
 struct nfs4_string {
 	unsigned int len;
 	char *data;
@@ -68,6 +73,7 @@ struct nfs_fattr {
 	struct timespec64	mtime;
 	struct timespec64	ctime;
 	struct timespec64	btime;
+	__u32			hsa_flags;	/* hidden, system, archive flags bitfield */
 	__u64			change_attr;	/* NFSv4 change attribute */
 	__u64			pre_change_attr;/* pre-op NFSv4 change attribute */
 	__u64			pre_size;	/* pre_op_attr.size	  */
@@ -108,6 +114,9 @@ struct nfs_fattr {
 #define NFS_ATTR_FATTR_GROUP_NAME	BIT_ULL(24)
 #define NFS_ATTR_FATTR_V4_SECURITY_LABEL BIT_ULL(25)
 #define NFS_ATTR_FATTR_BTIME		BIT_ULL(26)
+#define NFS_ATTR_FATTR_HIDDEN           BIT_ULL(27)
+#define NFS_ATTR_FATTR_SYSTEM           BIT_ULL(28)
+#define NFS_ATTR_FATTR_ARCHIVE          BIT_ULL(29)
 
 #define NFS_ATTR_FATTR (NFS_ATTR_FATTR_TYPE \
 		| NFS_ATTR_FATTR_MODE \
@@ -129,6 +138,9 @@ struct nfs_fattr {
 #define NFS_ATTR_FATTR_V4 (NFS_ATTR_FATTR \
 		| NFS_ATTR_FATTR_SPACE_USED \
 		| NFS_ATTR_FATTR_BTIME \
+		| NFS_ATTR_FATTR_HIDDEN \
+		| NFS_ATTR_FATTR_SYSTEM \
+		| NFS_ATTR_FATTR_ARCHIVE \
 		| NFS_ATTR_FATTR_V4_SECURITY_LABEL)
 
 /*
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH 5/8] nfs: Add 'time backup' to nfs inode
  2021-12-17 20:48       ` [PATCH 4/8] nfs: Add 'archive', 'hidden' and 'system' fields to nfs inode trondmy
@ 2021-12-17 20:48         ` trondmy
  2021-12-17 20:48           ` [PATCH 6/8] NFSv4: Support the offline bit trondmy
  0 siblings, 1 reply; 30+ messages in thread
From: trondmy @ 2021-12-17 20:48 UTC (permalink / raw)
  To: Anna Schumaker; +Cc: linux-nfs

From: Anne Marie Merritt <annemarie.merritt@primarydata.com>

Add tracking of the NFSv4 'time backup' attribute, along with
corresponding bitfields, request, and decode xdr routines.

Signed-off-by: Anne Marie Merritt <annemarie.merritt@primarydata.com>
Signed-off-by: Lance Shelton <lance.shelton@hammerspace.com>
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
---
 fs/nfs/inode.c          | 11 +++++++++++
 fs/nfs/nfs4proc.c       |  8 ++++++--
 fs/nfs/nfs4trace.h      |  3 ++-
 fs/nfs/nfs4xdr.c        | 24 ++++++++++++++++++++++++
 include/linux/nfs_fs.h  |  1 +
 include/linux/nfs_xdr.h |  3 +++
 6 files changed, 47 insertions(+), 3 deletions(-)

diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 9f138dc1880d..4673b091ea31 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -524,6 +524,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
 		memset(&inode->i_mtime, 0, sizeof(inode->i_mtime));
 		memset(&inode->i_ctime, 0, sizeof(inode->i_ctime));
 		memset(&nfsi->btime, 0, sizeof(nfsi->btime));
+		memset(&nfsi->timebackup, 0, sizeof(nfsi->timebackup));
 		nfsi->archive = 0;
 		nfsi->hidden = 0;
 		nfsi->system = 0;
@@ -554,6 +555,10 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
 			nfsi->btime = fattr->btime;
 		else if (fattr_supported & NFS_ATTR_FATTR_BTIME)
 			nfs_set_cache_invalid(inode, NFS_INO_INVALID_BTIME);
+		if (fattr->valid & NFS_ATTR_FATTR_TIME_BACKUP)
+			nfsi->timebackup = fattr->time_backup;
+		else if (fattr_supported & NFS_ATTR_FATTR_TIME_BACKUP)
+			nfs_set_cache_invalid(inode, NFS_INO_INVALID_WINATTR);
 		if (fattr->valid & NFS_ATTR_FATTR_ARCHIVE)
 			nfsi->archive = (fattr->hsa_flags & NFS_HSA_ARCHIVE) ? 1 : 0;
 		else if (fattr_supported & NFS_ATTR_FATTR_ARCHIVE)
@@ -2161,6 +2166,12 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
 		nfsi->cache_validity |=
 			save_cache_validity & NFS_INO_INVALID_BTIME;
 
+	if (fattr->valid & NFS_ATTR_FATTR_TIME_BACKUP)
+		nfsi->timebackup = fattr->time_backup;
+	else if (fattr_supported & NFS_ATTR_FATTR_TIME_BACKUP)
+		nfsi->cache_validity |=
+			save_cache_validity & NFS_INO_INVALID_WINATTR;
+
 	if (fattr->valid & NFS_ATTR_FATTR_ARCHIVE)
 		nfsi->archive = (fattr->hsa_flags & NFS_HSA_ARCHIVE) ? 1 : 0;
 	else if (fattr_supported & NFS_ATTR_FATTR_ARCHIVE)
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 06df71f6371b..d3a528a4c9b7 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -210,6 +210,7 @@ const u32 nfs4_fattr_bitmap[3] = {
 	| FATTR4_WORD1_SPACE_USED
 	| FATTR4_WORD1_SYSTEM
 	| FATTR4_WORD1_TIME_ACCESS
+	| FATTR4_WORD1_TIME_BACKUP
 	| FATTR4_WORD1_TIME_CREATE
 	| FATTR4_WORD1_TIME_METADATA
 	| FATTR4_WORD1_TIME_MODIFY
@@ -235,6 +236,7 @@ static const u32 nfs4_pnfs_open_bitmap[3] = {
 	| FATTR4_WORD1_SPACE_USED
 	| FATTR4_WORD1_SYSTEM
 	| FATTR4_WORD1_TIME_ACCESS
+	| FATTR4_WORD1_TIME_BACKUP
 	| FATTR4_WORD1_TIME_CREATE
 	| FATTR4_WORD1_TIME_METADATA
 	| FATTR4_WORD1_TIME_MODIFY,
@@ -321,7 +323,7 @@ static void nfs4_bitmap_copy_adjust(__u32 *dst, const __u32 *src,
 
 	if (!(cache_validity & NFS_INO_INVALID_WINATTR)) {
 		dst[0] &= ~(FATTR4_WORD0_ARCHIVE | FATTR4_WORD0_HIDDEN);
-		dst[1] &= ~FATTR4_WORD1_SYSTEM;
+		dst[1] &= ~(FATTR4_WORD1_SYSTEM | FATTR4_WORD1_TIME_BACKUP);
 	}
 }
 
@@ -3916,6 +3918,8 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f
 			server->fattr_valid &= ~NFS_ATTR_FATTR_MTIME;
 		if (!(res.attr_bitmask[1] & FATTR4_WORD1_TIME_MODIFY))
 			server->fattr_valid &= ~NFS_ATTR_FATTR_MTIME;
+		if (!(res.attr_bitmask[1] & FATTR4_WORD1_TIME_BACKUP))
+			server->fattr_valid &= ~NFS_ATTR_FATTR_TIME_BACKUP;
 		if (!(res.attr_bitmask[1] & FATTR4_WORD1_TIME_CREATE))
 			server->fattr_valid &= ~NFS_ATTR_FATTR_BTIME;
 		if (!(res.attr_bitmask[1] & FATTR4_WORD1_SYSTEM))
@@ -5479,7 +5483,7 @@ static void nfs4_bitmask_set(__u32 bitmask[NFS4_BITMASK_SZ], const __u32 *src,
 		bitmask[1] |= FATTR4_WORD1_TIME_CREATE;
 	if (cache_validity & NFS_INO_INVALID_WINATTR) {
 		bitmask[0] |= FATTR4_WORD0_ARCHIVE | FATTR4_WORD0_HIDDEN;
-		bitmask[1] |= FATTR4_WORD1_SYSTEM;
+		bitmask[1] |= FATTR4_WORD1_SYSTEM | FATTR4_WORD1_TIME_BACKUP;
 	}
 
 	if (cache_validity & NFS_INO_INVALID_SIZE)
diff --git a/fs/nfs/nfs4trace.h b/fs/nfs/nfs4trace.h
index babcd3207e8f..5e72639b469e 100644
--- a/fs/nfs/nfs4trace.h
+++ b/fs/nfs/nfs4trace.h
@@ -34,7 +34,8 @@
 		{ NFS_ATTR_FATTR_BTIME, "BTIME" }, \
 		{ NFS_ATTR_FATTR_HIDDEN, "HIDDEN" }, \
 		{ NFS_ATTR_FATTR_SYSTEM, "SYSTEM" }, \
-		{ NFS_ATTR_FATTR_ARCHIVE, "ARCHIVE" })
+		{ NFS_ATTR_FATTR_ARCHIVE, "ARCHIVE" }, \
+		{ NFS_ATTR_FATTR_TIME_BACKUP, "TIME_BACKUP" })
 
 DECLARE_EVENT_CLASS(nfs4_clientid_event,
 		TP_PROTO(
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index ca3f1bcd321c..68885ba5fc58 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -1619,6 +1619,7 @@ static void encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg
 			    FATTR4_WORD1_OWNER| FATTR4_WORD1_OWNER_GROUP |
 			    FATTR4_WORD1_RAWDEV | FATTR4_WORD1_SPACE_USED |
 			    FATTR4_WORD1_SYSTEM | FATTR4_WORD1_TIME_ACCESS |
+			    FATTR4_WORD1_TIME_BACKUP |
 			    FATTR4_WORD1_TIME_CREATE |
 			    FATTR4_WORD1_TIME_METADATA |
 			    FATTR4_WORD1_TIME_MODIFY;
@@ -4192,6 +4193,24 @@ static int decode_attr_time_access(struct xdr_stream *xdr, uint32_t *bitmap, str
 	return status;
 }
 
+static int decode_attr_time_backup(struct xdr_stream *xdr, uint32_t *bitmap, struct timespec64 *time)
+{
+	int status = 0;
+
+	time->tv_sec = 0;
+	time->tv_nsec = 0;
+	if (unlikely(bitmap[1] & (FATTR4_WORD1_TIME_BACKUP - 1U)))
+		return -EIO;
+	if (likely(bitmap[1] & FATTR4_WORD1_TIME_BACKUP)) {
+		status = decode_attr_time(xdr, time);
+		if (status == 0)
+			status = NFS_ATTR_FATTR_TIME_BACKUP;
+		bitmap[1] &= ~FATTR4_WORD1_TIME_BACKUP;
+	}
+	dprintk("%s: time_backup=%ld\n", __func__, (long)time->tv_sec);
+	return status;
+}
+
 static int decode_attr_time_create(struct xdr_stream *xdr, uint32_t *bitmap, struct timespec64 *time)
 {
 	int status = 0;
@@ -4782,6 +4801,11 @@ static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap,
 		goto xdr_error;
 	fattr->valid |= status;
 
+	status = decode_attr_time_backup(xdr, bitmap, &fattr->time_backup);
+	if (status < 0)
+		goto xdr_error;
+	fattr->valid |= status;
+
 	status = decode_attr_time_create(xdr, bitmap, &fattr->btime);
 	if (status < 0)
 		goto xdr_error;
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 337a3a17dbaf..5f1dbd7a7b69 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -141,6 +141,7 @@ struct nfs_inode {
 	 */
 
 	struct timespec64	btime;
+	struct timespec64	timebackup;
 
 	unsigned char		archive : 1;
 	unsigned char		hidden : 1;
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 8563162fd7e1..69c19f465571 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -73,6 +73,7 @@ struct nfs_fattr {
 	struct timespec64	mtime;
 	struct timespec64	ctime;
 	struct timespec64	btime;
+	struct timespec64	time_backup;
 	__u32			hsa_flags;	/* hidden, system, archive flags bitfield */
 	__u64			change_attr;	/* NFSv4 change attribute */
 	__u64			pre_change_attr;/* pre-op NFSv4 change attribute */
@@ -117,6 +118,7 @@ struct nfs_fattr {
 #define NFS_ATTR_FATTR_HIDDEN           BIT_ULL(27)
 #define NFS_ATTR_FATTR_SYSTEM           BIT_ULL(28)
 #define NFS_ATTR_FATTR_ARCHIVE          BIT_ULL(29)
+#define NFS_ATTR_FATTR_TIME_BACKUP      BIT_ULL(30)
 
 #define NFS_ATTR_FATTR (NFS_ATTR_FATTR_TYPE \
 		| NFS_ATTR_FATTR_MODE \
@@ -141,6 +143,7 @@ struct nfs_fattr {
 		| NFS_ATTR_FATTR_HIDDEN \
 		| NFS_ATTR_FATTR_SYSTEM \
 		| NFS_ATTR_FATTR_ARCHIVE \
+		| NFS_ATTR_FATTR_TIME_BACKUP \
 		| NFS_ATTR_FATTR_V4_SECURITY_LABEL)
 
 /*
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH 6/8] NFSv4: Support the offline bit
  2021-12-17 20:48         ` [PATCH 5/8] nfs: Add 'time backup' " trondmy
@ 2021-12-17 20:48           ` trondmy
  2021-12-17 20:48             ` [PATCH 7/8] NFS: Support statx_get and statx_set ioctls trondmy
  0 siblings, 1 reply; 30+ messages in thread
From: trondmy @ 2021-12-17 20:48 UTC (permalink / raw)
  To: Anna Schumaker; +Cc: linux-nfs

From: Trond Myklebust <trond.myklebust@primarydata.com>

Add tracking of the NFSv4 'offline' attribute.

Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
Signed-off-by: Lance Shelton <lance.shelton@hammerspace.com>
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
---
 fs/nfs/inode.c          | 11 +++++++++++
 fs/nfs/nfs4proc.c       |  6 ++++++
 fs/nfs/nfs4trace.h      |  3 ++-
 fs/nfs/nfs4xdr.c        | 31 ++++++++++++++++++++++++++++++-
 include/linux/nfs4.h    |  1 +
 include/linux/nfs_fs.h  |  1 +
 include/linux/nfs_xdr.h |  5 ++++-
 7 files changed, 55 insertions(+), 3 deletions(-)

diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 4673b091ea31..33f4410190b6 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -528,6 +528,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
 		nfsi->archive = 0;
 		nfsi->hidden = 0;
 		nfsi->system = 0;
+		nfsi->offline = 0;
 		inode_set_iversion_raw(inode, 0);
 		inode->i_size = 0;
 		clear_nlink(inode);
@@ -606,6 +607,10 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
 		} else if (fattr_supported & NFS_ATTR_FATTR_SPACE_USED &&
 			   fattr->size != 0)
 			nfs_set_cache_invalid(inode, NFS_INO_INVALID_BLOCKS);
+		if (fattr->valid & NFS_ATTR_FATTR_OFFLINE)
+			nfsi->offline = (fattr->hsa_flags & NFS_HSA_OFFLINE) ? 1 : 0;
+		else if (fattr_supported & NFS_ATTR_FATTR_OFFLINE)
+			nfs_set_cache_invalid(inode, NFS_INO_INVALID_WINATTR);
 
 		nfs_setsecurity(inode, fattr);
 
@@ -2274,6 +2279,12 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
 		nfsi->cache_validity |=
 			save_cache_validity & NFS_INO_INVALID_BLOCKS;
 
+	if (fattr->valid & NFS_ATTR_FATTR_OFFLINE)
+		nfsi->offline = (fattr->hsa_flags & NFS_HSA_OFFLINE) ? 1 : 0;
+	else if (fattr_supported & NFS_ATTR_FATTR_OFFLINE)
+		nfsi->cache_validity |=
+			save_cache_validity & NFS_INO_INVALID_WINATTR;
+
 	/* Update attrtimeo value if we're out of the unstable period */
 	if (attr_changed) {
 		nfs_inc_stats(inode, NFSIOS_ATTRINVALIDATE);
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index d3a528a4c9b7..d497616ca149 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -218,6 +218,7 @@ const u32 nfs4_fattr_bitmap[3] = {
 #ifdef CONFIG_NFS_V4_SECURITY_LABEL
 	FATTR4_WORD2_SECURITY_LABEL
 #endif
+	| FATTR4_WORD2_OFFLINE
 };
 
 static const u32 nfs4_pnfs_open_bitmap[3] = {
@@ -244,6 +245,7 @@ static const u32 nfs4_pnfs_open_bitmap[3] = {
 #ifdef CONFIG_NFS_V4_SECURITY_LABEL
 	| FATTR4_WORD2_SECURITY_LABEL
 #endif
+	| FATTR4_WORD2_OFFLINE
 };
 
 static const u32 nfs4_open_noattr_bitmap[3] = {
@@ -324,6 +326,7 @@ static void nfs4_bitmap_copy_adjust(__u32 *dst, const __u32 *src,
 	if (!(cache_validity & NFS_INO_INVALID_WINATTR)) {
 		dst[0] &= ~(FATTR4_WORD0_ARCHIVE | FATTR4_WORD0_HIDDEN);
 		dst[1] &= ~(FATTR4_WORD1_SYSTEM | FATTR4_WORD1_TIME_BACKUP);
+		dst[2] &= ~FATTR4_WORD2_OFFLINE;
 	}
 }
 
@@ -3927,6 +3930,8 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f
 		memcpy(server->attr_bitmask_nl, res.attr_bitmask,
 				sizeof(server->attr_bitmask));
 		server->attr_bitmask_nl[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
+		if (!(res.attr_bitmask[2] & FATTR4_WORD2_OFFLINE))
+			server->fattr_valid &= ~NFS_ATTR_FATTR_OFFLINE;
 
 		memcpy(server->cache_consistency_bitmask, res.attr_bitmask, sizeof(server->cache_consistency_bitmask));
 		server->cache_consistency_bitmask[0] &= FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE;
@@ -5484,6 +5489,7 @@ static void nfs4_bitmask_set(__u32 bitmask[NFS4_BITMASK_SZ], const __u32 *src,
 	if (cache_validity & NFS_INO_INVALID_WINATTR) {
 		bitmask[0] |= FATTR4_WORD0_ARCHIVE | FATTR4_WORD0_HIDDEN;
 		bitmask[1] |= FATTR4_WORD1_SYSTEM | FATTR4_WORD1_TIME_BACKUP;
+		bitmask[2] |= FATTR4_WORD2_OFFLINE;
 	}
 
 	if (cache_validity & NFS_INO_INVALID_SIZE)
diff --git a/fs/nfs/nfs4trace.h b/fs/nfs/nfs4trace.h
index 5e72639b469e..02c78d66c36d 100644
--- a/fs/nfs/nfs4trace.h
+++ b/fs/nfs/nfs4trace.h
@@ -35,7 +35,8 @@
 		{ NFS_ATTR_FATTR_HIDDEN, "HIDDEN" }, \
 		{ NFS_ATTR_FATTR_SYSTEM, "SYSTEM" }, \
 		{ NFS_ATTR_FATTR_ARCHIVE, "ARCHIVE" }, \
-		{ NFS_ATTR_FATTR_TIME_BACKUP, "TIME_BACKUP" })
+		{ NFS_ATTR_FATTR_TIME_BACKUP, "TIME_BACKUP" }, \
+		{ NFS_ATTR_FATTR_OFFLINE, "OFFLINE" })
 
 DECLARE_EVENT_CLASS(nfs4_clientid_event,
 		TP_PROTO(
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 68885ba5fc58..d2c240effc87 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -1623,7 +1623,7 @@ static void encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg
 			    FATTR4_WORD1_TIME_CREATE |
 			    FATTR4_WORD1_TIME_METADATA |
 			    FATTR4_WORD1_TIME_MODIFY;
-		attrs[2] |= FATTR4_WORD2_SECURITY_LABEL;
+		attrs[2] |= FATTR4_WORD2_SECURITY_LABEL | FATTR4_WORD2_OFFLINE;
 		dircount >>= 1;
 	}
 	/* Use mounted_on_fileid only if the server supports it */
@@ -4353,6 +4353,29 @@ static int decode_attr_xattrsupport(struct xdr_stream *xdr, uint32_t *bitmap,
 	return 0;
 }
 
+static int decode_attr_offline(struct xdr_stream *xdr, uint32_t *bitmap,
+			       uint32_t *res, uint64_t *flags)
+{
+	int status = 0;
+	__be32 *p;
+
+	if (unlikely(bitmap[2] & (FATTR4_WORD2_OFFLINE - 1U)))
+		return -EIO;
+	if (likely(bitmap[2] & FATTR4_WORD2_OFFLINE)) {
+		p = xdr_inline_decode(xdr, 4);
+		if (unlikely(!p))
+			return -EIO;
+		if (be32_to_cpup(p))
+			*res |= NFS_HSA_OFFLINE;
+		else
+			*res &= ~NFS_HSA_OFFLINE;
+		bitmap[2] &= ~FATTR4_WORD2_OFFLINE;
+		*flags |= NFS_ATTR_FATTR_OFFLINE;
+	}
+	dprintk("%s: system file: =%s\n", __func__, (*res & NFS_HSA_OFFLINE) == 0 ? "false" : "true");
+	return status;
+}
+
 static int verify_attr_len(struct xdr_stream *xdr, unsigned int savep, uint32_t attrlen)
 {
 	unsigned int attrwords = XDR_QUADLEN(attrlen);
@@ -4841,6 +4864,12 @@ static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap,
 		fattr->valid |= status;
 	}
 
+	status = decode_attr_offline(xdr, bitmap, &fattr->hsa_flags,
+				     &fattr->valid);
+	if (status < 0)
+		goto xdr_error;
+
+	status = 0;
 xdr_error:
 	dprintk("%s: xdr returned %d\n", __func__, -status);
 	return status;
diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
index 5662d8be04eb..817b349c24ca 100644
--- a/include/linux/nfs4.h
+++ b/include/linux/nfs4.h
@@ -460,6 +460,7 @@ enum lock_type4 {
 #define FATTR4_WORD2_SECURITY_LABEL     (1UL << 16)
 #define FATTR4_WORD2_MODE_UMASK		(1UL << 17)
 #define FATTR4_WORD2_XATTR_SUPPORT	(1UL << 18)
+#define FATTR4_WORD2_OFFLINE		(1UL << 19)
 
 /* MDS threshold bitmap bits */
 #define THRESHOLD_RD                    (1UL << 0)
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 5f1dbd7a7b69..058fc11338d9 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -146,6 +146,7 @@ struct nfs_inode {
 	unsigned char		archive : 1;
 	unsigned char		hidden : 1;
 	unsigned char		system : 1;
+	unsigned char		offline : 1;
 
 	/*
 	 * read_cache_jiffies is when we started read-caching this inode.
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 69c19f465571..0d5b11c1bfec 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -21,6 +21,7 @@
 #define NFS_HSA_HIDDEN		BIT(0)
 #define NFS_HSA_SYSTEM		BIT(1)
 #define NFS_HSA_ARCHIVE		BIT(2)
+#define NFS_HSA_OFFLINE		BIT(3)
 
 struct nfs4_string {
 	unsigned int len;
@@ -119,6 +120,7 @@ struct nfs_fattr {
 #define NFS_ATTR_FATTR_SYSTEM           BIT_ULL(28)
 #define NFS_ATTR_FATTR_ARCHIVE          BIT_ULL(29)
 #define NFS_ATTR_FATTR_TIME_BACKUP      BIT_ULL(30)
+#define NFS_ATTR_FATTR_OFFLINE          BIT_ULL(31)
 
 #define NFS_ATTR_FATTR (NFS_ATTR_FATTR_TYPE \
 		| NFS_ATTR_FATTR_MODE \
@@ -144,7 +146,8 @@ struct nfs_fattr {
 		| NFS_ATTR_FATTR_SYSTEM \
 		| NFS_ATTR_FATTR_ARCHIVE \
 		| NFS_ATTR_FATTR_TIME_BACKUP \
-		| NFS_ATTR_FATTR_V4_SECURITY_LABEL)
+		| NFS_ATTR_FATTR_V4_SECURITY_LABEL \
+		| NFS_ATTR_FATTR_OFFLINE)
 
 /*
  * Maximal number of supported layout drivers.
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH 7/8] NFS: Support statx_get and statx_set ioctls
  2021-12-17 20:48           ` [PATCH 6/8] NFSv4: Support the offline bit trondmy
@ 2021-12-17 20:48             ` trondmy
  2021-12-17 20:48               ` [PATCH 8/8] NFSv4: Add an ioctl to allow retrieval of the NFS raw ACCESS mask trondmy
  2022-01-03 20:52               ` [PATCH 7/8] NFS: Support statx_get and statx_set ioctls J. Bruce Fields
  0 siblings, 2 replies; 30+ messages in thread
From: trondmy @ 2021-12-17 20:48 UTC (permalink / raw)
  To: Anna Schumaker; +Cc: linux-nfs

From: Richard Sharpe <richard.sharpe@primarydata.com>

Add support for returning all of the Windows attributes with a statx
ioctl.
Add support for setting all of the Windows attributes using an ioctl.

Signed-off-by: Richard Sharpe <richard.sharpe@primarydata.com>
Signed-off-by: Lance Shelton <lance.shelton@hammerspace.com>
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
---
 fs/nfs/dir.c             |  24 +-
 fs/nfs/getroot.c         |   3 +-
 fs/nfs/inode.c           |  41 +++-
 fs/nfs/internal.h        |   8 +
 fs/nfs/nfs3proc.c        |   1 +
 fs/nfs/nfs4_fs.h         |  31 +++
 fs/nfs/nfs4file.c        | 511 +++++++++++++++++++++++++++++++++++++++
 fs/nfs/nfs4proc.c        | 124 ++++++++++
 fs/nfs/nfs4xdr.c         |  63 ++++-
 fs/nfs/nfstrace.c        |   5 +
 fs/nfs/nfstrace.h        |   5 +
 fs/nfs/proc.c            |   1 +
 include/linux/nfs_fs.h   |   1 +
 include/linux/nfs_xdr.h  |   3 +
 include/uapi/linux/nfs.h |  90 +++++++
 15 files changed, 887 insertions(+), 24 deletions(-)

diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 731d31015b6a..f6fc60822153 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -48,11 +48,6 @@
 
 /* #define NFS_DEBUG_VERBOSE 1 */
 
-static int nfs_opendir(struct inode *, struct file *);
-static int nfs_closedir(struct inode *, struct file *);
-static int nfs_readdir(struct file *, struct dir_context *);
-static int nfs_fsync_dir(struct file *, loff_t, loff_t, int);
-static loff_t nfs_llseek_dir(struct file *, loff_t, int);
 static void nfs_readdir_clear_array(struct page*);
 
 const struct file_operations nfs_dir_operations = {
@@ -63,6 +58,7 @@ const struct file_operations nfs_dir_operations = {
 	.release	= nfs_closedir,
 	.fsync		= nfs_fsync_dir,
 };
+EXPORT_SYMBOL_GPL(nfs_dir_operations);
 
 const struct address_space_operations nfs_dir_aops = {
 	.freepage = nfs_readdir_clear_array,
@@ -104,8 +100,7 @@ static void put_nfs_open_dir_context(struct inode *dir, struct nfs_open_dir_cont
 /*
  * Open file
  */
-static int
-nfs_opendir(struct inode *inode, struct file *filp)
+int nfs_opendir(struct inode *inode, struct file *filp)
 {
 	int res = 0;
 	struct nfs_open_dir_context *ctx;
@@ -123,13 +118,14 @@ nfs_opendir(struct inode *inode, struct file *filp)
 out:
 	return res;
 }
+EXPORT_SYMBOL_GPL(nfs_opendir);
 
-static int
-nfs_closedir(struct inode *inode, struct file *filp)
+int nfs_closedir(struct inode *inode, struct file *filp)
 {
 	put_nfs_open_dir_context(file_inode(filp), filp->private_data);
 	return 0;
 }
+EXPORT_SYMBOL_GPL(nfs_closedir);
 
 struct nfs_cache_array_entry {
 	u64 cookie;
@@ -1064,7 +1060,7 @@ static int uncached_readdir(struct nfs_readdir_descriptor *desc)
    last cookie cache takes care of the common case of reading the
    whole directory.
  */
-static int nfs_readdir(struct file *file, struct dir_context *ctx)
+int nfs_readdir(struct file *file, struct dir_context *ctx)
 {
 	struct dentry	*dentry = file_dentry(file);
 	struct inode	*inode = d_inode(dentry);
@@ -1157,8 +1153,9 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx)
 	dfprintk(FILE, "NFS: readdir(%pD2) returns %d\n", file, res);
 	return res;
 }
+EXPORT_SYMBOL_GPL(nfs_readdir);
 
-static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int whence)
+loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int whence)
 {
 	struct nfs_open_dir_context *dir_ctx = filp->private_data;
 
@@ -1196,19 +1193,20 @@ static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int whence)
 	spin_unlock(&filp->f_lock);
 	return offset;
 }
+EXPORT_SYMBOL_GPL(nfs_llseek_dir);
 
 /*
  * All directory operations under NFS are synchronous, so fsync()
  * is a dummy operation.
  */
-static int nfs_fsync_dir(struct file *filp, loff_t start, loff_t end,
-			 int datasync)
+int nfs_fsync_dir(struct file *filp, loff_t start, loff_t end, int datasync)
 {
 	dfprintk(FILE, "NFS: fsync dir(%pD2) datasync %d\n", filp, datasync);
 
 	nfs_inc_stats(file_inode(filp), NFSIOS_VFSFSYNC);
 	return 0;
 }
+EXPORT_SYMBOL_GPL(nfs_fsync_dir);
 
 /**
  * nfs_force_lookup_revalidate - Mark the directory as having changed
diff --git a/fs/nfs/getroot.c b/fs/nfs/getroot.c
index 11ff2b2e060f..f872970d6240 100644
--- a/fs/nfs/getroot.c
+++ b/fs/nfs/getroot.c
@@ -127,7 +127,8 @@ int nfs_get_root(struct super_block *s, struct fs_context *fc)
 	if (server->caps & NFS_CAP_SECURITY_LABEL)
 		kflags |= SECURITY_LSM_NATIVE_LABELS;
 	if (ctx->clone_data.sb) {
-		if (d_inode(fc->root)->i_fop != &nfs_dir_operations) {
+		if (d_inode(fc->root)->i_fop !=
+		    server->nfs_client->rpc_ops->dir_ops) {
 			error = -ESTALE;
 			goto error_splat_root;
 		}
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 33f4410190b6..8da662a4953d 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -108,6 +108,7 @@ u64 nfs_compat_user_ino64(u64 fileid)
 		ino ^= fileid >> (sizeof(fileid)-sizeof(ino)) * 8;
 	return ino;
 }
+EXPORT_SYMBOL_GPL(nfs_compat_user_ino64);
 
 int nfs_drop_inode(struct inode *inode)
 {
@@ -501,7 +502,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
 			nfs_inode_init_regular(nfsi);
 		} else if (S_ISDIR(inode->i_mode)) {
 			inode->i_op = NFS_SB(sb)->nfs_client->rpc_ops->dir_inode_ops;
-			inode->i_fop = &nfs_dir_operations;
+			inode->i_fop = NFS_SB(sb)->nfs_client->rpc_ops->dir_ops;
 			inode->i_data.a_ops = &nfs_dir_aops;
 			nfs_inode_init_dir(nfsi);
 			/* Deal with crossing mountpoints */
@@ -867,6 +868,44 @@ static u32 nfs_get_valid_attrmask(struct inode *inode)
 	return reply_mask;
 }
 
+static int nfs_getattr_revalidate_force(struct dentry *dentry)
+{
+	struct inode *inode = d_inode(dentry);
+	struct nfs_server *server = NFS_SERVER(inode);
+
+	if (!(server->flags & NFS_MOUNT_NOAC))
+		nfs_readdirplus_parent_cache_miss(dentry);
+	else
+		nfs_readdirplus_parent_cache_hit(dentry);
+	return __nfs_revalidate_inode(server, inode);
+}
+
+static int nfs_getattr_revalidate_none(struct dentry *dentry)
+{
+	nfs_readdirplus_parent_cache_hit(dentry);
+	return NFS_STALE(d_inode(dentry)) ? -ESTALE : 0;
+}
+
+static int nfs_getattr_revalidate_maybe(struct dentry *dentry,
+					unsigned long flags)
+{
+	if (nfs_check_cache_invalid(d_inode(dentry), flags))
+		return nfs_getattr_revalidate_force(dentry);
+	return nfs_getattr_revalidate_none(dentry);
+}
+
+int nfs_getattr_revalidate(const struct path *path,
+			   unsigned long flags,
+			   unsigned int query_flags)
+{
+	if (query_flags & AT_STATX_FORCE_SYNC)
+		return nfs_getattr_revalidate_force(path->dentry);
+	if (!(query_flags & AT_STATX_DONT_SYNC))
+		return nfs_getattr_revalidate_maybe(path->dentry, flags);
+	return nfs_getattr_revalidate_none(path->dentry);
+}
+EXPORT_SYMBOL_GPL(nfs_getattr_revalidate);
+
 int nfs_getattr(struct user_namespace *mnt_userns, const struct path *path,
 		struct kstat *stat, u32 request_mask, unsigned int query_flags)
 {
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index 12f6acb483bb..9602a886f0f0 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -366,6 +366,12 @@ extern struct nfs_client *nfs_init_client(struct nfs_client *clp,
 			   const struct nfs_client_initdata *);
 
 /* dir.c */
+int nfs_opendir(struct inode *, struct file *);
+int nfs_closedir(struct inode *, struct file *);
+int nfs_readdir(struct file *file, struct dir_context *ctx);
+int nfs_fsync_dir(struct file *, loff_t, loff_t, int);
+loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int whence);
+
 extern void nfs_advise_use_readdirplus(struct inode *dir);
 extern void nfs_force_use_readdirplus(struct inode *dir);
 extern unsigned long nfs_access_cache_count(struct shrinker *shrink,
@@ -411,6 +417,8 @@ extern void nfs_set_cache_invalid(struct inode *inode, unsigned long flags);
 extern bool nfs_check_cache_invalid(struct inode *, unsigned long);
 extern int nfs_wait_bit_killable(struct wait_bit_key *key, int mode);
 extern int nfs_wait_atomic_killable(atomic_t *p, unsigned int mode);
+extern int nfs_getattr_revalidate(const struct path *path, unsigned long flags,
+				  unsigned int query_flags);
 
 /* super.c */
 extern const struct super_operations nfs_sops;
diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c
index 7100514d306b..091005e169b7 100644
--- a/fs/nfs/nfs3proc.c
+++ b/fs/nfs/nfs3proc.c
@@ -1018,6 +1018,7 @@ const struct nfs_rpc_ops nfs_v3_clientops = {
 	.dir_inode_ops	= &nfs3_dir_inode_operations,
 	.file_inode_ops	= &nfs3_file_inode_operations,
 	.file_ops	= &nfs_file_operations,
+	.dir_ops	= &nfs_dir_operations,
 	.nlmclnt_ops	= &nlmclnt_fl_close_lock_ops,
 	.getroot	= nfs3_proc_get_root,
 	.submount	= nfs_submount,
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index ed5eaca6801e..9f21d8520e99 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -248,6 +248,34 @@ struct nfs4_opendata {
 	int rpc_status;
 };
 
+struct nfs4_statx {
+	int		real_fd;		/* real FD to use,
+						   -1 means use current file */
+	__u32		fa_options;		/* statx flags */
+	__u64		fa_request[2];		/* Attributes requested */
+	__u64		fa_valid[2];		/* Attributes set */
+
+	struct timespec64 fa_time_backup;	/* Backup time */
+	struct timespec64 fa_btime;		/* Birth time */
+	/* Flag attributes */
+	__u64 fa_flags;
+	struct timespec64 fa_atime;		/* Access time */
+	struct timespec64 fa_mtime;		/* Modify time */
+	struct timespec64 fa_ctime;		/* Change time */
+	kuid_t		fa_owner_uid;		/* Owner User ID */
+	kgid_t		fa_group_gid;		/* Primary Group ID */
+        /* Normal stat fields after this */
+	__u32	 	fa_mode;		/* Mode */
+	unsigned int 	fa_nlink;
+	__u32		fa_blksize;
+	__u32		fa_spare;		/* Alignment */
+	__u64		fa_ino;
+	dev_t		fa_dev;
+	dev_t		fa_rdev;
+	loff_t		fa_size;
+	__u64		fa_blocks;
+};
+
 struct nfs4_add_xprt_data {
 	struct nfs_client	*clp;
 	const struct cred	*cred;
@@ -315,6 +343,9 @@ extern int nfs4_set_rw_stateid(nfs4_stateid *stateid,
 		const struct nfs_open_context *ctx,
 		const struct nfs_lock_context *l_ctx,
 		fmode_t fmode);
+int nfs4_set_nfs4_statx(struct inode *inode,
+		struct nfs4_statx *statx,
+		struct nfs_fattr *fattr);
 extern int nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
 			     struct nfs_fattr *fattr, struct inode *inode);
 extern int update_open_stateid(struct nfs4_state *state,
diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c
index e79ae4cbc395..494ebc7cd1c0 100644
--- a/fs/nfs/nfs4file.c
+++ b/fs/nfs/nfs4file.c
@@ -9,6 +9,8 @@
 #include <linux/falloc.h>
 #include <linux/mount.h>
 #include <linux/nfs_fs.h>
+#include <linux/time32.h>
+#include <linux/compat.h>
 #include <linux/nfs_ssc.h>
 #include "delegation.h"
 #include "internal.h"
@@ -132,6 +134,503 @@ nfs4_file_flush(struct file *file, fl_owner_t id)
 	return filemap_check_wb_err(file->f_mapping, since);
 }
 
+static int nfs_get_timespec64(struct timespec64 *ts,
+			      const struct nfs_ioctl_timespec __user *uts)
+{
+	__s64 dummy;
+	if (unlikely(get_user(dummy, &uts->tv_sec) != 0))
+		return EFAULT;
+	ts->tv_sec = dummy;
+	if (unlikely(get_user(dummy, &uts->tv_nsec) != 0))
+		return EFAULT;
+	ts->tv_nsec = dummy;
+	return 0;
+}
+
+static int nfs_put_timespec64(const struct timespec64 *ts,
+			      struct nfs_ioctl_timespec __user *uts)
+{
+	__s64 dummy;
+
+	dummy = ts->tv_sec;
+	if (unlikely(put_user(dummy, &uts->tv_sec) != 0))
+		return EFAULT;
+	dummy = ts->tv_nsec;
+	if (unlikely(put_user(dummy, &uts->tv_nsec) != 0))
+		return EFAULT;
+	return 0;
+}
+
+static struct file *nfs4_get_real_file(struct file *src, unsigned int fd)
+{
+	struct file *filp = fget_raw(fd);
+	int ret = -EBADF;
+
+	if (!filp)
+		goto out;
+	/* Validate that the files share the same underlying filesystem */
+	ret = -EXDEV;
+	if (file_inode(filp)->i_sb != file_inode(src)->i_sb)
+		goto out_put;
+	return filp;
+out_put:
+	fput(filp);
+out:
+	return ERR_PTR(ret);
+}
+
+static unsigned long nfs4_statx_request_to_cache_validity(__u64 request,
+							  u64 fattr_supported)
+{
+	unsigned long ret = 0;
+
+	if (request & NFS_FA_VALID_ATIME)
+		ret |= NFS_INO_INVALID_ATIME;
+	if (request & NFS_FA_VALID_CTIME)
+		ret |= NFS_INO_INVALID_CTIME;
+	if (request & NFS_FA_VALID_MTIME)
+		ret |= NFS_INO_INVALID_MTIME;
+	if (request & NFS_FA_VALID_SIZE)
+		ret |= NFS_INO_INVALID_SIZE;
+
+	if (request & NFS_FA_VALID_MODE)
+		ret |= NFS_INO_INVALID_MODE;
+	if (request & (NFS_FA_VALID_OWNER | NFS_FA_VALID_OWNER_GROUP))
+		ret |= NFS_INO_INVALID_OTHER;
+
+	if (request & NFS_FA_VALID_NLINK)
+		ret |= NFS_INO_INVALID_NLINK;
+	if (request & NFS_FA_VALID_BLOCKS)
+		ret |= NFS_INO_INVALID_BLOCKS;
+
+	if (request & NFS_FA_VALID_TIME_CREATE)
+		ret |= NFS_INO_INVALID_BTIME;
+
+	if (request & NFS_FA_VALID_ARCHIVE) {
+		if (fattr_supported & NFS_ATTR_FATTR_ARCHIVE)
+			ret |= NFS_INO_INVALID_WINATTR;
+		else if (fattr_supported & NFS_ATTR_FATTR_TIME_BACKUP)
+			ret |= NFS_INO_INVALID_WINATTR | NFS_INO_INVALID_MTIME;
+	}
+	if (request & (NFS_FA_VALID_TIME_BACKUP | NFS_FA_VALID_HIDDEN |
+		       NFS_FA_VALID_SYSTEM | NFS_FA_VALID_OFFLINE))
+		ret |= NFS_INO_INVALID_WINATTR;
+
+	return ret ? (ret | NFS_INO_INVALID_CHANGE) : 0;
+}
+
+static long nfs4_ioctl_file_statx_get(struct file *dst_file,
+				      struct nfs_ioctl_nfs4_statx __user *uarg)
+{
+	struct nfs4_statx args = {
+		.real_fd = -1,
+		.fa_valid = { 0 },
+	};
+	struct inode *inode;
+	struct nfs_inode *nfsi;
+	struct nfs_server *server;
+	u64 fattr_supported;
+	unsigned long reval_attr;
+	unsigned int reval_flags;
+	__u32 tmp;
+	int ret;
+
+	/*
+	 * We get the first word from the uarg as it tells us whether
+	 * to use the passed in struct file or use that fd to find the
+	 * struct file.
+	 */
+	if (get_user(args.real_fd, &uarg->real_fd))
+		return -EFAULT;
+
+	if (get_user(args.fa_options, &uarg->fa_options))
+		return -EFAULT;
+
+	if (get_user(args.fa_request[0], &uarg->fa_request[0]))
+		return -EFAULT;
+
+	if (args.real_fd >= 0) {
+		dst_file = nfs4_get_real_file(dst_file, args.real_fd);
+		if (IS_ERR(dst_file))
+			return PTR_ERR(dst_file);
+	}
+
+	/*
+	 * Backward compatibility: we stole the top 32 bits of 'real_fd'
+	 * to create the fa_options field, so if its value is -1, then
+	 * assume it is the high word of (__s64)real_fd == -1, and just
+	 * set it to zero.
+	 */
+	if (args.fa_options == 0xFFFF)
+		args.fa_options = 0;
+
+	inode = file_inode(dst_file);
+	nfsi = NFS_I(inode);
+	server = NFS_SERVER(inode);
+	fattr_supported = server->fattr_valid;
+
+	trace_nfs_ioctl_file_statx_get_enter(inode);
+
+	if (args.fa_options & NFS_FA_OPTIONS_FORCE_SYNC)
+		reval_flags = AT_STATX_FORCE_SYNC;
+	else if (args.fa_options & NFS_FA_OPTIONS_DONT_SYNC)
+		reval_flags = AT_STATX_DONT_SYNC;
+	else
+		reval_flags = AT_STATX_SYNC_AS_STAT;
+
+	reval_attr = nfs4_statx_request_to_cache_validity(args.fa_request[0],
+							  fattr_supported);
+
+	if ((reval_attr & (NFS_INO_INVALID_CTIME | NFS_INO_INVALID_MTIME)) &&
+	    reval_flags != AT_STATX_DONT_SYNC && S_ISREG(inode->i_mode)) {
+		ret = filemap_write_and_wait(inode->i_mapping);
+		if (ret)
+			goto out;
+	}
+
+	if ((dst_file->f_path.mnt->mnt_flags & MNT_NOATIME) ||
+	    ((dst_file->f_path.mnt->mnt_flags & MNT_NODIRATIME) &&
+	     S_ISDIR(inode->i_mode)))
+		reval_attr &= ~NFS_INO_INVALID_ATIME;
+
+	ret = nfs_getattr_revalidate(&dst_file->f_path, reval_attr,
+				     reval_flags);
+	if (ret != 0)
+		goto out;
+
+	ret = -EFAULT;
+	if ((fattr_supported & NFS_ATTR_FATTR_OWNER) &&
+	    (args.fa_request[0] & NFS_FA_VALID_OWNER)) {
+		tmp = from_kuid_munged(current_user_ns(), inode->i_uid);
+		if (unlikely(put_user(tmp, &uarg->fa_owner_uid) != 0))
+			goto out;
+		args.fa_valid[0] |= NFS_FA_VALID_OWNER;
+	}
+
+	if ((fattr_supported & NFS_ATTR_FATTR_GROUP) &&
+	    (args.fa_request[0] & NFS_FA_VALID_OWNER_GROUP)) {
+		tmp = from_kgid_munged(current_user_ns(), inode->i_gid);
+		if (unlikely(put_user(tmp, &uarg->fa_group_gid) != 0))
+			goto out;
+		args.fa_valid[0] |= NFS_FA_VALID_OWNER_GROUP;
+	}
+
+	if ((fattr_supported & NFS_ATTR_FATTR_TIME_BACKUP) &&
+	    (args.fa_request[0] & NFS_FA_VALID_TIME_BACKUP)) {
+		if (nfs_put_timespec64(&nfsi->timebackup, &uarg->fa_time_backup))
+			goto out;
+		args.fa_valid[0] |= NFS_FA_VALID_TIME_BACKUP;
+	}
+
+	if ((fattr_supported & NFS_ATTR_FATTR_BTIME) &&
+	    (args.fa_request[0] & NFS_FA_VALID_TIME_CREATE)) {
+		if (nfs_put_timespec64(&nfsi->btime, &uarg->fa_btime))
+			goto out;
+		args.fa_valid[0] |= NFS_FA_VALID_TIME_CREATE;
+	}
+
+	/* atime, mtime, and ctime are all stored in the regular inode,
+	 * not the nfs inode.
+	 */
+	if ((fattr_supported & NFS_ATTR_FATTR_ATIME) &&
+	    (args.fa_request[0] & NFS_FA_VALID_ATIME)) {
+		if (nfs_put_timespec64(&inode->i_atime, &uarg->fa_atime))
+			goto out;
+		args.fa_valid[0] |= NFS_FA_VALID_ATIME;
+	}
+
+	if ((fattr_supported & NFS_ATTR_FATTR_MTIME) &&
+	    (args.fa_request[0] & NFS_FA_VALID_MTIME)) {
+		if (nfs_put_timespec64(&inode->i_mtime, &uarg->fa_mtime))
+                        goto out;
+		args.fa_valid[0] |= NFS_FA_VALID_MTIME;
+	}
+
+	if ((fattr_supported & NFS_ATTR_FATTR_CTIME) &&
+	    (args.fa_request[0] & NFS_FA_VALID_CTIME)) {
+		if (nfs_put_timespec64(&inode->i_ctime, &uarg->fa_ctime))
+			goto out;
+		args.fa_valid[0] |= NFS_FA_VALID_CTIME;
+	}
+
+        /*
+         * It looks like PDFS does not support or properly handle the
+         * archive bit.
+         */
+	if ((fattr_supported & NFS_ATTR_FATTR_ARCHIVE) &&
+	    (args.fa_request[0] & NFS_FA_VALID_ARCHIVE)) {
+		if (nfsi->archive)
+			args.fa_flags |= NFS_FA_FLAG_ARCHIVE;
+		args.fa_valid[0] |= NFS_FA_VALID_ARCHIVE;
+	}
+
+	if ((fattr_supported & NFS_ATTR_FATTR_TIME_BACKUP) &&
+	    (args.fa_request[0] & NFS_FA_VALID_ARCHIVE)) {
+		if (timespec64_compare(&inode->i_mtime, &nfsi->timebackup) > 0)
+			args.fa_flags |= NFS_FA_FLAG_ARCHIVE;
+		args.fa_valid[0] |= NFS_FA_VALID_ARCHIVE;
+	}
+
+	if ((fattr_supported & NFS_ATTR_FATTR_HIDDEN) &&
+	    (args.fa_request[0] & NFS_FA_VALID_HIDDEN)) {
+		if (nfsi->hidden)
+			args.fa_flags |= NFS_FA_FLAG_HIDDEN;
+		args.fa_valid[0] |= NFS_FA_VALID_HIDDEN;
+	}
+	if ((fattr_supported & NFS_ATTR_FATTR_SYSTEM) &&
+	    (args.fa_request[0] & NFS_FA_VALID_SYSTEM)) {
+		if (nfsi->system)
+			args.fa_flags |= NFS_FA_FLAG_SYSTEM;
+		args.fa_valid[0] |= NFS_FA_VALID_SYSTEM;
+	}
+
+	if ((fattr_supported & NFS_ATTR_FATTR_OFFLINE) &&
+	    (args.fa_request[0] & NFS_FA_VALID_OFFLINE)) {
+		if (nfsi->offline)
+			args.fa_flags |= NFS_FA_FLAG_OFFLINE;
+		args.fa_valid[0] |= NFS_FA_VALID_OFFLINE;
+	}
+
+	if ((args.fa_valid[0] & (NFS_FA_VALID_ARCHIVE |
+				NFS_FA_VALID_HIDDEN |
+				NFS_FA_VALID_SYSTEM |
+				NFS_FA_VALID_OFFLINE)) &&
+	    put_user(args.fa_flags, &uarg->fa_flags))
+		goto out;
+
+	if ((fattr_supported & NFS_ATTR_FATTR_MODE) &&
+	    (args.fa_request[0] & NFS_FA_VALID_MODE)) {
+		tmp = inode->i_mode;
+		/* This is an unsigned short we put into an __u32 */
+		if (unlikely(put_user(tmp, &uarg->fa_mode) != 0))
+			goto out;
+		args.fa_valid[0] |= NFS_FA_VALID_MODE;
+	}
+
+	if ((fattr_supported & NFS_ATTR_FATTR_NLINK) &&
+	    (args.fa_request[0] & NFS_FA_VALID_NLINK)) {
+		tmp = inode->i_nlink;
+		if (unlikely(put_user(tmp, &uarg->fa_nlink) != 0))
+			goto out;
+		args.fa_valid[0] |= NFS_FA_VALID_NLINK;
+	}
+
+	if (args.fa_request[0] & NFS_FA_VALID_BLKSIZE) {
+		tmp = i_blocksize(inode);
+		if (S_ISDIR(inode->i_mode))
+			tmp = NFS_SERVER(inode)->dtsize;
+		if (unlikely(put_user(tmp, &uarg->fa_blksize) != 0))
+			goto out;
+		args.fa_valid[0] |= NFS_FA_VALID_BLKSIZE;
+	}
+
+	if (args.fa_request[0] & NFS_FA_VALID_INO) {
+		__u64 ino = nfs_compat_user_ino64(NFS_FILEID(inode));
+		if (unlikely(put_user(ino, &uarg->fa_ino) != 0))
+			goto out;
+		args.fa_valid[0] |= NFS_FA_VALID_INO;
+	}
+
+	if (args.fa_request[0] & NFS_FA_VALID_DEV) {
+		tmp = inode->i_sb->s_dev;
+		if (unlikely(put_user(tmp, &uarg->fa_dev) != 0))
+			goto out;
+		args.fa_valid[0] |= NFS_FA_VALID_DEV;
+	}
+
+	if ((fattr_supported & NFS_ATTR_FATTR_RDEV) &&
+	    (args.fa_request[0] & NFS_FA_VALID_RDEV)) {
+		tmp = inode->i_rdev;
+		if (unlikely(put_user(tmp, &uarg->fa_rdev) != 0))
+			goto out;
+		args.fa_valid[0] |= NFS_FA_VALID_RDEV;
+	}
+
+	if ((fattr_supported & NFS_ATTR_FATTR_SIZE) &&
+	    (args.fa_request[0] & NFS_FA_VALID_SIZE)) {
+		__s64 size = i_size_read(inode);
+		if (unlikely(put_user(size, &uarg->fa_size) != 0))
+			goto out;
+		args.fa_valid[0] |= NFS_FA_VALID_SIZE;
+	}
+
+	if ((fattr_supported &
+	     (NFS_ATTR_FATTR_BLOCKS_USED | NFS_ATTR_FATTR_SPACE_USED)) &&
+	    (args.fa_request[0] & NFS_FA_VALID_BLOCKS)) {
+		__s64 blocks = inode->i_blocks;
+		if (unlikely(put_user(blocks, &uarg->fa_blocks) != 0))
+			goto out;
+		args.fa_valid[0] |= NFS_FA_VALID_BLOCKS;
+	}
+
+	if (unlikely(put_user(args.fa_valid[0], &uarg->fa_valid[0]) != 0))
+		goto out;
+	if (unlikely(put_user(args.fa_valid[1], &uarg->fa_valid[1]) != 0))
+		goto out;
+
+	ret = 0;
+out:
+	if (args.real_fd >= 0)
+		fput(dst_file);
+	trace_nfs_ioctl_file_statx_get_exit(inode, ret);
+	return ret;
+}
+
+static long nfs4_ioctl_file_statx_set(struct file *dst_file,
+				      struct nfs_ioctl_nfs4_statx __user *uarg)
+{
+	struct nfs4_statx args = {
+		.real_fd = -1,
+		.fa_valid = { 0 },
+	};
+	struct nfs_fattr *fattr = nfs_alloc_fattr();
+	struct inode *inode;
+	/*
+	 * If you need a different error code below, you need to set it
+	 */
+	int ret = -EFAULT;
+
+	if (fattr == NULL)
+		return -ENOMEM;
+
+	/*
+	 * We get the first u64 word from the uarg as it tells us whether
+	 * to use the passed in struct file or use that fd to find the
+	 * struct file.
+	 */
+	if (get_user(args.real_fd, &uarg->real_fd))
+		goto out_free;
+
+	if (args.real_fd >= 0) {
+		dst_file = nfs4_get_real_file(dst_file, args.real_fd);
+		if (IS_ERR(dst_file)) {
+			ret = PTR_ERR(dst_file);
+			goto out_free;
+		}
+	}
+	inode = file_inode(dst_file);
+	trace_nfs_ioctl_file_statx_set_enter(inode);
+
+	inode_lock(inode);
+
+	/* Write all dirty data */
+	if (S_ISREG(inode->i_mode)) {
+		ret = nfs_sync_inode(inode);
+		if (ret)
+			goto out;
+	}
+
+	ret = -EFAULT;
+	if (get_user(args.fa_valid[0], &uarg->fa_valid[0]))
+		goto out;
+	args.fa_valid[0] &= NFS_FA_VALID_ALL_ATTR_0;
+
+	if (args.fa_valid[0] & NFS_FA_VALID_OWNER) {
+		uid_t uid;
+
+		if (unlikely(get_user(uid, &uarg->fa_owner_uid) != 0))
+			goto out;
+		args.fa_owner_uid = make_kuid(current_user_ns(), uid);
+		if (!uid_valid(args.fa_owner_uid)) {
+			ret = -EINVAL;
+			goto out;
+		}
+	}
+
+	if (args.fa_valid[0] & NFS_FA_VALID_OWNER_GROUP) {
+		gid_t gid;
+
+		if (unlikely(get_user(gid, &uarg->fa_group_gid) != 0))
+			goto out;
+		args.fa_group_gid = make_kgid(current_user_ns(), gid);
+		if (!gid_valid(args.fa_group_gid)) {
+			ret = -EINVAL;
+			goto out;
+		}
+	}
+
+	if ((args.fa_valid[0] & (NFS_FA_VALID_ARCHIVE |
+					NFS_FA_VALID_HIDDEN |
+					NFS_FA_VALID_SYSTEM)) &&
+	    get_user(args.fa_flags, &uarg->fa_flags))
+		goto out;
+
+	if ((args.fa_valid[0] & NFS_FA_VALID_TIME_CREATE) &&
+	    nfs_get_timespec64(&args.fa_btime, &uarg->fa_btime))
+		goto out;
+
+	if ((args.fa_valid[0] & NFS_FA_VALID_ATIME) &&
+	    nfs_get_timespec64(&args.fa_atime, &uarg->fa_atime))
+		goto out;
+
+	if ((args.fa_valid[0] & NFS_FA_VALID_MTIME) &&
+	    nfs_get_timespec64(&args.fa_mtime, &uarg->fa_mtime))
+		goto out;
+
+	if (args.fa_valid[0] & NFS_FA_VALID_TIME_BACKUP) {
+		if (nfs_get_timespec64(&args.fa_time_backup, &uarg->fa_time_backup))
+			goto out;
+	} else if ((args.fa_valid[0] & NFS_FA_VALID_ARCHIVE) &&
+			!(NFS_SERVER(inode)->fattr_valid & NFS_ATTR_FATTR_ARCHIVE)) {
+		args.fa_valid[0] |= NFS_FA_VALID_TIME_BACKUP;
+		if (!(args.fa_flags & NFS_FA_FLAG_ARCHIVE)) {
+			nfs_revalidate_inode(inode, NFS_INO_INVALID_MTIME);
+			args.fa_time_backup.tv_sec = inode->i_mtime.tv_sec;
+			args.fa_time_backup.tv_nsec = inode->i_mtime.tv_nsec;
+		} else if (args.fa_valid[0] & NFS_FA_VALID_TIME_CREATE)
+			args.fa_time_backup = args.fa_btime;
+		else {
+			nfs_revalidate_inode(inode, NFS_INO_INVALID_BTIME);
+			args.fa_time_backup = NFS_I(inode)->btime;
+		}
+	}
+
+        if (args.fa_valid[0] & NFS_FA_VALID_SIZE) {
+		if (copy_from_user(&args.fa_size, &uarg->fa_size,
+					sizeof(args.fa_size)))
+			goto out;
+		ret = inode_newsize_ok(inode,args.fa_size);
+		if (ret)
+			goto out;
+		if (args.fa_size == i_size_read(inode))
+			args.fa_valid[0] &= ~NFS_FA_VALID_SIZE;
+	}
+
+	/*
+	 * No need to update the inode because that is done in nfs4_set_nfs4_statx
+	 */
+	ret = nfs4_set_nfs4_statx(inode, &args, fattr);
+
+out:
+	inode_unlock(inode);
+	if (args.real_fd >= 0)
+		fput(dst_file);
+	trace_nfs_ioctl_file_statx_set_exit(inode, ret);
+out_free:
+	nfs_free_fattr(fattr);
+	return ret;
+}
+
+static long nfs4_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	void __user *argp = (void __user *)arg;
+	long ret;
+
+	switch (cmd) {
+	case NFS_IOC_FILE_STATX_GET:
+		ret = nfs4_ioctl_file_statx_get(file, argp);
+		break;
+	case NFS_IOC_FILE_STATX_SET:
+		ret = nfs4_ioctl_file_statx_set(file, argp);
+		break;
+	default:
+		ret = -ENOIOCTLCMD;
+	}
+
+	dprintk("%s: file=%pD2, cmd=%u, ret=%ld\n", __func__, file, cmd, ret);
+	return ret;
+}
+
 #ifdef CONFIG_NFS_V4_2
 static ssize_t __nfs4_copy_file_range(struct file *file_in, loff_t pos_in,
 				      struct file *file_out, loff_t pos_out,
@@ -187,6 +686,7 @@ static ssize_t __nfs4_copy_file_range(struct file *file_in, loff_t pos_in,
 	return ret;
 }
 
+
 static ssize_t nfs4_copy_file_range(struct file *file_in, loff_t pos_in,
 				    struct file *file_out, loff_t pos_out,
 				    size_t count, unsigned int flags)
@@ -461,4 +961,15 @@ const struct file_operations nfs4_file_operations = {
 #else
 	.llseek		= nfs_file_llseek,
 #endif
+	.unlocked_ioctl	= nfs4_ioctl,
+};
+
+const struct file_operations nfs4_dir_operations = {
+	.llseek		= nfs_llseek_dir,
+	.read		= generic_read_dir,
+	.iterate_shared	= nfs_readdir,
+	.open		= nfs_opendir,
+	.release	= nfs_closedir,
+	.fsync		= nfs_fsync_dir,
+	.unlocked_ioctl	= nfs4_ioctl,
 };
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index d497616ca149..7c032583ffa2 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -7959,6 +7959,129 @@ static int _nfs41_proc_get_locations(struct inode *inode,
 
 #endif	/* CONFIG_NFS_V4_1 */
 
+static int _nfs4_set_nfs4_statx(struct inode *inode,
+		struct nfs4_statx *statx,
+		struct nfs_fattr *fattr)
+{
+	const __u64 statx_win = NFS_FA_VALID_TIME_CREATE |
+				NFS_FA_VALID_TIME_BACKUP |
+				NFS_FA_VALID_ARCHIVE | NFS_FA_VALID_HIDDEN |
+				NFS_FA_VALID_SYSTEM;
+	struct iattr sattr = {0};
+	struct nfs_server *server = NFS_SERVER(inode);
+	__u32 bitmask[3];
+	struct nfs_setattrargs arg = {
+		.fh             = NFS_FH(inode),
+		.iap            = &sattr,
+		.server		= server,
+		.bitmask	= bitmask,
+		.statx		= statx,
+	};
+	struct nfs_setattrres res = {
+		.fattr		= fattr,
+		.server		= server,
+	};
+	struct rpc_message msg = {
+		.rpc_proc       = &nfs4_procedures[NFSPROC4_CLNT_SETATTR],
+		.rpc_argp       = &arg,
+		.rpc_resp       = &res,
+	};
+	int status;
+
+	nfs4_bitmap_copy_adjust(
+		bitmask, server->attr_bitmask, inode,
+		NFS_INO_INVALID_CHANGE | NFS_INO_INVALID_CTIME |
+			NFS_INO_INVALID_SIZE | NFS_INO_INVALID_OTHER |
+			NFS_INO_INVALID_BTIME | NFS_INO_INVALID_WINATTR);
+	/* Use the iattr structure to set atime and mtime since handling already
+	 * exists for them using the iattr struct in the encode_attrs()
+	 * (xdr encoding) routine.
+	 */
+	if (statx && (statx->fa_valid[0] & NFS_FA_VALID_MTIME)) {
+		sattr.ia_valid |= ATTR_MTIME_SET;
+		sattr.ia_mtime.tv_sec = statx->fa_mtime.tv_sec;
+		sattr.ia_mtime.tv_nsec = statx->fa_mtime.tv_nsec;
+	}
+
+	if (statx && (statx->fa_valid[0] & NFS_FA_VALID_ATIME)) {
+		sattr.ia_valid |= ATTR_ATIME_SET;
+		sattr.ia_atime.tv_sec = statx->fa_atime.tv_sec;
+		sattr.ia_atime.tv_nsec = statx->fa_atime.tv_nsec;
+	}
+
+	if (statx && (statx->fa_valid[0] & NFS_FA_VALID_OWNER)) {
+		sattr.ia_valid |= ATTR_UID;
+		sattr.ia_uid = statx->fa_owner_uid;
+	}
+
+	if (statx && (statx->fa_valid[0] & NFS_FA_VALID_OWNER_GROUP)) {
+		sattr.ia_valid |= ATTR_GID;
+		sattr.ia_gid = statx->fa_group_gid;
+	}
+
+	if (statx && (statx->fa_valid[0] & NFS_FA_VALID_SIZE)) {
+		sattr.ia_valid |= ATTR_SIZE;
+		sattr.ia_size = statx->fa_size;
+	}
+
+	nfs4_stateid_copy(&arg.stateid, &zero_stateid);
+
+	status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
+	if (!status) {
+		if (statx->fa_valid[0] & statx_win) {
+			struct nfs_inode *nfsi = NFS_I(inode);
+
+			spin_lock(&inode->i_lock);
+			if (statx->fa_valid[0] & NFS_FA_VALID_TIME_CREATE)
+				nfsi->btime = statx->fa_btime;
+			if (statx->fa_valid[0] & NFS_FA_VALID_TIME_BACKUP)
+				nfsi->timebackup = statx->fa_time_backup;
+			if (statx->fa_valid[0] & NFS_FA_VALID_ARCHIVE)
+				nfsi->archive = (statx->fa_flags &
+						 NFS_FA_FLAG_ARCHIVE) != 0;
+			if (statx->fa_valid[0] & NFS_FA_VALID_SYSTEM)
+				nfsi->system = (statx->fa_flags &
+						NFS_FA_FLAG_SYSTEM) != 0;
+			if (statx->fa_valid[0] & NFS_FA_VALID_HIDDEN)
+				nfsi->hidden = (statx->fa_flags &
+						NFS_FA_FLAG_HIDDEN) != 0;
+			if (statx->fa_valid[0] & NFS_FA_VALID_OFFLINE)
+				nfsi->offline = (statx->fa_flags &
+						 NFS_FA_FLAG_OFFLINE) != 0;
+
+			nfsi->cache_validity &= ~NFS_INO_INVALID_CTIME;
+			if (fattr->valid & NFS_ATTR_FATTR_CTIME)
+				inode->i_ctime = fattr->ctime;
+			else
+				nfs_set_cache_invalid(
+					inode, NFS_INO_INVALID_CHANGE |
+						   NFS_INO_INVALID_CTIME);
+			spin_unlock(&inode->i_lock);
+		}
+
+		nfs_setattr_update_inode(inode, &sattr, fattr);
+	} else
+		dprintk("%s failed: %d\n", __func__, status);
+
+	return status;
+}
+
+int nfs4_set_nfs4_statx(struct inode *inode,
+		struct nfs4_statx *statx,
+		struct nfs_fattr *fattr)
+{
+	struct nfs4_exception exception = { };
+	struct nfs_server *server = NFS_SERVER(inode);
+	int err;
+
+	do {
+		err = nfs4_handle_exception(server,
+				_nfs4_set_nfs4_statx(inode, statx, fattr),
+				&exception);
+	} while (exception.retry);
+	return err;
+}
+
 /**
  * nfs4_proc_get_locations - discover locations for a migrated FSID
  * @inode: inode on FSID that is migrating
@@ -10419,6 +10542,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
 	.dir_inode_ops	= &nfs4_dir_inode_operations,
 	.file_inode_ops	= &nfs4_file_inode_operations,
 	.file_ops	= &nfs4_file_operations,
+	.dir_ops	= &nfs4_dir_operations,
 	.getroot	= nfs4_proc_get_root,
 	.submount	= nfs4_submount,
 	.try_get_tree	= nfs4_try_get_tree,
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index d2c240effc87..e5300d7ed712 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -129,12 +129,15 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
 				nfs4_fattr_value_maxsz)
 #define decode_getattr_maxsz    (op_decode_hdr_maxsz + nfs4_fattr_maxsz)
 #define encode_attrs_maxsz	(nfs4_fattr_bitmap_maxsz + \
-				 1 + 2 + 1 + \
+				 1 + 2 + 1 + 1 + 1 + \
 				nfs4_owner_maxsz + \
 				nfs4_group_maxsz + \
-				nfs4_label_maxsz + \
+				1 + \
+				1 + nfstime4_maxsz + \
+				nfstime4_maxsz + nfstime4_maxsz + \
 				1 + nfstime4_maxsz + \
-				1 + nfstime4_maxsz)
+				nfs4_label_maxsz + \
+				2)
 #define encode_savefh_maxsz     (op_encode_hdr_maxsz)
 #define decode_savefh_maxsz     (op_decode_hdr_maxsz)
 #define encode_restorefh_maxsz  (op_encode_hdr_maxsz)
@@ -1081,6 +1084,7 @@ xdr_encode_nfstime4(__be32 *p, const struct timespec64 *t)
 static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
 				const struct nfs4_label *label,
 				const umode_t *umask,
+				const struct nfs4_statx *statx,
 				const struct nfs_server *server,
 				const uint32_t attrmask[])
 {
@@ -1153,6 +1157,34 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
 		}
 	}
 
+	if (statx && (statx->fa_valid[0] & NFS_FA_VALID_TIME_BACKUP) &&
+	    (attrmask[1] & FATTR4_WORD1_TIME_BACKUP)) {
+		bmval[1] |= FATTR4_WORD1_TIME_BACKUP;
+		len += (nfstime4_maxsz << 2);
+	}
+	if (statx && (statx->fa_valid[0] & NFS_FA_VALID_TIME_CREATE) &&
+	    (attrmask[1] & FATTR4_WORD1_TIME_CREATE)) {
+		bmval[1] |= FATTR4_WORD1_TIME_CREATE;
+		len += (nfstime4_maxsz << 2);
+	}
+
+	if (statx && (statx->fa_valid[0] & NFS_FA_VALID_ARCHIVE) &&
+	   (attrmask[0] & FATTR4_WORD0_ARCHIVE)) {
+		bmval[0] |= FATTR4_WORD0_ARCHIVE;
+		len += 4;
+	}
+	if (statx && (statx->fa_valid[0] & NFS_FA_VALID_HIDDEN) &&
+	   (attrmask[0] & FATTR4_WORD0_HIDDEN)) {
+		bmval[0] |= FATTR4_WORD0_HIDDEN;
+		len += 4;
+	}
+
+	if (statx && (statx->fa_valid[0] & NFS_FA_VALID_SYSTEM) &&
+	   (attrmask[1] & FATTR4_WORD1_SYSTEM)) {
+		bmval[1] |= FATTR4_WORD1_SYSTEM;
+		len += 4;
+	}
+
 	if (label && (attrmask[2] & FATTR4_WORD2_SECURITY_LABEL)) {
 		len += 4 + 4 + 4 + (XDR_QUADLEN(label->len) << 2);
 		bmval[2] |= FATTR4_WORD2_SECURITY_LABEL;
@@ -1163,12 +1195,21 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
 
 	if (bmval[0] & FATTR4_WORD0_SIZE)
 		p = xdr_encode_hyper(p, iap->ia_size);
+	if (bmval[0] & FATTR4_WORD0_ARCHIVE)
+		*p++ = (statx->fa_flags & NFS_FA_FLAG_ARCHIVE) ?
+			cpu_to_be32(1) : cpu_to_be32(0);
+	if (bmval[0] & FATTR4_WORD0_HIDDEN)
+		*p++ = (statx->fa_flags & NFS_FA_FLAG_HIDDEN) ?
+			cpu_to_be32(1) : cpu_to_be32(0);
 	if (bmval[1] & FATTR4_WORD1_MODE)
 		*p++ = cpu_to_be32(iap->ia_mode & S_IALLUGO);
 	if (bmval[1] & FATTR4_WORD1_OWNER)
 		p = xdr_encode_opaque(p, owner_name, owner_namelen);
 	if (bmval[1] & FATTR4_WORD1_OWNER_GROUP)
 		p = xdr_encode_opaque(p, owner_group, owner_grouplen);
+	if (bmval[1] & FATTR4_WORD1_SYSTEM)
+		*p++ = (statx->fa_flags & NFS_FA_FLAG_SYSTEM) ?
+			cpu_to_be32(1) : cpu_to_be32(0);
 	if (bmval[1] & FATTR4_WORD1_TIME_ACCESS_SET) {
 		if (iap->ia_valid & ATTR_ATIME_SET) {
 			*p++ = cpu_to_be32(NFS4_SET_TO_CLIENT_TIME);
@@ -1176,6 +1217,10 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
 		} else
 			*p++ = cpu_to_be32(NFS4_SET_TO_SERVER_TIME);
 	}
+	if (bmval[1] & FATTR4_WORD1_TIME_BACKUP)
+		p = xdr_encode_nfstime4(p, &statx->fa_time_backup);
+	if (bmval[1] & FATTR4_WORD1_TIME_CREATE)
+		p = xdr_encode_nfstime4(p, &statx->fa_btime);
 	if (bmval[1] & FATTR4_WORD1_TIME_MODIFY_SET) {
 		if (iap->ia_valid & ATTR_MTIME_SET) {
 			*p++ = cpu_to_be32(NFS4_SET_TO_CLIENT_TIME);
@@ -1248,7 +1293,7 @@ static void encode_create(struct xdr_stream *xdr, const struct nfs4_create_arg *
 
 	encode_string(xdr, create->name->len, create->name->name);
 	encode_attrs(xdr, create->attrs, create->label, &create->umask,
-			create->server, create->server->attr_bitmask);
+		     NULL, create->server, create->server->attr_bitmask);
 }
 
 static void encode_getattr(struct xdr_stream *xdr,
@@ -1434,12 +1479,12 @@ static inline void encode_createmode(struct xdr_stream *xdr, const struct nfs_op
 	case NFS4_CREATE_UNCHECKED:
 		*p = cpu_to_be32(NFS4_CREATE_UNCHECKED);
 		encode_attrs(xdr, arg->u.attrs, arg->label, &arg->umask,
-				arg->server, arg->server->attr_bitmask);
+			     NULL, arg->server, arg->server->attr_bitmask);
 		break;
 	case NFS4_CREATE_GUARDED:
 		*p = cpu_to_be32(NFS4_CREATE_GUARDED);
 		encode_attrs(xdr, arg->u.attrs, arg->label, &arg->umask,
-				arg->server, arg->server->attr_bitmask);
+			     NULL, arg->server, arg->server->attr_bitmask);
 		break;
 	case NFS4_CREATE_EXCLUSIVE:
 		*p = cpu_to_be32(NFS4_CREATE_EXCLUSIVE);
@@ -1449,7 +1494,7 @@ static inline void encode_createmode(struct xdr_stream *xdr, const struct nfs_op
 		*p = cpu_to_be32(NFS4_CREATE_EXCLUSIVE4_1);
 		encode_nfs4_verifier(xdr, &arg->u.verifier);
 		encode_attrs(xdr, arg->u.attrs, arg->label, &arg->umask,
-				arg->server, arg->server->exclcreat_bitmask);
+			     NULL, arg->server, arg->server->exclcreat_bitmask);
 	}
 }
 
@@ -1712,8 +1757,8 @@ static void encode_setattr(struct xdr_stream *xdr, const struct nfs_setattrargs
 {
 	encode_op_hdr(xdr, OP_SETATTR, decode_setattr_maxsz, hdr);
 	encode_nfs4_stateid(xdr, &arg->stateid);
-	encode_attrs(xdr, arg->iap, arg->label, NULL, server,
-			server->attr_bitmask);
+	encode_attrs(xdr, arg->iap, arg->label, NULL, arg->statx, server,
+		     server->attr_bitmask);
 }
 
 static void encode_setclientid(struct xdr_stream *xdr, const struct nfs4_setclientid *setclientid, struct compound_hdr *hdr)
diff --git a/fs/nfs/nfstrace.c b/fs/nfs/nfstrace.c
index 5d1bfccbb4da..0b88deb0216e 100644
--- a/fs/nfs/nfstrace.c
+++ b/fs/nfs/nfstrace.c
@@ -9,6 +9,11 @@
 #define CREATE_TRACE_POINTS
 #include "nfstrace.h"
 
+EXPORT_TRACEPOINT_SYMBOL_GPL(nfs_ioctl_file_statx_get_enter);
+EXPORT_TRACEPOINT_SYMBOL_GPL(nfs_ioctl_file_statx_get_exit);
+EXPORT_TRACEPOINT_SYMBOL_GPL(nfs_ioctl_file_statx_set_enter);
+EXPORT_TRACEPOINT_SYMBOL_GPL(nfs_ioctl_file_statx_set_exit);
+
 EXPORT_TRACEPOINT_SYMBOL_GPL(nfs_fsync_enter);
 EXPORT_TRACEPOINT_SYMBOL_GPL(nfs_fsync_exit);
 EXPORT_TRACEPOINT_SYMBOL_GPL(nfs_xdr_status);
diff --git a/fs/nfs/nfstrace.h b/fs/nfs/nfstrace.h
index 2ef7cff8a4ba..b67dd087fb47 100644
--- a/fs/nfs/nfstrace.h
+++ b/fs/nfs/nfstrace.h
@@ -166,6 +166,11 @@ DEFINE_NFS_INODE_EVENT_DONE(nfs_fsync_exit);
 DEFINE_NFS_INODE_EVENT(nfs_access_enter);
 DEFINE_NFS_INODE_EVENT_DONE(nfs_set_cache_invalid);
 
+DEFINE_NFS_INODE_EVENT(nfs_ioctl_file_statx_get_enter);
+DEFINE_NFS_INODE_EVENT_DONE(nfs_ioctl_file_statx_get_exit);
+DEFINE_NFS_INODE_EVENT(nfs_ioctl_file_statx_set_enter);
+DEFINE_NFS_INODE_EVENT_DONE(nfs_ioctl_file_statx_set_exit);
+
 TRACE_EVENT(nfs_access_exit,
 		TP_PROTO(
 			const struct inode *inode,
diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c
index 73dcaa99fa9b..8fd96d93630a 100644
--- a/fs/nfs/proc.c
+++ b/fs/nfs/proc.c
@@ -717,6 +717,7 @@ const struct nfs_rpc_ops nfs_v2_clientops = {
 	.dir_inode_ops	= &nfs_dir_inode_operations,
 	.file_inode_ops	= &nfs_file_inode_operations,
 	.file_ops	= &nfs_file_operations,
+	.dir_ops	= &nfs_dir_operations,
 	.getroot	= nfs_proc_get_root,
 	.submount	= nfs_submount,
 	.try_get_tree	= nfs_try_get_tree,
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 058fc11338d9..0c3a5859f7f3 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -501,6 +501,7 @@ extern __be32 root_nfs_parse_addr(char *name); /*__init*/
 extern const struct file_operations nfs_file_operations;
 #if IS_ENABLED(CONFIG_NFS_V4)
 extern const struct file_operations nfs4_file_operations;
+extern const struct file_operations nfs4_dir_operations;
 #endif /* CONFIG_NFS_V4 */
 extern const struct address_space_operations nfs_file_aops;
 extern const struct address_space_operations nfs_dir_aops;
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 0d5b11c1bfec..9ce61f680a13 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -812,6 +812,7 @@ struct nfs_createargs {
 	struct iattr *		sattr;
 };
 
+struct nfs4_statx;
 struct nfs_setattrargs {
 	struct nfs4_sequence_args 	seq_args;
 	struct nfs_fh *                 fh;
@@ -820,6 +821,7 @@ struct nfs_setattrargs {
 	const struct nfs_server *	server; /* Needed for name mapping */
 	const u32 *			bitmask;
 	const struct nfs4_label		*label;
+	const struct nfs4_statx		*statx;
 };
 
 struct nfs_setaclargs {
@@ -1744,6 +1746,7 @@ struct nfs_rpc_ops {
 	const struct inode_operations *dir_inode_ops;
 	const struct inode_operations *file_inode_ops;
 	const struct file_operations *file_ops;
+	const struct file_operations *dir_ops;
 	const struct nlmclnt_operations *nlmclnt_ops;
 
 	int	(*getroot) (struct nfs_server *, struct nfs_fh *,
diff --git a/include/uapi/linux/nfs.h b/include/uapi/linux/nfs.h
index 946cb62d64b0..df87da39bc43 100644
--- a/include/uapi/linux/nfs.h
+++ b/include/uapi/linux/nfs.h
@@ -9,6 +9,8 @@
 #define _UAPI_LINUX_NFS_H
 
 #include <linux/types.h>
+#include <asm/byteorder.h>
+#include <linux/time.h>
 
 #define NFS_PROGRAM	100003
 #define NFS_PORT	2049
@@ -35,6 +37,94 @@
 
 #define NFS_PIPE_DIRNAME "nfs"
 
+/* NFS ioctls */
+#define NFS_IOC_FILE_STATX_GET	_IOR('N', 2, struct nfs_ioctl_nfs4_statx)
+#define NFS_IOC_FILE_STATX_SET	_IOW('N', 3, struct nfs_ioctl_nfs4_statx)
+
+/* Options for struct nfs_ioctl_nfs4_statx */
+#define NFS_FA_OPTIONS_SYNC_AS_STAT			0x0000
+#define NFS_FA_OPTIONS_FORCE_SYNC			0x2000 /* See statx */
+#define NFS_FA_OPTIONS_DONT_SYNC			0x4000 /* See statx */
+
+#define NFS_FA_VALID_TIME_CREATE			0x00001UL
+#define NFS_FA_VALID_TIME_BACKUP			0x00002UL
+#define NFS_FA_VALID_ARCHIVE				0x00004UL
+#define NFS_FA_VALID_HIDDEN				0x00008UL
+#define NFS_FA_VALID_SYSTEM				0x00010UL
+#define NFS_FA_VALID_OWNER				0x00020UL
+#define NFS_FA_VALID_OWNER_GROUP			0x00040UL
+#define NFS_FA_VALID_ATIME				0x00080UL
+#define NFS_FA_VALID_MTIME				0x00100UL
+#define NFS_FA_VALID_CTIME				0x00200UL
+#define NFS_FA_VALID_OFFLINE				0x00400UL
+#define NFS_FA_VALID_MODE				0x00800UL
+#define NFS_FA_VALID_NLINK				0x01000UL
+#define NFS_FA_VALID_BLKSIZE				0x02000UL
+#define NFS_FA_VALID_INO				0x04000UL
+#define NFS_FA_VALID_DEV				0x08000UL
+#define NFS_FA_VALID_RDEV				0x10000UL
+#define NFS_FA_VALID_SIZE				0x20000UL
+#define NFS_FA_VALID_BLOCKS				0x40000UL
+
+#define NFS_FA_VALID_ALL_ATTR_0 ( NFS_FA_VALID_TIME_CREATE | \
+		NFS_FA_VALID_TIME_BACKUP | \
+		NFS_FA_VALID_ARCHIVE | \
+		NFS_FA_VALID_HIDDEN | \
+		NFS_FA_VALID_SYSTEM | \
+		NFS_FA_VALID_OWNER | \
+		NFS_FA_VALID_OWNER_GROUP | \
+		NFS_FA_VALID_ATIME | \
+		NFS_FA_VALID_MTIME | \
+		NFS_FA_VALID_CTIME | \
+		NFS_FA_VALID_OFFLINE | \
+		NFS_FA_VALID_MODE | \
+		NFS_FA_VALID_NLINK | \
+		NFS_FA_VALID_BLKSIZE | \
+		NFS_FA_VALID_INO | \
+		NFS_FA_VALID_DEV | \
+		NFS_FA_VALID_RDEV | \
+		NFS_FA_VALID_SIZE | \
+		NFS_FA_VALID_BLOCKS)
+
+#define NFS_FA_FLAG_ARCHIVE				(1UL << 0)
+#define NFS_FA_FLAG_HIDDEN				(1UL << 1)
+#define NFS_FA_FLAG_SYSTEM				(1UL << 2)
+#define NFS_FA_FLAG_OFFLINE				(1UL << 3)
+
+struct nfs_ioctl_timespec {
+	__s64		tv_sec;
+	__s64		tv_nsec;
+};
+
+struct nfs_ioctl_nfs4_statx {
+	__s32		real_fd;		/* real FD to use,
+						   -1 means use current file */
+	__u32		fa_options;
+
+	__u64		fa_request[2];		/* Attributes to retrieve */
+	__u64		fa_valid[2];		/* Attributes set */
+
+	struct nfs_ioctl_timespec fa_time_backup;/* Backup time */
+	struct nfs_ioctl_timespec fa_btime;     /* Birth time */
+	__u64		fa_flags;		/* Flag attributes */
+	/* Ordinary attributes follow */
+	struct nfs_ioctl_timespec fa_atime;	/* Access time */
+	struct nfs_ioctl_timespec fa_mtime;	/* Modify time */
+	struct nfs_ioctl_timespec fa_ctime;	/* Change time */
+	__u32		fa_owner_uid;		/* Owner User ID */
+	__u32		fa_group_gid;		/* Primary Group ID */
+	__u32		fa_mode;		/* Mode */
+	__u32	 	fa_nlink;
+	__u32		fa_blksize;
+	__u32		fa_spare;		/* Alignment */
+	__u64		fa_ino;
+	__u32		fa_dev;
+	__u32		fa_rdev;
+	__s64		fa_size;
+	__s64		fa_blocks;
+	__u64 		fa_padding[4];
+};
+
 /*
  * NFS stats. The good thing with these values is that NFSv3 errors are
  * a superset of NFSv2 errors (with the exception of NFSERR_WFLUSH which
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH 8/8] NFSv4: Add an ioctl to allow retrieval of the NFS raw ACCESS mask
  2021-12-17 20:48             ` [PATCH 7/8] NFS: Support statx_get and statx_set ioctls trondmy
@ 2021-12-17 20:48               ` trondmy
  2021-12-21 22:20                 ` Anna Schumaker
  2022-01-03 20:52               ` [PATCH 7/8] NFS: Support statx_get and statx_set ioctls J. Bruce Fields
  1 sibling, 1 reply; 30+ messages in thread
From: trondmy @ 2021-12-17 20:48 UTC (permalink / raw)
  To: Anna Schumaker; +Cc: linux-nfs

From: Trond Myklebust <trond.myklebust@hammerspace.com>

Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
---
 fs/nfs/dir.c             | 47 +++++++++++++++++++++++++---------------
 fs/nfs/internal.h        |  2 ++
 fs/nfs/nfs4file.c        | 39 +++++++++++++++++++++++++++++++++
 include/uapi/linux/nfs.h | 11 ++++++++++
 4 files changed, 81 insertions(+), 18 deletions(-)

diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index f6fc60822153..2cbff76d36de 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -2863,37 +2863,33 @@ void nfs_access_set_mask(struct nfs_access_entry *entry, u32 access_result)
 }
 EXPORT_SYMBOL_GPL(nfs_access_set_mask);
 
-static int nfs_do_access(struct inode *inode, const struct cred *cred, int mask)
+int nfs_get_access(struct inode *inode, const struct cred *cred,
+		   struct nfs_access_entry *cache, bool may_block)
 {
-	struct nfs_access_entry cache;
-	bool may_block = (mask & MAY_NOT_BLOCK) == 0;
-	int cache_mask = -1;
 	int status;
 
 	trace_nfs_access_enter(inode);
 
-	status = nfs_access_get_cached(inode, cred, &cache, may_block);
+	status = nfs_access_get_cached(inode, cred, cache, may_block);
 	if (status == 0)
-		goto out_cached;
+		return 0;
 
-	status = -ECHILD;
 	if (!may_block)
-		goto out;
-
+		return -ECHILD;
 	/*
 	 * Determine which access bits we want to ask for...
 	 */
-	cache.mask = NFS_ACCESS_READ | NFS_ACCESS_MODIFY | NFS_ACCESS_EXTEND;
+	cache->mask = NFS_ACCESS_READ | NFS_ACCESS_MODIFY | NFS_ACCESS_EXTEND;
 	if (nfs_server_capable(inode, NFS_CAP_XATTR)) {
-		cache.mask |= NFS_ACCESS_XAREAD | NFS_ACCESS_XAWRITE |
+		cache->mask |= NFS_ACCESS_XAREAD | NFS_ACCESS_XAWRITE |
 		    NFS_ACCESS_XALIST;
 	}
 	if (S_ISDIR(inode->i_mode))
-		cache.mask |= NFS_ACCESS_DELETE | NFS_ACCESS_LOOKUP;
+		cache->mask |= NFS_ACCESS_DELETE | NFS_ACCESS_LOOKUP;
 	else
-		cache.mask |= NFS_ACCESS_EXECUTE;
-	cache.cred = cred;
-	status = NFS_PROTO(inode)->access(inode, &cache);
+		cache->mask |= NFS_ACCESS_EXECUTE;
+	cache->cred = cred;
+	status = NFS_PROTO(inode)->access(inode, cache);
 	if (status != 0) {
 		if (status == -ESTALE) {
 			if (!S_ISDIR(inode->i_mode))
@@ -2901,10 +2897,25 @@ static int nfs_do_access(struct inode *inode, const struct cred *cred, int mask)
 			else
 				nfs_zap_caches(inode);
 		}
-		goto out;
+		return status;
 	}
-	nfs_access_add_cache(inode, &cache);
-out_cached:
+	nfs_access_add_cache(inode, cache);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(nfs_get_access);
+
+static int nfs_do_access(struct inode *inode, const struct cred *cred, int mask)
+{
+	struct nfs_access_entry cache;
+	bool may_block = (mask & MAY_NOT_BLOCK) == 0;
+	int cache_mask = -1;
+	int status;
+
+	trace_nfs_access_enter(inode);
+
+	status = nfs_get_access(inode, cred, &cache, may_block);
+	if (status < 0)
+		goto out;
 	cache_mask = nfs_access_calc_mask(cache.mask, inode->i_mode);
 	if ((mask & ~cache_mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) != 0)
 		status = -EACCES;
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index 9602a886f0f0..9b8fd2247533 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -392,6 +392,8 @@ int nfs_mknod(struct user_namespace *, struct inode *, struct dentry *, umode_t,
 	      dev_t);
 int nfs_rename(struct user_namespace *, struct inode *, struct dentry *,
 	       struct inode *, struct dentry *, unsigned int);
+int nfs_get_access(struct inode *inode, const struct cred *cred,
+		   struct nfs_access_entry *cache, bool may_block);
 
 /* file.c */
 int nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync);
diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c
index 494ebc7cd1c0..ccf70d26c5c4 100644
--- a/fs/nfs/nfs4file.c
+++ b/fs/nfs/nfs4file.c
@@ -611,6 +611,42 @@ static long nfs4_ioctl_file_statx_set(struct file *dst_file,
 	return ret;
 }
 
+static long
+nfs4_ioctl_file_access_get(struct file *file,
+			   struct nfs_ioctl_nfs4_access __user *uarg)
+{
+	struct inode *inode = file_inode(file);
+	struct nfs_access_entry cache;
+	__u64 ac_flags;
+	const struct cred *old_cred;
+	struct cred *override_cred;
+	long ret;
+
+	if (!NFS_PROTO(inode)->access)
+		return -ENOTSUPP;
+
+	if (get_user(ac_flags, &uarg->ac_flags))
+		return -EFAULT;
+
+	override_cred = prepare_creds();
+	if (!override_cred)
+		return -ENOMEM;
+
+	if (!(ac_flags & NFS_AC_FLAG_EACCESS)) {
+		override_cred->fsuid = override_cred->uid;
+		override_cred->fsgid = override_cred->gid;
+	}
+	old_cred = override_creds(override_cred);
+
+	ret = nfs_get_access(inode, override_cred, &cache, true);
+	if (!ret && unlikely(put_user(cache.mask, &uarg->ac_mask) != 0))
+		ret = -EFAULT;
+
+	revert_creds(old_cred);
+	put_cred(override_cred);
+	return ret;
+}
+
 static long nfs4_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
 	void __user *argp = (void __user *)arg;
@@ -623,6 +659,9 @@ static long nfs4_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 	case NFS_IOC_FILE_STATX_SET:
 		ret = nfs4_ioctl_file_statx_set(file, argp);
 		break;
+	case NFS_IOC_FILE_ACCESS_GET:
+		ret = nfs4_ioctl_file_access_get(file, argp);
+		break;
 	default:
 		ret = -ENOIOCTLCMD;
 	}
diff --git a/include/uapi/linux/nfs.h b/include/uapi/linux/nfs.h
index df87da39bc43..b1e50f14db18 100644
--- a/include/uapi/linux/nfs.h
+++ b/include/uapi/linux/nfs.h
@@ -41,6 +41,8 @@
 #define NFS_IOC_FILE_STATX_GET	_IOR('N', 2, struct nfs_ioctl_nfs4_statx)
 #define NFS_IOC_FILE_STATX_SET	_IOW('N', 3, struct nfs_ioctl_nfs4_statx)
 
+#define NFS_IOC_FILE_ACCESS_GET	_IOR('N', 4, struct nfs_ioctl_nfs4_access)
+
 /* Options for struct nfs_ioctl_nfs4_statx */
 #define NFS_FA_OPTIONS_SYNC_AS_STAT			0x0000
 #define NFS_FA_OPTIONS_FORCE_SYNC			0x2000 /* See statx */
@@ -125,6 +127,15 @@ struct nfs_ioctl_nfs4_statx {
 	__u64 		fa_padding[4];
 };
 
+struct nfs_ioctl_nfs4_access {
+	/* input */
+	__u64		ac_flags;		/* operation flags */
+	/* output */
+	__u64		ac_mask;		/* NFS raw ACCESS reply mask */
+};
+
+#define NFS_AC_FLAG_EACCESS (1UL << 0)
+
 /*
  * NFS stats. The good thing with these values is that NFSv3 errors are
  * a superset of NFSv2 errors (with the exception of NFSERR_WFLUSH which
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* Re: [PATCH 8/8] NFSv4: Add an ioctl to allow retrieval of the NFS raw ACCESS mask
  2021-12-17 20:48               ` [PATCH 8/8] NFSv4: Add an ioctl to allow retrieval of the NFS raw ACCESS mask trondmy
@ 2021-12-21 22:20                 ` Anna Schumaker
  0 siblings, 0 replies; 30+ messages in thread
From: Anna Schumaker @ 2021-12-21 22:20 UTC (permalink / raw)
  To: trondmy; +Cc: Linux NFS Mailing List

Hi Trond,

On Fri, Dec 17, 2021 at 5:03 PM <trondmy@kernel.org> wrote:
>
> From: Trond Myklebust <trond.myklebust@hammerspace.com>

This patch conflicts with Neil Brown's cred cleanups. I did my best to
get it to apply, but then it didn't compile. Can you please rebase and
test on top of those patches, since they're already included in my
linux-next  branch?

Thanks,
Anna

>
> Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
> ---
>  fs/nfs/dir.c             | 47 +++++++++++++++++++++++++---------------
>  fs/nfs/internal.h        |  2 ++
>  fs/nfs/nfs4file.c        | 39 +++++++++++++++++++++++++++++++++
>  include/uapi/linux/nfs.h | 11 ++++++++++
>  4 files changed, 81 insertions(+), 18 deletions(-)
>
> diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
> index f6fc60822153..2cbff76d36de 100644
> --- a/fs/nfs/dir.c
> +++ b/fs/nfs/dir.c
> @@ -2863,37 +2863,33 @@ void nfs_access_set_mask(struct nfs_access_entry *entry, u32 access_result)
>  }
>  EXPORT_SYMBOL_GPL(nfs_access_set_mask);
>
> -static int nfs_do_access(struct inode *inode, const struct cred *cred, int mask)
> +int nfs_get_access(struct inode *inode, const struct cred *cred,
> +                  struct nfs_access_entry *cache, bool may_block)
>  {
> -       struct nfs_access_entry cache;
> -       bool may_block = (mask & MAY_NOT_BLOCK) == 0;
> -       int cache_mask = -1;
>         int status;
>
>         trace_nfs_access_enter(inode);
>
> -       status = nfs_access_get_cached(inode, cred, &cache, may_block);
> +       status = nfs_access_get_cached(inode, cred, cache, may_block);
>         if (status == 0)
> -               goto out_cached;
> +               return 0;
>
> -       status = -ECHILD;
>         if (!may_block)
> -               goto out;
> -
> +               return -ECHILD;
>         /*
>          * Determine which access bits we want to ask for...
>          */
> -       cache.mask = NFS_ACCESS_READ | NFS_ACCESS_MODIFY | NFS_ACCESS_EXTEND;
> +       cache->mask = NFS_ACCESS_READ | NFS_ACCESS_MODIFY | NFS_ACCESS_EXTEND;
>         if (nfs_server_capable(inode, NFS_CAP_XATTR)) {
> -               cache.mask |= NFS_ACCESS_XAREAD | NFS_ACCESS_XAWRITE |
> +               cache->mask |= NFS_ACCESS_XAREAD | NFS_ACCESS_XAWRITE |
>                     NFS_ACCESS_XALIST;
>         }
>         if (S_ISDIR(inode->i_mode))
> -               cache.mask |= NFS_ACCESS_DELETE | NFS_ACCESS_LOOKUP;
> +               cache->mask |= NFS_ACCESS_DELETE | NFS_ACCESS_LOOKUP;
>         else
> -               cache.mask |= NFS_ACCESS_EXECUTE;
> -       cache.cred = cred;
> -       status = NFS_PROTO(inode)->access(inode, &cache);
> +               cache->mask |= NFS_ACCESS_EXECUTE;
> +       cache->cred = cred;
> +       status = NFS_PROTO(inode)->access(inode, cache);
>         if (status != 0) {
>                 if (status == -ESTALE) {
>                         if (!S_ISDIR(inode->i_mode))
> @@ -2901,10 +2897,25 @@ static int nfs_do_access(struct inode *inode, const struct cred *cred, int mask)
>                         else
>                                 nfs_zap_caches(inode);
>                 }
> -               goto out;
> +               return status;
>         }
> -       nfs_access_add_cache(inode, &cache);
> -out_cached:
> +       nfs_access_add_cache(inode, cache);
> +       return 0;
> +}
> +EXPORT_SYMBOL_GPL(nfs_get_access);
> +
> +static int nfs_do_access(struct inode *inode, const struct cred *cred, int mask)
> +{
> +       struct nfs_access_entry cache;
> +       bool may_block = (mask & MAY_NOT_BLOCK) == 0;
> +       int cache_mask = -1;
> +       int status;
> +
> +       trace_nfs_access_enter(inode);
> +
> +       status = nfs_get_access(inode, cred, &cache, may_block);
> +       if (status < 0)
> +               goto out;
>         cache_mask = nfs_access_calc_mask(cache.mask, inode->i_mode);
>         if ((mask & ~cache_mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) != 0)
>                 status = -EACCES;
> diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
> index 9602a886f0f0..9b8fd2247533 100644
> --- a/fs/nfs/internal.h
> +++ b/fs/nfs/internal.h
> @@ -392,6 +392,8 @@ int nfs_mknod(struct user_namespace *, struct inode *, struct dentry *, umode_t,
>               dev_t);
>  int nfs_rename(struct user_namespace *, struct inode *, struct dentry *,
>                struct inode *, struct dentry *, unsigned int);
> +int nfs_get_access(struct inode *inode, const struct cred *cred,
> +                  struct nfs_access_entry *cache, bool may_block);
>
>  /* file.c */
>  int nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync);
> diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c
> index 494ebc7cd1c0..ccf70d26c5c4 100644
> --- a/fs/nfs/nfs4file.c
> +++ b/fs/nfs/nfs4file.c
> @@ -611,6 +611,42 @@ static long nfs4_ioctl_file_statx_set(struct file *dst_file,
>         return ret;
>  }
>
> +static long
> +nfs4_ioctl_file_access_get(struct file *file,
> +                          struct nfs_ioctl_nfs4_access __user *uarg)
> +{
> +       struct inode *inode = file_inode(file);
> +       struct nfs_access_entry cache;
> +       __u64 ac_flags;
> +       const struct cred *old_cred;
> +       struct cred *override_cred;
> +       long ret;
> +
> +       if (!NFS_PROTO(inode)->access)
> +               return -ENOTSUPP;
> +
> +       if (get_user(ac_flags, &uarg->ac_flags))
> +               return -EFAULT;
> +
> +       override_cred = prepare_creds();
> +       if (!override_cred)
> +               return -ENOMEM;
> +
> +       if (!(ac_flags & NFS_AC_FLAG_EACCESS)) {
> +               override_cred->fsuid = override_cred->uid;
> +               override_cred->fsgid = override_cred->gid;
> +       }
> +       old_cred = override_creds(override_cred);
> +
> +       ret = nfs_get_access(inode, override_cred, &cache, true);
> +       if (!ret && unlikely(put_user(cache.mask, &uarg->ac_mask) != 0))
> +               ret = -EFAULT;
> +
> +       revert_creds(old_cred);
> +       put_cred(override_cred);
> +       return ret;
> +}
> +
>  static long nfs4_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
>  {
>         void __user *argp = (void __user *)arg;
> @@ -623,6 +659,9 @@ static long nfs4_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
>         case NFS_IOC_FILE_STATX_SET:
>                 ret = nfs4_ioctl_file_statx_set(file, argp);
>                 break;
> +       case NFS_IOC_FILE_ACCESS_GET:
> +               ret = nfs4_ioctl_file_access_get(file, argp);
> +               break;
>         default:
>                 ret = -ENOIOCTLCMD;
>         }
> diff --git a/include/uapi/linux/nfs.h b/include/uapi/linux/nfs.h
> index df87da39bc43..b1e50f14db18 100644
> --- a/include/uapi/linux/nfs.h
> +++ b/include/uapi/linux/nfs.h
> @@ -41,6 +41,8 @@
>  #define NFS_IOC_FILE_STATX_GET _IOR('N', 2, struct nfs_ioctl_nfs4_statx)
>  #define NFS_IOC_FILE_STATX_SET _IOW('N', 3, struct nfs_ioctl_nfs4_statx)
>
> +#define NFS_IOC_FILE_ACCESS_GET        _IOR('N', 4, struct nfs_ioctl_nfs4_access)
> +
>  /* Options for struct nfs_ioctl_nfs4_statx */
>  #define NFS_FA_OPTIONS_SYNC_AS_STAT                    0x0000
>  #define NFS_FA_OPTIONS_FORCE_SYNC                      0x2000 /* See statx */
> @@ -125,6 +127,15 @@ struct nfs_ioctl_nfs4_statx {
>         __u64           fa_padding[4];
>  };
>
> +struct nfs_ioctl_nfs4_access {
> +       /* input */
> +       __u64           ac_flags;               /* operation flags */
> +       /* output */
> +       __u64           ac_mask;                /* NFS raw ACCESS reply mask */
> +};
> +
> +#define NFS_AC_FLAG_EACCESS (1UL << 0)
> +
>  /*
>   * NFS stats. The good thing with these values is that NFSv3 errors are
>   * a superset of NFSv2 errors (with the exception of NFSERR_WFLUSH which
> --
> 2.33.1
>

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH 0/8] Support btime and other NFSv4 specific attributes
  2021-12-17 20:48 [PATCH 0/8] Support btime and other NFSv4 specific attributes trondmy
  2021-12-17 20:48 ` [PATCH 1/8] NFS: Expand the type of nfs_fattr->valid trondmy
@ 2022-01-03 20:51 ` J. Bruce Fields
  2022-01-03 20:51   ` Trond Myklebust
  2022-01-06 18:47   ` Olga Kornievskaia
  1 sibling, 2 replies; 30+ messages in thread
From: J. Bruce Fields @ 2022-01-03 20:51 UTC (permalink / raw)
  To: trondmy; +Cc: Anna Schumaker, linux-nfs

On Fri, Dec 17, 2021 at 03:48:46PM -0500, trondmy@kernel.org wrote:
> From: Trond Myklebust <trond.myklebust@hammerspace.com>
> 
> NFSv4 has support for a number of extra attributes that are of interest
> to Samba when it is used to re-export a filesystem to Windows clients.
> Aside from the btime, which is of interest in statx(), Windows clients
> have an interest in determining the status of the 'hidden', and 'system'
> flags.
> Backup programs want to read the 'archive' flags and the 'time backup'
> attribute.
> Finally, the 'offline' flag can tell whether or not a file needs to be
> staged by an HSM system before it can be read or written to.
> 
> The patch series also adds an ioctl() to allow userspace retrieval and
> setting of these attributes where appropriate. It also adds an ioctl()
> to allow retrieval of the raw NFSv4 ACCESS information, to allow more
> fine grained determination of the user's access rights to a file or
> directory. All of this information is of use for Samba.

Same question, what filesystem and server are you testing against?

--b.

> 
> Anne Marie Merritt (3):
>   nfs: Add timecreate to nfs inode
>   nfs: Add 'archive', 'hidden' and 'system' fields to nfs inode
>   nfs: Add 'time backup' to nfs inode
> 
> Richard Sharpe (1):
>   NFS: Support statx_get and statx_set ioctls
> 
> Trond Myklebust (4):
>   NFS: Expand the type of nfs_fattr->valid
>   NFS: Return the file btime in the statx results when appropriate
>   NFSv4: Support the offline bit
>   NFSv4: Add an ioctl to allow retrieval of the NFS raw ACCESS mask
> 
>  fs/nfs/dir.c              |  71 ++---
>  fs/nfs/getroot.c          |   3 +-
>  fs/nfs/inode.c            | 147 +++++++++-
>  fs/nfs/internal.h         |  10 +
>  fs/nfs/nfs3proc.c         |   1 +
>  fs/nfs/nfs4_fs.h          |  31 +++
>  fs/nfs/nfs4file.c         | 550 ++++++++++++++++++++++++++++++++++++++
>  fs/nfs/nfs4proc.c         | 175 +++++++++++-
>  fs/nfs/nfs4trace.h        |   8 +-
>  fs/nfs/nfs4xdr.c          | 240 +++++++++++++++--
>  fs/nfs/nfstrace.c         |   5 +
>  fs/nfs/nfstrace.h         |   9 +-
>  fs/nfs/proc.c             |   1 +
>  include/linux/nfs4.h      |   1 +
>  include/linux/nfs_fs.h    |  15 ++
>  include/linux/nfs_fs_sb.h |   2 +-
>  include/linux/nfs_xdr.h   |  80 ++++--
>  include/uapi/linux/nfs.h  | 101 +++++++
>  18 files changed, 1356 insertions(+), 94 deletions(-)
> 
> -- 
> 2.33.1

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH 0/8] Support btime and other NFSv4 specific attributes
  2022-01-03 20:51 ` [PATCH 0/8] Support btime and other NFSv4 specific attributes J. Bruce Fields
@ 2022-01-03 20:51   ` Trond Myklebust
  2022-01-05 15:05     ` Ondrej Valousek
  2022-01-06 18:47   ` Olga Kornievskaia
  1 sibling, 1 reply; 30+ messages in thread
From: Trond Myklebust @ 2022-01-03 20:51 UTC (permalink / raw)
  To: bfields, trondmy; +Cc: linux-nfs, anna.schumaker

On Mon, 2022-01-03 at 15:51 -0500, J. Bruce Fields wrote:
> On Fri, Dec 17, 2021 at 03:48:46PM -0500, trondmy@kernel.org wrote:
> > From: Trond Myklebust <trond.myklebust@hammerspace.com>
> > 
> > NFSv4 has support for a number of extra attributes that are of
> > interest
> > to Samba when it is used to re-export a filesystem to Windows
> > clients.
> > Aside from the btime, which is of interest in statx(), Windows
> > clients
> > have an interest in determining the status of the 'hidden', and
> > 'system'
> > flags.
> > Backup programs want to read the 'archive' flags and the 'time
> > backup'
> > attribute.
> > Finally, the 'offline' flag can tell whether or not a file needs to
> > be
> > staged by an HSM system before it can be read or written to.
> > 
> > The patch series also adds an ioctl() to allow userspace retrieval
> > and
> > setting of these attributes where appropriate. It also adds an
> > ioctl()
> > to allow retrieval of the raw NFSv4 ACCESS information, to allow
> > more
> > fine grained determination of the user's access rights to a file or
> > directory. All of this information is of use for Samba.
> 
> Same question, what filesystem and server are you testing against?
> 

Same answer.

> --b.
> 
> > 
> > Anne Marie Merritt (3):
> >   nfs: Add timecreate to nfs inode
> >   nfs: Add 'archive', 'hidden' and 'system' fields to nfs inode
> >   nfs: Add 'time backup' to nfs inode
> > 
> > Richard Sharpe (1):
> >   NFS: Support statx_get and statx_set ioctls
> > 
> > Trond Myklebust (4):
> >   NFS: Expand the type of nfs_fattr->valid
> >   NFS: Return the file btime in the statx results when appropriate
> >   NFSv4: Support the offline bit
> >   NFSv4: Add an ioctl to allow retrieval of the NFS raw ACCESS mask
> > 
> >  fs/nfs/dir.c              |  71 ++---
> >  fs/nfs/getroot.c          |   3 +-
> >  fs/nfs/inode.c            | 147 +++++++++-
> >  fs/nfs/internal.h         |  10 +
> >  fs/nfs/nfs3proc.c         |   1 +
> >  fs/nfs/nfs4_fs.h          |  31 +++
> >  fs/nfs/nfs4file.c         | 550
> > ++++++++++++++++++++++++++++++++++++++
> >  fs/nfs/nfs4proc.c         | 175 +++++++++++-
> >  fs/nfs/nfs4trace.h        |   8 +-
> >  fs/nfs/nfs4xdr.c          | 240 +++++++++++++++--
> >  fs/nfs/nfstrace.c         |   5 +
> >  fs/nfs/nfstrace.h         |   9 +-
> >  fs/nfs/proc.c             |   1 +
> >  include/linux/nfs4.h      |   1 +
> >  include/linux/nfs_fs.h    |  15 ++
> >  include/linux/nfs_fs_sb.h |   2 +-
> >  include/linux/nfs_xdr.h   |  80 ++++--
> >  include/uapi/linux/nfs.h  | 101 +++++++
> >  18 files changed, 1356 insertions(+), 94 deletions(-)
> > 
> > -- 
> > 2.33.1

-- 
Trond Myklebust
Linux NFS client maintainer, Hammerspace
trond.myklebust@hammerspace.com



^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH 7/8] NFS: Support statx_get and statx_set ioctls
  2021-12-17 20:48             ` [PATCH 7/8] NFS: Support statx_get and statx_set ioctls trondmy
  2021-12-17 20:48               ` [PATCH 8/8] NFSv4: Add an ioctl to allow retrieval of the NFS raw ACCESS mask trondmy
@ 2022-01-03 20:52               ` J. Bruce Fields
  2022-01-03 20:56                 ` Trond Myklebust
  1 sibling, 1 reply; 30+ messages in thread
From: J. Bruce Fields @ 2022-01-03 20:52 UTC (permalink / raw)
  To: trondmy; +Cc: Anna Schumaker, linux-nfs

On Fri, Dec 17, 2021 at 03:48:53PM -0500, trondmy@kernel.org wrote:
> From: Richard Sharpe <richard.sharpe@primarydata.com>
> 
> Add support for returning all of the Windows attributes with a statx
> ioctl.

I suppose I'm just woodshedding, but "statx ioctl" is a little
confusing--it doesn't have any actual connection with the statx
system call, right?

But, why not add this to statx?

--b.

> Add support for setting all of the Windows attributes using an ioctl.
> 
> Signed-off-by: Richard Sharpe <richard.sharpe@primarydata.com>
> Signed-off-by: Lance Shelton <lance.shelton@hammerspace.com>
> Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
> ---
>  fs/nfs/dir.c             |  24 +-
>  fs/nfs/getroot.c         |   3 +-
>  fs/nfs/inode.c           |  41 +++-
>  fs/nfs/internal.h        |   8 +
>  fs/nfs/nfs3proc.c        |   1 +
>  fs/nfs/nfs4_fs.h         |  31 +++
>  fs/nfs/nfs4file.c        | 511 +++++++++++++++++++++++++++++++++++++++
>  fs/nfs/nfs4proc.c        | 124 ++++++++++
>  fs/nfs/nfs4xdr.c         |  63 ++++-
>  fs/nfs/nfstrace.c        |   5 +
>  fs/nfs/nfstrace.h        |   5 +
>  fs/nfs/proc.c            |   1 +
>  include/linux/nfs_fs.h   |   1 +
>  include/linux/nfs_xdr.h  |   3 +
>  include/uapi/linux/nfs.h |  90 +++++++
>  15 files changed, 887 insertions(+), 24 deletions(-)
> 
> diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
> index 731d31015b6a..f6fc60822153 100644
> --- a/fs/nfs/dir.c
> +++ b/fs/nfs/dir.c
> @@ -48,11 +48,6 @@
>  
>  /* #define NFS_DEBUG_VERBOSE 1 */
>  
> -static int nfs_opendir(struct inode *, struct file *);
> -static int nfs_closedir(struct inode *, struct file *);
> -static int nfs_readdir(struct file *, struct dir_context *);
> -static int nfs_fsync_dir(struct file *, loff_t, loff_t, int);
> -static loff_t nfs_llseek_dir(struct file *, loff_t, int);
>  static void nfs_readdir_clear_array(struct page*);
>  
>  const struct file_operations nfs_dir_operations = {
> @@ -63,6 +58,7 @@ const struct file_operations nfs_dir_operations = {
>  	.release	= nfs_closedir,
>  	.fsync		= nfs_fsync_dir,
>  };
> +EXPORT_SYMBOL_GPL(nfs_dir_operations);
>  
>  const struct address_space_operations nfs_dir_aops = {
>  	.freepage = nfs_readdir_clear_array,
> @@ -104,8 +100,7 @@ static void put_nfs_open_dir_context(struct inode *dir, struct nfs_open_dir_cont
>  /*
>   * Open file
>   */
> -static int
> -nfs_opendir(struct inode *inode, struct file *filp)
> +int nfs_opendir(struct inode *inode, struct file *filp)
>  {
>  	int res = 0;
>  	struct nfs_open_dir_context *ctx;
> @@ -123,13 +118,14 @@ nfs_opendir(struct inode *inode, struct file *filp)
>  out:
>  	return res;
>  }
> +EXPORT_SYMBOL_GPL(nfs_opendir);
>  
> -static int
> -nfs_closedir(struct inode *inode, struct file *filp)
> +int nfs_closedir(struct inode *inode, struct file *filp)
>  {
>  	put_nfs_open_dir_context(file_inode(filp), filp->private_data);
>  	return 0;
>  }
> +EXPORT_SYMBOL_GPL(nfs_closedir);
>  
>  struct nfs_cache_array_entry {
>  	u64 cookie;
> @@ -1064,7 +1060,7 @@ static int uncached_readdir(struct nfs_readdir_descriptor *desc)
>     last cookie cache takes care of the common case of reading the
>     whole directory.
>   */
> -static int nfs_readdir(struct file *file, struct dir_context *ctx)
> +int nfs_readdir(struct file *file, struct dir_context *ctx)
>  {
>  	struct dentry	*dentry = file_dentry(file);
>  	struct inode	*inode = d_inode(dentry);
> @@ -1157,8 +1153,9 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx)
>  	dfprintk(FILE, "NFS: readdir(%pD2) returns %d\n", file, res);
>  	return res;
>  }
> +EXPORT_SYMBOL_GPL(nfs_readdir);
>  
> -static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int whence)
> +loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int whence)
>  {
>  	struct nfs_open_dir_context *dir_ctx = filp->private_data;
>  
> @@ -1196,19 +1193,20 @@ static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int whence)
>  	spin_unlock(&filp->f_lock);
>  	return offset;
>  }
> +EXPORT_SYMBOL_GPL(nfs_llseek_dir);
>  
>  /*
>   * All directory operations under NFS are synchronous, so fsync()
>   * is a dummy operation.
>   */
> -static int nfs_fsync_dir(struct file *filp, loff_t start, loff_t end,
> -			 int datasync)
> +int nfs_fsync_dir(struct file *filp, loff_t start, loff_t end, int datasync)
>  {
>  	dfprintk(FILE, "NFS: fsync dir(%pD2) datasync %d\n", filp, datasync);
>  
>  	nfs_inc_stats(file_inode(filp), NFSIOS_VFSFSYNC);
>  	return 0;
>  }
> +EXPORT_SYMBOL_GPL(nfs_fsync_dir);
>  
>  /**
>   * nfs_force_lookup_revalidate - Mark the directory as having changed
> diff --git a/fs/nfs/getroot.c b/fs/nfs/getroot.c
> index 11ff2b2e060f..f872970d6240 100644
> --- a/fs/nfs/getroot.c
> +++ b/fs/nfs/getroot.c
> @@ -127,7 +127,8 @@ int nfs_get_root(struct super_block *s, struct fs_context *fc)
>  	if (server->caps & NFS_CAP_SECURITY_LABEL)
>  		kflags |= SECURITY_LSM_NATIVE_LABELS;
>  	if (ctx->clone_data.sb) {
> -		if (d_inode(fc->root)->i_fop != &nfs_dir_operations) {
> +		if (d_inode(fc->root)->i_fop !=
> +		    server->nfs_client->rpc_ops->dir_ops) {
>  			error = -ESTALE;
>  			goto error_splat_root;
>  		}
> diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
> index 33f4410190b6..8da662a4953d 100644
> --- a/fs/nfs/inode.c
> +++ b/fs/nfs/inode.c
> @@ -108,6 +108,7 @@ u64 nfs_compat_user_ino64(u64 fileid)
>  		ino ^= fileid >> (sizeof(fileid)-sizeof(ino)) * 8;
>  	return ino;
>  }
> +EXPORT_SYMBOL_GPL(nfs_compat_user_ino64);
>  
>  int nfs_drop_inode(struct inode *inode)
>  {
> @@ -501,7 +502,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
>  			nfs_inode_init_regular(nfsi);
>  		} else if (S_ISDIR(inode->i_mode)) {
>  			inode->i_op = NFS_SB(sb)->nfs_client->rpc_ops->dir_inode_ops;
> -			inode->i_fop = &nfs_dir_operations;
> +			inode->i_fop = NFS_SB(sb)->nfs_client->rpc_ops->dir_ops;
>  			inode->i_data.a_ops = &nfs_dir_aops;
>  			nfs_inode_init_dir(nfsi);
>  			/* Deal with crossing mountpoints */
> @@ -867,6 +868,44 @@ static u32 nfs_get_valid_attrmask(struct inode *inode)
>  	return reply_mask;
>  }
>  
> +static int nfs_getattr_revalidate_force(struct dentry *dentry)
> +{
> +	struct inode *inode = d_inode(dentry);
> +	struct nfs_server *server = NFS_SERVER(inode);
> +
> +	if (!(server->flags & NFS_MOUNT_NOAC))
> +		nfs_readdirplus_parent_cache_miss(dentry);
> +	else
> +		nfs_readdirplus_parent_cache_hit(dentry);
> +	return __nfs_revalidate_inode(server, inode);
> +}
> +
> +static int nfs_getattr_revalidate_none(struct dentry *dentry)
> +{
> +	nfs_readdirplus_parent_cache_hit(dentry);
> +	return NFS_STALE(d_inode(dentry)) ? -ESTALE : 0;
> +}
> +
> +static int nfs_getattr_revalidate_maybe(struct dentry *dentry,
> +					unsigned long flags)
> +{
> +	if (nfs_check_cache_invalid(d_inode(dentry), flags))
> +		return nfs_getattr_revalidate_force(dentry);
> +	return nfs_getattr_revalidate_none(dentry);
> +}
> +
> +int nfs_getattr_revalidate(const struct path *path,
> +			   unsigned long flags,
> +			   unsigned int query_flags)
> +{
> +	if (query_flags & AT_STATX_FORCE_SYNC)
> +		return nfs_getattr_revalidate_force(path->dentry);
> +	if (!(query_flags & AT_STATX_DONT_SYNC))
> +		return nfs_getattr_revalidate_maybe(path->dentry, flags);
> +	return nfs_getattr_revalidate_none(path->dentry);
> +}
> +EXPORT_SYMBOL_GPL(nfs_getattr_revalidate);
> +
>  int nfs_getattr(struct user_namespace *mnt_userns, const struct path *path,
>  		struct kstat *stat, u32 request_mask, unsigned int query_flags)
>  {
> diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
> index 12f6acb483bb..9602a886f0f0 100644
> --- a/fs/nfs/internal.h
> +++ b/fs/nfs/internal.h
> @@ -366,6 +366,12 @@ extern struct nfs_client *nfs_init_client(struct nfs_client *clp,
>  			   const struct nfs_client_initdata *);
>  
>  /* dir.c */
> +int nfs_opendir(struct inode *, struct file *);
> +int nfs_closedir(struct inode *, struct file *);
> +int nfs_readdir(struct file *file, struct dir_context *ctx);
> +int nfs_fsync_dir(struct file *, loff_t, loff_t, int);
> +loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int whence);
> +
>  extern void nfs_advise_use_readdirplus(struct inode *dir);
>  extern void nfs_force_use_readdirplus(struct inode *dir);
>  extern unsigned long nfs_access_cache_count(struct shrinker *shrink,
> @@ -411,6 +417,8 @@ extern void nfs_set_cache_invalid(struct inode *inode, unsigned long flags);
>  extern bool nfs_check_cache_invalid(struct inode *, unsigned long);
>  extern int nfs_wait_bit_killable(struct wait_bit_key *key, int mode);
>  extern int nfs_wait_atomic_killable(atomic_t *p, unsigned int mode);
> +extern int nfs_getattr_revalidate(const struct path *path, unsigned long flags,
> +				  unsigned int query_flags);
>  
>  /* super.c */
>  extern const struct super_operations nfs_sops;
> diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c
> index 7100514d306b..091005e169b7 100644
> --- a/fs/nfs/nfs3proc.c
> +++ b/fs/nfs/nfs3proc.c
> @@ -1018,6 +1018,7 @@ const struct nfs_rpc_ops nfs_v3_clientops = {
>  	.dir_inode_ops	= &nfs3_dir_inode_operations,
>  	.file_inode_ops	= &nfs3_file_inode_operations,
>  	.file_ops	= &nfs_file_operations,
> +	.dir_ops	= &nfs_dir_operations,
>  	.nlmclnt_ops	= &nlmclnt_fl_close_lock_ops,
>  	.getroot	= nfs3_proc_get_root,
>  	.submount	= nfs_submount,
> diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
> index ed5eaca6801e..9f21d8520e99 100644
> --- a/fs/nfs/nfs4_fs.h
> +++ b/fs/nfs/nfs4_fs.h
> @@ -248,6 +248,34 @@ struct nfs4_opendata {
>  	int rpc_status;
>  };
>  
> +struct nfs4_statx {
> +	int		real_fd;		/* real FD to use,
> +						   -1 means use current file */
> +	__u32		fa_options;		/* statx flags */
> +	__u64		fa_request[2];		/* Attributes requested */
> +	__u64		fa_valid[2];		/* Attributes set */
> +
> +	struct timespec64 fa_time_backup;	/* Backup time */
> +	struct timespec64 fa_btime;		/* Birth time */
> +	/* Flag attributes */
> +	__u64 fa_flags;
> +	struct timespec64 fa_atime;		/* Access time */
> +	struct timespec64 fa_mtime;		/* Modify time */
> +	struct timespec64 fa_ctime;		/* Change time */
> +	kuid_t		fa_owner_uid;		/* Owner User ID */
> +	kgid_t		fa_group_gid;		/* Primary Group ID */
> +        /* Normal stat fields after this */
> +	__u32	 	fa_mode;		/* Mode */
> +	unsigned int 	fa_nlink;
> +	__u32		fa_blksize;
> +	__u32		fa_spare;		/* Alignment */
> +	__u64		fa_ino;
> +	dev_t		fa_dev;
> +	dev_t		fa_rdev;
> +	loff_t		fa_size;
> +	__u64		fa_blocks;
> +};
> +
>  struct nfs4_add_xprt_data {
>  	struct nfs_client	*clp;
>  	const struct cred	*cred;
> @@ -315,6 +343,9 @@ extern int nfs4_set_rw_stateid(nfs4_stateid *stateid,
>  		const struct nfs_open_context *ctx,
>  		const struct nfs_lock_context *l_ctx,
>  		fmode_t fmode);
> +int nfs4_set_nfs4_statx(struct inode *inode,
> +		struct nfs4_statx *statx,
> +		struct nfs_fattr *fattr);
>  extern int nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
>  			     struct nfs_fattr *fattr, struct inode *inode);
>  extern int update_open_stateid(struct nfs4_state *state,
> diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c
> index e79ae4cbc395..494ebc7cd1c0 100644
> --- a/fs/nfs/nfs4file.c
> +++ b/fs/nfs/nfs4file.c
> @@ -9,6 +9,8 @@
>  #include <linux/falloc.h>
>  #include <linux/mount.h>
>  #include <linux/nfs_fs.h>
> +#include <linux/time32.h>
> +#include <linux/compat.h>
>  #include <linux/nfs_ssc.h>
>  #include "delegation.h"
>  #include "internal.h"
> @@ -132,6 +134,503 @@ nfs4_file_flush(struct file *file, fl_owner_t id)
>  	return filemap_check_wb_err(file->f_mapping, since);
>  }
>  
> +static int nfs_get_timespec64(struct timespec64 *ts,
> +			      const struct nfs_ioctl_timespec __user *uts)
> +{
> +	__s64 dummy;
> +	if (unlikely(get_user(dummy, &uts->tv_sec) != 0))
> +		return EFAULT;
> +	ts->tv_sec = dummy;
> +	if (unlikely(get_user(dummy, &uts->tv_nsec) != 0))
> +		return EFAULT;
> +	ts->tv_nsec = dummy;
> +	return 0;
> +}
> +
> +static int nfs_put_timespec64(const struct timespec64 *ts,
> +			      struct nfs_ioctl_timespec __user *uts)
> +{
> +	__s64 dummy;
> +
> +	dummy = ts->tv_sec;
> +	if (unlikely(put_user(dummy, &uts->tv_sec) != 0))
> +		return EFAULT;
> +	dummy = ts->tv_nsec;
> +	if (unlikely(put_user(dummy, &uts->tv_nsec) != 0))
> +		return EFAULT;
> +	return 0;
> +}
> +
> +static struct file *nfs4_get_real_file(struct file *src, unsigned int fd)
> +{
> +	struct file *filp = fget_raw(fd);
> +	int ret = -EBADF;
> +
> +	if (!filp)
> +		goto out;
> +	/* Validate that the files share the same underlying filesystem */
> +	ret = -EXDEV;
> +	if (file_inode(filp)->i_sb != file_inode(src)->i_sb)
> +		goto out_put;
> +	return filp;
> +out_put:
> +	fput(filp);
> +out:
> +	return ERR_PTR(ret);
> +}
> +
> +static unsigned long nfs4_statx_request_to_cache_validity(__u64 request,
> +							  u64 fattr_supported)
> +{
> +	unsigned long ret = 0;
> +
> +	if (request & NFS_FA_VALID_ATIME)
> +		ret |= NFS_INO_INVALID_ATIME;
> +	if (request & NFS_FA_VALID_CTIME)
> +		ret |= NFS_INO_INVALID_CTIME;
> +	if (request & NFS_FA_VALID_MTIME)
> +		ret |= NFS_INO_INVALID_MTIME;
> +	if (request & NFS_FA_VALID_SIZE)
> +		ret |= NFS_INO_INVALID_SIZE;
> +
> +	if (request & NFS_FA_VALID_MODE)
> +		ret |= NFS_INO_INVALID_MODE;
> +	if (request & (NFS_FA_VALID_OWNER | NFS_FA_VALID_OWNER_GROUP))
> +		ret |= NFS_INO_INVALID_OTHER;
> +
> +	if (request & NFS_FA_VALID_NLINK)
> +		ret |= NFS_INO_INVALID_NLINK;
> +	if (request & NFS_FA_VALID_BLOCKS)
> +		ret |= NFS_INO_INVALID_BLOCKS;
> +
> +	if (request & NFS_FA_VALID_TIME_CREATE)
> +		ret |= NFS_INO_INVALID_BTIME;
> +
> +	if (request & NFS_FA_VALID_ARCHIVE) {
> +		if (fattr_supported & NFS_ATTR_FATTR_ARCHIVE)
> +			ret |= NFS_INO_INVALID_WINATTR;
> +		else if (fattr_supported & NFS_ATTR_FATTR_TIME_BACKUP)
> +			ret |= NFS_INO_INVALID_WINATTR | NFS_INO_INVALID_MTIME;
> +	}
> +	if (request & (NFS_FA_VALID_TIME_BACKUP | NFS_FA_VALID_HIDDEN |
> +		       NFS_FA_VALID_SYSTEM | NFS_FA_VALID_OFFLINE))
> +		ret |= NFS_INO_INVALID_WINATTR;
> +
> +	return ret ? (ret | NFS_INO_INVALID_CHANGE) : 0;
> +}
> +
> +static long nfs4_ioctl_file_statx_get(struct file *dst_file,
> +				      struct nfs_ioctl_nfs4_statx __user *uarg)
> +{
> +	struct nfs4_statx args = {
> +		.real_fd = -1,
> +		.fa_valid = { 0 },
> +	};
> +	struct inode *inode;
> +	struct nfs_inode *nfsi;
> +	struct nfs_server *server;
> +	u64 fattr_supported;
> +	unsigned long reval_attr;
> +	unsigned int reval_flags;
> +	__u32 tmp;
> +	int ret;
> +
> +	/*
> +	 * We get the first word from the uarg as it tells us whether
> +	 * to use the passed in struct file or use that fd to find the
> +	 * struct file.
> +	 */
> +	if (get_user(args.real_fd, &uarg->real_fd))
> +		return -EFAULT;
> +
> +	if (get_user(args.fa_options, &uarg->fa_options))
> +		return -EFAULT;
> +
> +	if (get_user(args.fa_request[0], &uarg->fa_request[0]))
> +		return -EFAULT;
> +
> +	if (args.real_fd >= 0) {
> +		dst_file = nfs4_get_real_file(dst_file, args.real_fd);
> +		if (IS_ERR(dst_file))
> +			return PTR_ERR(dst_file);
> +	}
> +
> +	/*
> +	 * Backward compatibility: we stole the top 32 bits of 'real_fd'
> +	 * to create the fa_options field, so if its value is -1, then
> +	 * assume it is the high word of (__s64)real_fd == -1, and just
> +	 * set it to zero.
> +	 */
> +	if (args.fa_options == 0xFFFF)
> +		args.fa_options = 0;
> +
> +	inode = file_inode(dst_file);
> +	nfsi = NFS_I(inode);
> +	server = NFS_SERVER(inode);
> +	fattr_supported = server->fattr_valid;
> +
> +	trace_nfs_ioctl_file_statx_get_enter(inode);
> +
> +	if (args.fa_options & NFS_FA_OPTIONS_FORCE_SYNC)
> +		reval_flags = AT_STATX_FORCE_SYNC;
> +	else if (args.fa_options & NFS_FA_OPTIONS_DONT_SYNC)
> +		reval_flags = AT_STATX_DONT_SYNC;
> +	else
> +		reval_flags = AT_STATX_SYNC_AS_STAT;
> +
> +	reval_attr = nfs4_statx_request_to_cache_validity(args.fa_request[0],
> +							  fattr_supported);
> +
> +	if ((reval_attr & (NFS_INO_INVALID_CTIME | NFS_INO_INVALID_MTIME)) &&
> +	    reval_flags != AT_STATX_DONT_SYNC && S_ISREG(inode->i_mode)) {
> +		ret = filemap_write_and_wait(inode->i_mapping);
> +		if (ret)
> +			goto out;
> +	}
> +
> +	if ((dst_file->f_path.mnt->mnt_flags & MNT_NOATIME) ||
> +	    ((dst_file->f_path.mnt->mnt_flags & MNT_NODIRATIME) &&
> +	     S_ISDIR(inode->i_mode)))
> +		reval_attr &= ~NFS_INO_INVALID_ATIME;
> +
> +	ret = nfs_getattr_revalidate(&dst_file->f_path, reval_attr,
> +				     reval_flags);
> +	if (ret != 0)
> +		goto out;
> +
> +	ret = -EFAULT;
> +	if ((fattr_supported & NFS_ATTR_FATTR_OWNER) &&
> +	    (args.fa_request[0] & NFS_FA_VALID_OWNER)) {
> +		tmp = from_kuid_munged(current_user_ns(), inode->i_uid);
> +		if (unlikely(put_user(tmp, &uarg->fa_owner_uid) != 0))
> +			goto out;
> +		args.fa_valid[0] |= NFS_FA_VALID_OWNER;
> +	}
> +
> +	if ((fattr_supported & NFS_ATTR_FATTR_GROUP) &&
> +	    (args.fa_request[0] & NFS_FA_VALID_OWNER_GROUP)) {
> +		tmp = from_kgid_munged(current_user_ns(), inode->i_gid);
> +		if (unlikely(put_user(tmp, &uarg->fa_group_gid) != 0))
> +			goto out;
> +		args.fa_valid[0] |= NFS_FA_VALID_OWNER_GROUP;
> +	}
> +
> +	if ((fattr_supported & NFS_ATTR_FATTR_TIME_BACKUP) &&
> +	    (args.fa_request[0] & NFS_FA_VALID_TIME_BACKUP)) {
> +		if (nfs_put_timespec64(&nfsi->timebackup, &uarg->fa_time_backup))
> +			goto out;
> +		args.fa_valid[0] |= NFS_FA_VALID_TIME_BACKUP;
> +	}
> +
> +	if ((fattr_supported & NFS_ATTR_FATTR_BTIME) &&
> +	    (args.fa_request[0] & NFS_FA_VALID_TIME_CREATE)) {
> +		if (nfs_put_timespec64(&nfsi->btime, &uarg->fa_btime))
> +			goto out;
> +		args.fa_valid[0] |= NFS_FA_VALID_TIME_CREATE;
> +	}
> +
> +	/* atime, mtime, and ctime are all stored in the regular inode,
> +	 * not the nfs inode.
> +	 */
> +	if ((fattr_supported & NFS_ATTR_FATTR_ATIME) &&
> +	    (args.fa_request[0] & NFS_FA_VALID_ATIME)) {
> +		if (nfs_put_timespec64(&inode->i_atime, &uarg->fa_atime))
> +			goto out;
> +		args.fa_valid[0] |= NFS_FA_VALID_ATIME;
> +	}
> +
> +	if ((fattr_supported & NFS_ATTR_FATTR_MTIME) &&
> +	    (args.fa_request[0] & NFS_FA_VALID_MTIME)) {
> +		if (nfs_put_timespec64(&inode->i_mtime, &uarg->fa_mtime))
> +                        goto out;
> +		args.fa_valid[0] |= NFS_FA_VALID_MTIME;
> +	}
> +
> +	if ((fattr_supported & NFS_ATTR_FATTR_CTIME) &&
> +	    (args.fa_request[0] & NFS_FA_VALID_CTIME)) {
> +		if (nfs_put_timespec64(&inode->i_ctime, &uarg->fa_ctime))
> +			goto out;
> +		args.fa_valid[0] |= NFS_FA_VALID_CTIME;
> +	}
> +
> +        /*
> +         * It looks like PDFS does not support or properly handle the
> +         * archive bit.
> +         */
> +	if ((fattr_supported & NFS_ATTR_FATTR_ARCHIVE) &&
> +	    (args.fa_request[0] & NFS_FA_VALID_ARCHIVE)) {
> +		if (nfsi->archive)
> +			args.fa_flags |= NFS_FA_FLAG_ARCHIVE;
> +		args.fa_valid[0] |= NFS_FA_VALID_ARCHIVE;
> +	}
> +
> +	if ((fattr_supported & NFS_ATTR_FATTR_TIME_BACKUP) &&
> +	    (args.fa_request[0] & NFS_FA_VALID_ARCHIVE)) {
> +		if (timespec64_compare(&inode->i_mtime, &nfsi->timebackup) > 0)
> +			args.fa_flags |= NFS_FA_FLAG_ARCHIVE;
> +		args.fa_valid[0] |= NFS_FA_VALID_ARCHIVE;
> +	}
> +
> +	if ((fattr_supported & NFS_ATTR_FATTR_HIDDEN) &&
> +	    (args.fa_request[0] & NFS_FA_VALID_HIDDEN)) {
> +		if (nfsi->hidden)
> +			args.fa_flags |= NFS_FA_FLAG_HIDDEN;
> +		args.fa_valid[0] |= NFS_FA_VALID_HIDDEN;
> +	}
> +	if ((fattr_supported & NFS_ATTR_FATTR_SYSTEM) &&
> +	    (args.fa_request[0] & NFS_FA_VALID_SYSTEM)) {
> +		if (nfsi->system)
> +			args.fa_flags |= NFS_FA_FLAG_SYSTEM;
> +		args.fa_valid[0] |= NFS_FA_VALID_SYSTEM;
> +	}
> +
> +	if ((fattr_supported & NFS_ATTR_FATTR_OFFLINE) &&
> +	    (args.fa_request[0] & NFS_FA_VALID_OFFLINE)) {
> +		if (nfsi->offline)
> +			args.fa_flags |= NFS_FA_FLAG_OFFLINE;
> +		args.fa_valid[0] |= NFS_FA_VALID_OFFLINE;
> +	}
> +
> +	if ((args.fa_valid[0] & (NFS_FA_VALID_ARCHIVE |
> +				NFS_FA_VALID_HIDDEN |
> +				NFS_FA_VALID_SYSTEM |
> +				NFS_FA_VALID_OFFLINE)) &&
> +	    put_user(args.fa_flags, &uarg->fa_flags))
> +		goto out;
> +
> +	if ((fattr_supported & NFS_ATTR_FATTR_MODE) &&
> +	    (args.fa_request[0] & NFS_FA_VALID_MODE)) {
> +		tmp = inode->i_mode;
> +		/* This is an unsigned short we put into an __u32 */
> +		if (unlikely(put_user(tmp, &uarg->fa_mode) != 0))
> +			goto out;
> +		args.fa_valid[0] |= NFS_FA_VALID_MODE;
> +	}
> +
> +	if ((fattr_supported & NFS_ATTR_FATTR_NLINK) &&
> +	    (args.fa_request[0] & NFS_FA_VALID_NLINK)) {
> +		tmp = inode->i_nlink;
> +		if (unlikely(put_user(tmp, &uarg->fa_nlink) != 0))
> +			goto out;
> +		args.fa_valid[0] |= NFS_FA_VALID_NLINK;
> +	}
> +
> +	if (args.fa_request[0] & NFS_FA_VALID_BLKSIZE) {
> +		tmp = i_blocksize(inode);
> +		if (S_ISDIR(inode->i_mode))
> +			tmp = NFS_SERVER(inode)->dtsize;
> +		if (unlikely(put_user(tmp, &uarg->fa_blksize) != 0))
> +			goto out;
> +		args.fa_valid[0] |= NFS_FA_VALID_BLKSIZE;
> +	}
> +
> +	if (args.fa_request[0] & NFS_FA_VALID_INO) {
> +		__u64 ino = nfs_compat_user_ino64(NFS_FILEID(inode));
> +		if (unlikely(put_user(ino, &uarg->fa_ino) != 0))
> +			goto out;
> +		args.fa_valid[0] |= NFS_FA_VALID_INO;
> +	}
> +
> +	if (args.fa_request[0] & NFS_FA_VALID_DEV) {
> +		tmp = inode->i_sb->s_dev;
> +		if (unlikely(put_user(tmp, &uarg->fa_dev) != 0))
> +			goto out;
> +		args.fa_valid[0] |= NFS_FA_VALID_DEV;
> +	}
> +
> +	if ((fattr_supported & NFS_ATTR_FATTR_RDEV) &&
> +	    (args.fa_request[0] & NFS_FA_VALID_RDEV)) {
> +		tmp = inode->i_rdev;
> +		if (unlikely(put_user(tmp, &uarg->fa_rdev) != 0))
> +			goto out;
> +		args.fa_valid[0] |= NFS_FA_VALID_RDEV;
> +	}
> +
> +	if ((fattr_supported & NFS_ATTR_FATTR_SIZE) &&
> +	    (args.fa_request[0] & NFS_FA_VALID_SIZE)) {
> +		__s64 size = i_size_read(inode);
> +		if (unlikely(put_user(size, &uarg->fa_size) != 0))
> +			goto out;
> +		args.fa_valid[0] |= NFS_FA_VALID_SIZE;
> +	}
> +
> +	if ((fattr_supported &
> +	     (NFS_ATTR_FATTR_BLOCKS_USED | NFS_ATTR_FATTR_SPACE_USED)) &&
> +	    (args.fa_request[0] & NFS_FA_VALID_BLOCKS)) {
> +		__s64 blocks = inode->i_blocks;
> +		if (unlikely(put_user(blocks, &uarg->fa_blocks) != 0))
> +			goto out;
> +		args.fa_valid[0] |= NFS_FA_VALID_BLOCKS;
> +	}
> +
> +	if (unlikely(put_user(args.fa_valid[0], &uarg->fa_valid[0]) != 0))
> +		goto out;
> +	if (unlikely(put_user(args.fa_valid[1], &uarg->fa_valid[1]) != 0))
> +		goto out;
> +
> +	ret = 0;
> +out:
> +	if (args.real_fd >= 0)
> +		fput(dst_file);
> +	trace_nfs_ioctl_file_statx_get_exit(inode, ret);
> +	return ret;
> +}
> +
> +static long nfs4_ioctl_file_statx_set(struct file *dst_file,
> +				      struct nfs_ioctl_nfs4_statx __user *uarg)
> +{
> +	struct nfs4_statx args = {
> +		.real_fd = -1,
> +		.fa_valid = { 0 },
> +	};
> +	struct nfs_fattr *fattr = nfs_alloc_fattr();
> +	struct inode *inode;
> +	/*
> +	 * If you need a different error code below, you need to set it
> +	 */
> +	int ret = -EFAULT;
> +
> +	if (fattr == NULL)
> +		return -ENOMEM;
> +
> +	/*
> +	 * We get the first u64 word from the uarg as it tells us whether
> +	 * to use the passed in struct file or use that fd to find the
> +	 * struct file.
> +	 */
> +	if (get_user(args.real_fd, &uarg->real_fd))
> +		goto out_free;
> +
> +	if (args.real_fd >= 0) {
> +		dst_file = nfs4_get_real_file(dst_file, args.real_fd);
> +		if (IS_ERR(dst_file)) {
> +			ret = PTR_ERR(dst_file);
> +			goto out_free;
> +		}
> +	}
> +	inode = file_inode(dst_file);
> +	trace_nfs_ioctl_file_statx_set_enter(inode);
> +
> +	inode_lock(inode);
> +
> +	/* Write all dirty data */
> +	if (S_ISREG(inode->i_mode)) {
> +		ret = nfs_sync_inode(inode);
> +		if (ret)
> +			goto out;
> +	}
> +
> +	ret = -EFAULT;
> +	if (get_user(args.fa_valid[0], &uarg->fa_valid[0]))
> +		goto out;
> +	args.fa_valid[0] &= NFS_FA_VALID_ALL_ATTR_0;
> +
> +	if (args.fa_valid[0] & NFS_FA_VALID_OWNER) {
> +		uid_t uid;
> +
> +		if (unlikely(get_user(uid, &uarg->fa_owner_uid) != 0))
> +			goto out;
> +		args.fa_owner_uid = make_kuid(current_user_ns(), uid);
> +		if (!uid_valid(args.fa_owner_uid)) {
> +			ret = -EINVAL;
> +			goto out;
> +		}
> +	}
> +
> +	if (args.fa_valid[0] & NFS_FA_VALID_OWNER_GROUP) {
> +		gid_t gid;
> +
> +		if (unlikely(get_user(gid, &uarg->fa_group_gid) != 0))
> +			goto out;
> +		args.fa_group_gid = make_kgid(current_user_ns(), gid);
> +		if (!gid_valid(args.fa_group_gid)) {
> +			ret = -EINVAL;
> +			goto out;
> +		}
> +	}
> +
> +	if ((args.fa_valid[0] & (NFS_FA_VALID_ARCHIVE |
> +					NFS_FA_VALID_HIDDEN |
> +					NFS_FA_VALID_SYSTEM)) &&
> +	    get_user(args.fa_flags, &uarg->fa_flags))
> +		goto out;
> +
> +	if ((args.fa_valid[0] & NFS_FA_VALID_TIME_CREATE) &&
> +	    nfs_get_timespec64(&args.fa_btime, &uarg->fa_btime))
> +		goto out;
> +
> +	if ((args.fa_valid[0] & NFS_FA_VALID_ATIME) &&
> +	    nfs_get_timespec64(&args.fa_atime, &uarg->fa_atime))
> +		goto out;
> +
> +	if ((args.fa_valid[0] & NFS_FA_VALID_MTIME) &&
> +	    nfs_get_timespec64(&args.fa_mtime, &uarg->fa_mtime))
> +		goto out;
> +
> +	if (args.fa_valid[0] & NFS_FA_VALID_TIME_BACKUP) {
> +		if (nfs_get_timespec64(&args.fa_time_backup, &uarg->fa_time_backup))
> +			goto out;
> +	} else if ((args.fa_valid[0] & NFS_FA_VALID_ARCHIVE) &&
> +			!(NFS_SERVER(inode)->fattr_valid & NFS_ATTR_FATTR_ARCHIVE)) {
> +		args.fa_valid[0] |= NFS_FA_VALID_TIME_BACKUP;
> +		if (!(args.fa_flags & NFS_FA_FLAG_ARCHIVE)) {
> +			nfs_revalidate_inode(inode, NFS_INO_INVALID_MTIME);
> +			args.fa_time_backup.tv_sec = inode->i_mtime.tv_sec;
> +			args.fa_time_backup.tv_nsec = inode->i_mtime.tv_nsec;
> +		} else if (args.fa_valid[0] & NFS_FA_VALID_TIME_CREATE)
> +			args.fa_time_backup = args.fa_btime;
> +		else {
> +			nfs_revalidate_inode(inode, NFS_INO_INVALID_BTIME);
> +			args.fa_time_backup = NFS_I(inode)->btime;
> +		}
> +	}
> +
> +        if (args.fa_valid[0] & NFS_FA_VALID_SIZE) {
> +		if (copy_from_user(&args.fa_size, &uarg->fa_size,
> +					sizeof(args.fa_size)))
> +			goto out;
> +		ret = inode_newsize_ok(inode,args.fa_size);
> +		if (ret)
> +			goto out;
> +		if (args.fa_size == i_size_read(inode))
> +			args.fa_valid[0] &= ~NFS_FA_VALID_SIZE;
> +	}
> +
> +	/*
> +	 * No need to update the inode because that is done in nfs4_set_nfs4_statx
> +	 */
> +	ret = nfs4_set_nfs4_statx(inode, &args, fattr);
> +
> +out:
> +	inode_unlock(inode);
> +	if (args.real_fd >= 0)
> +		fput(dst_file);
> +	trace_nfs_ioctl_file_statx_set_exit(inode, ret);
> +out_free:
> +	nfs_free_fattr(fattr);
> +	return ret;
> +}
> +
> +static long nfs4_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> +{
> +	void __user *argp = (void __user *)arg;
> +	long ret;
> +
> +	switch (cmd) {
> +	case NFS_IOC_FILE_STATX_GET:
> +		ret = nfs4_ioctl_file_statx_get(file, argp);
> +		break;
> +	case NFS_IOC_FILE_STATX_SET:
> +		ret = nfs4_ioctl_file_statx_set(file, argp);
> +		break;
> +	default:
> +		ret = -ENOIOCTLCMD;
> +	}
> +
> +	dprintk("%s: file=%pD2, cmd=%u, ret=%ld\n", __func__, file, cmd, ret);
> +	return ret;
> +}
> +
>  #ifdef CONFIG_NFS_V4_2
>  static ssize_t __nfs4_copy_file_range(struct file *file_in, loff_t pos_in,
>  				      struct file *file_out, loff_t pos_out,
> @@ -187,6 +686,7 @@ static ssize_t __nfs4_copy_file_range(struct file *file_in, loff_t pos_in,
>  	return ret;
>  }
>  
> +
>  static ssize_t nfs4_copy_file_range(struct file *file_in, loff_t pos_in,
>  				    struct file *file_out, loff_t pos_out,
>  				    size_t count, unsigned int flags)
> @@ -461,4 +961,15 @@ const struct file_operations nfs4_file_operations = {
>  #else
>  	.llseek		= nfs_file_llseek,
>  #endif
> +	.unlocked_ioctl	= nfs4_ioctl,
> +};
> +
> +const struct file_operations nfs4_dir_operations = {
> +	.llseek		= nfs_llseek_dir,
> +	.read		= generic_read_dir,
> +	.iterate_shared	= nfs_readdir,
> +	.open		= nfs_opendir,
> +	.release	= nfs_closedir,
> +	.fsync		= nfs_fsync_dir,
> +	.unlocked_ioctl	= nfs4_ioctl,
>  };
> diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
> index d497616ca149..7c032583ffa2 100644
> --- a/fs/nfs/nfs4proc.c
> +++ b/fs/nfs/nfs4proc.c
> @@ -7959,6 +7959,129 @@ static int _nfs41_proc_get_locations(struct inode *inode,
>  
>  #endif	/* CONFIG_NFS_V4_1 */
>  
> +static int _nfs4_set_nfs4_statx(struct inode *inode,
> +		struct nfs4_statx *statx,
> +		struct nfs_fattr *fattr)
> +{
> +	const __u64 statx_win = NFS_FA_VALID_TIME_CREATE |
> +				NFS_FA_VALID_TIME_BACKUP |
> +				NFS_FA_VALID_ARCHIVE | NFS_FA_VALID_HIDDEN |
> +				NFS_FA_VALID_SYSTEM;
> +	struct iattr sattr = {0};
> +	struct nfs_server *server = NFS_SERVER(inode);
> +	__u32 bitmask[3];
> +	struct nfs_setattrargs arg = {
> +		.fh             = NFS_FH(inode),
> +		.iap            = &sattr,
> +		.server		= server,
> +		.bitmask	= bitmask,
> +		.statx		= statx,
> +	};
> +	struct nfs_setattrres res = {
> +		.fattr		= fattr,
> +		.server		= server,
> +	};
> +	struct rpc_message msg = {
> +		.rpc_proc       = &nfs4_procedures[NFSPROC4_CLNT_SETATTR],
> +		.rpc_argp       = &arg,
> +		.rpc_resp       = &res,
> +	};
> +	int status;
> +
> +	nfs4_bitmap_copy_adjust(
> +		bitmask, server->attr_bitmask, inode,
> +		NFS_INO_INVALID_CHANGE | NFS_INO_INVALID_CTIME |
> +			NFS_INO_INVALID_SIZE | NFS_INO_INVALID_OTHER |
> +			NFS_INO_INVALID_BTIME | NFS_INO_INVALID_WINATTR);
> +	/* Use the iattr structure to set atime and mtime since handling already
> +	 * exists for them using the iattr struct in the encode_attrs()
> +	 * (xdr encoding) routine.
> +	 */
> +	if (statx && (statx->fa_valid[0] & NFS_FA_VALID_MTIME)) {
> +		sattr.ia_valid |= ATTR_MTIME_SET;
> +		sattr.ia_mtime.tv_sec = statx->fa_mtime.tv_sec;
> +		sattr.ia_mtime.tv_nsec = statx->fa_mtime.tv_nsec;
> +	}
> +
> +	if (statx && (statx->fa_valid[0] & NFS_FA_VALID_ATIME)) {
> +		sattr.ia_valid |= ATTR_ATIME_SET;
> +		sattr.ia_atime.tv_sec = statx->fa_atime.tv_sec;
> +		sattr.ia_atime.tv_nsec = statx->fa_atime.tv_nsec;
> +	}
> +
> +	if (statx && (statx->fa_valid[0] & NFS_FA_VALID_OWNER)) {
> +		sattr.ia_valid |= ATTR_UID;
> +		sattr.ia_uid = statx->fa_owner_uid;
> +	}
> +
> +	if (statx && (statx->fa_valid[0] & NFS_FA_VALID_OWNER_GROUP)) {
> +		sattr.ia_valid |= ATTR_GID;
> +		sattr.ia_gid = statx->fa_group_gid;
> +	}
> +
> +	if (statx && (statx->fa_valid[0] & NFS_FA_VALID_SIZE)) {
> +		sattr.ia_valid |= ATTR_SIZE;
> +		sattr.ia_size = statx->fa_size;
> +	}
> +
> +	nfs4_stateid_copy(&arg.stateid, &zero_stateid);
> +
> +	status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
> +	if (!status) {
> +		if (statx->fa_valid[0] & statx_win) {
> +			struct nfs_inode *nfsi = NFS_I(inode);
> +
> +			spin_lock(&inode->i_lock);
> +			if (statx->fa_valid[0] & NFS_FA_VALID_TIME_CREATE)
> +				nfsi->btime = statx->fa_btime;
> +			if (statx->fa_valid[0] & NFS_FA_VALID_TIME_BACKUP)
> +				nfsi->timebackup = statx->fa_time_backup;
> +			if (statx->fa_valid[0] & NFS_FA_VALID_ARCHIVE)
> +				nfsi->archive = (statx->fa_flags &
> +						 NFS_FA_FLAG_ARCHIVE) != 0;
> +			if (statx->fa_valid[0] & NFS_FA_VALID_SYSTEM)
> +				nfsi->system = (statx->fa_flags &
> +						NFS_FA_FLAG_SYSTEM) != 0;
> +			if (statx->fa_valid[0] & NFS_FA_VALID_HIDDEN)
> +				nfsi->hidden = (statx->fa_flags &
> +						NFS_FA_FLAG_HIDDEN) != 0;
> +			if (statx->fa_valid[0] & NFS_FA_VALID_OFFLINE)
> +				nfsi->offline = (statx->fa_flags &
> +						 NFS_FA_FLAG_OFFLINE) != 0;
> +
> +			nfsi->cache_validity &= ~NFS_INO_INVALID_CTIME;
> +			if (fattr->valid & NFS_ATTR_FATTR_CTIME)
> +				inode->i_ctime = fattr->ctime;
> +			else
> +				nfs_set_cache_invalid(
> +					inode, NFS_INO_INVALID_CHANGE |
> +						   NFS_INO_INVALID_CTIME);
> +			spin_unlock(&inode->i_lock);
> +		}
> +
> +		nfs_setattr_update_inode(inode, &sattr, fattr);
> +	} else
> +		dprintk("%s failed: %d\n", __func__, status);
> +
> +	return status;
> +}
> +
> +int nfs4_set_nfs4_statx(struct inode *inode,
> +		struct nfs4_statx *statx,
> +		struct nfs_fattr *fattr)
> +{
> +	struct nfs4_exception exception = { };
> +	struct nfs_server *server = NFS_SERVER(inode);
> +	int err;
> +
> +	do {
> +		err = nfs4_handle_exception(server,
> +				_nfs4_set_nfs4_statx(inode, statx, fattr),
> +				&exception);
> +	} while (exception.retry);
> +	return err;
> +}
> +
>  /**
>   * nfs4_proc_get_locations - discover locations for a migrated FSID
>   * @inode: inode on FSID that is migrating
> @@ -10419,6 +10542,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
>  	.dir_inode_ops	= &nfs4_dir_inode_operations,
>  	.file_inode_ops	= &nfs4_file_inode_operations,
>  	.file_ops	= &nfs4_file_operations,
> +	.dir_ops	= &nfs4_dir_operations,
>  	.getroot	= nfs4_proc_get_root,
>  	.submount	= nfs4_submount,
>  	.try_get_tree	= nfs4_try_get_tree,
> diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
> index d2c240effc87..e5300d7ed712 100644
> --- a/fs/nfs/nfs4xdr.c
> +++ b/fs/nfs/nfs4xdr.c
> @@ -129,12 +129,15 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
>  				nfs4_fattr_value_maxsz)
>  #define decode_getattr_maxsz    (op_decode_hdr_maxsz + nfs4_fattr_maxsz)
>  #define encode_attrs_maxsz	(nfs4_fattr_bitmap_maxsz + \
> -				 1 + 2 + 1 + \
> +				 1 + 2 + 1 + 1 + 1 + \
>  				nfs4_owner_maxsz + \
>  				nfs4_group_maxsz + \
> -				nfs4_label_maxsz + \
> +				1 + \
> +				1 + nfstime4_maxsz + \
> +				nfstime4_maxsz + nfstime4_maxsz + \
>  				1 + nfstime4_maxsz + \
> -				1 + nfstime4_maxsz)
> +				nfs4_label_maxsz + \
> +				2)
>  #define encode_savefh_maxsz     (op_encode_hdr_maxsz)
>  #define decode_savefh_maxsz     (op_decode_hdr_maxsz)
>  #define encode_restorefh_maxsz  (op_encode_hdr_maxsz)
> @@ -1081,6 +1084,7 @@ xdr_encode_nfstime4(__be32 *p, const struct timespec64 *t)
>  static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
>  				const struct nfs4_label *label,
>  				const umode_t *umask,
> +				const struct nfs4_statx *statx,
>  				const struct nfs_server *server,
>  				const uint32_t attrmask[])
>  {
> @@ -1153,6 +1157,34 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
>  		}
>  	}
>  
> +	if (statx && (statx->fa_valid[0] & NFS_FA_VALID_TIME_BACKUP) &&
> +	    (attrmask[1] & FATTR4_WORD1_TIME_BACKUP)) {
> +		bmval[1] |= FATTR4_WORD1_TIME_BACKUP;
> +		len += (nfstime4_maxsz << 2);
> +	}
> +	if (statx && (statx->fa_valid[0] & NFS_FA_VALID_TIME_CREATE) &&
> +	    (attrmask[1] & FATTR4_WORD1_TIME_CREATE)) {
> +		bmval[1] |= FATTR4_WORD1_TIME_CREATE;
> +		len += (nfstime4_maxsz << 2);
> +	}
> +
> +	if (statx && (statx->fa_valid[0] & NFS_FA_VALID_ARCHIVE) &&
> +	   (attrmask[0] & FATTR4_WORD0_ARCHIVE)) {
> +		bmval[0] |= FATTR4_WORD0_ARCHIVE;
> +		len += 4;
> +	}
> +	if (statx && (statx->fa_valid[0] & NFS_FA_VALID_HIDDEN) &&
> +	   (attrmask[0] & FATTR4_WORD0_HIDDEN)) {
> +		bmval[0] |= FATTR4_WORD0_HIDDEN;
> +		len += 4;
> +	}
> +
> +	if (statx && (statx->fa_valid[0] & NFS_FA_VALID_SYSTEM) &&
> +	   (attrmask[1] & FATTR4_WORD1_SYSTEM)) {
> +		bmval[1] |= FATTR4_WORD1_SYSTEM;
> +		len += 4;
> +	}
> +
>  	if (label && (attrmask[2] & FATTR4_WORD2_SECURITY_LABEL)) {
>  		len += 4 + 4 + 4 + (XDR_QUADLEN(label->len) << 2);
>  		bmval[2] |= FATTR4_WORD2_SECURITY_LABEL;
> @@ -1163,12 +1195,21 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
>  
>  	if (bmval[0] & FATTR4_WORD0_SIZE)
>  		p = xdr_encode_hyper(p, iap->ia_size);
> +	if (bmval[0] & FATTR4_WORD0_ARCHIVE)
> +		*p++ = (statx->fa_flags & NFS_FA_FLAG_ARCHIVE) ?
> +			cpu_to_be32(1) : cpu_to_be32(0);
> +	if (bmval[0] & FATTR4_WORD0_HIDDEN)
> +		*p++ = (statx->fa_flags & NFS_FA_FLAG_HIDDEN) ?
> +			cpu_to_be32(1) : cpu_to_be32(0);
>  	if (bmval[1] & FATTR4_WORD1_MODE)
>  		*p++ = cpu_to_be32(iap->ia_mode & S_IALLUGO);
>  	if (bmval[1] & FATTR4_WORD1_OWNER)
>  		p = xdr_encode_opaque(p, owner_name, owner_namelen);
>  	if (bmval[1] & FATTR4_WORD1_OWNER_GROUP)
>  		p = xdr_encode_opaque(p, owner_group, owner_grouplen);
> +	if (bmval[1] & FATTR4_WORD1_SYSTEM)
> +		*p++ = (statx->fa_flags & NFS_FA_FLAG_SYSTEM) ?
> +			cpu_to_be32(1) : cpu_to_be32(0);
>  	if (bmval[1] & FATTR4_WORD1_TIME_ACCESS_SET) {
>  		if (iap->ia_valid & ATTR_ATIME_SET) {
>  			*p++ = cpu_to_be32(NFS4_SET_TO_CLIENT_TIME);
> @@ -1176,6 +1217,10 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
>  		} else
>  			*p++ = cpu_to_be32(NFS4_SET_TO_SERVER_TIME);
>  	}
> +	if (bmval[1] & FATTR4_WORD1_TIME_BACKUP)
> +		p = xdr_encode_nfstime4(p, &statx->fa_time_backup);
> +	if (bmval[1] & FATTR4_WORD1_TIME_CREATE)
> +		p = xdr_encode_nfstime4(p, &statx->fa_btime);
>  	if (bmval[1] & FATTR4_WORD1_TIME_MODIFY_SET) {
>  		if (iap->ia_valid & ATTR_MTIME_SET) {
>  			*p++ = cpu_to_be32(NFS4_SET_TO_CLIENT_TIME);
> @@ -1248,7 +1293,7 @@ static void encode_create(struct xdr_stream *xdr, const struct nfs4_create_arg *
>  
>  	encode_string(xdr, create->name->len, create->name->name);
>  	encode_attrs(xdr, create->attrs, create->label, &create->umask,
> -			create->server, create->server->attr_bitmask);
> +		     NULL, create->server, create->server->attr_bitmask);
>  }
>  
>  static void encode_getattr(struct xdr_stream *xdr,
> @@ -1434,12 +1479,12 @@ static inline void encode_createmode(struct xdr_stream *xdr, const struct nfs_op
>  	case NFS4_CREATE_UNCHECKED:
>  		*p = cpu_to_be32(NFS4_CREATE_UNCHECKED);
>  		encode_attrs(xdr, arg->u.attrs, arg->label, &arg->umask,
> -				arg->server, arg->server->attr_bitmask);
> +			     NULL, arg->server, arg->server->attr_bitmask);
>  		break;
>  	case NFS4_CREATE_GUARDED:
>  		*p = cpu_to_be32(NFS4_CREATE_GUARDED);
>  		encode_attrs(xdr, arg->u.attrs, arg->label, &arg->umask,
> -				arg->server, arg->server->attr_bitmask);
> +			     NULL, arg->server, arg->server->attr_bitmask);
>  		break;
>  	case NFS4_CREATE_EXCLUSIVE:
>  		*p = cpu_to_be32(NFS4_CREATE_EXCLUSIVE);
> @@ -1449,7 +1494,7 @@ static inline void encode_createmode(struct xdr_stream *xdr, const struct nfs_op
>  		*p = cpu_to_be32(NFS4_CREATE_EXCLUSIVE4_1);
>  		encode_nfs4_verifier(xdr, &arg->u.verifier);
>  		encode_attrs(xdr, arg->u.attrs, arg->label, &arg->umask,
> -				arg->server, arg->server->exclcreat_bitmask);
> +			     NULL, arg->server, arg->server->exclcreat_bitmask);
>  	}
>  }
>  
> @@ -1712,8 +1757,8 @@ static void encode_setattr(struct xdr_stream *xdr, const struct nfs_setattrargs
>  {
>  	encode_op_hdr(xdr, OP_SETATTR, decode_setattr_maxsz, hdr);
>  	encode_nfs4_stateid(xdr, &arg->stateid);
> -	encode_attrs(xdr, arg->iap, arg->label, NULL, server,
> -			server->attr_bitmask);
> +	encode_attrs(xdr, arg->iap, arg->label, NULL, arg->statx, server,
> +		     server->attr_bitmask);
>  }
>  
>  static void encode_setclientid(struct xdr_stream *xdr, const struct nfs4_setclientid *setclientid, struct compound_hdr *hdr)
> diff --git a/fs/nfs/nfstrace.c b/fs/nfs/nfstrace.c
> index 5d1bfccbb4da..0b88deb0216e 100644
> --- a/fs/nfs/nfstrace.c
> +++ b/fs/nfs/nfstrace.c
> @@ -9,6 +9,11 @@
>  #define CREATE_TRACE_POINTS
>  #include "nfstrace.h"
>  
> +EXPORT_TRACEPOINT_SYMBOL_GPL(nfs_ioctl_file_statx_get_enter);
> +EXPORT_TRACEPOINT_SYMBOL_GPL(nfs_ioctl_file_statx_get_exit);
> +EXPORT_TRACEPOINT_SYMBOL_GPL(nfs_ioctl_file_statx_set_enter);
> +EXPORT_TRACEPOINT_SYMBOL_GPL(nfs_ioctl_file_statx_set_exit);
> +
>  EXPORT_TRACEPOINT_SYMBOL_GPL(nfs_fsync_enter);
>  EXPORT_TRACEPOINT_SYMBOL_GPL(nfs_fsync_exit);
>  EXPORT_TRACEPOINT_SYMBOL_GPL(nfs_xdr_status);
> diff --git a/fs/nfs/nfstrace.h b/fs/nfs/nfstrace.h
> index 2ef7cff8a4ba..b67dd087fb47 100644
> --- a/fs/nfs/nfstrace.h
> +++ b/fs/nfs/nfstrace.h
> @@ -166,6 +166,11 @@ DEFINE_NFS_INODE_EVENT_DONE(nfs_fsync_exit);
>  DEFINE_NFS_INODE_EVENT(nfs_access_enter);
>  DEFINE_NFS_INODE_EVENT_DONE(nfs_set_cache_invalid);
>  
> +DEFINE_NFS_INODE_EVENT(nfs_ioctl_file_statx_get_enter);
> +DEFINE_NFS_INODE_EVENT_DONE(nfs_ioctl_file_statx_get_exit);
> +DEFINE_NFS_INODE_EVENT(nfs_ioctl_file_statx_set_enter);
> +DEFINE_NFS_INODE_EVENT_DONE(nfs_ioctl_file_statx_set_exit);
> +
>  TRACE_EVENT(nfs_access_exit,
>  		TP_PROTO(
>  			const struct inode *inode,
> diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c
> index 73dcaa99fa9b..8fd96d93630a 100644
> --- a/fs/nfs/proc.c
> +++ b/fs/nfs/proc.c
> @@ -717,6 +717,7 @@ const struct nfs_rpc_ops nfs_v2_clientops = {
>  	.dir_inode_ops	= &nfs_dir_inode_operations,
>  	.file_inode_ops	= &nfs_file_inode_operations,
>  	.file_ops	= &nfs_file_operations,
> +	.dir_ops	= &nfs_dir_operations,
>  	.getroot	= nfs_proc_get_root,
>  	.submount	= nfs_submount,
>  	.try_get_tree	= nfs_try_get_tree,
> diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
> index 058fc11338d9..0c3a5859f7f3 100644
> --- a/include/linux/nfs_fs.h
> +++ b/include/linux/nfs_fs.h
> @@ -501,6 +501,7 @@ extern __be32 root_nfs_parse_addr(char *name); /*__init*/
>  extern const struct file_operations nfs_file_operations;
>  #if IS_ENABLED(CONFIG_NFS_V4)
>  extern const struct file_operations nfs4_file_operations;
> +extern const struct file_operations nfs4_dir_operations;
>  #endif /* CONFIG_NFS_V4 */
>  extern const struct address_space_operations nfs_file_aops;
>  extern const struct address_space_operations nfs_dir_aops;
> diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
> index 0d5b11c1bfec..9ce61f680a13 100644
> --- a/include/linux/nfs_xdr.h
> +++ b/include/linux/nfs_xdr.h
> @@ -812,6 +812,7 @@ struct nfs_createargs {
>  	struct iattr *		sattr;
>  };
>  
> +struct nfs4_statx;
>  struct nfs_setattrargs {
>  	struct nfs4_sequence_args 	seq_args;
>  	struct nfs_fh *                 fh;
> @@ -820,6 +821,7 @@ struct nfs_setattrargs {
>  	const struct nfs_server *	server; /* Needed for name mapping */
>  	const u32 *			bitmask;
>  	const struct nfs4_label		*label;
> +	const struct nfs4_statx		*statx;
>  };
>  
>  struct nfs_setaclargs {
> @@ -1744,6 +1746,7 @@ struct nfs_rpc_ops {
>  	const struct inode_operations *dir_inode_ops;
>  	const struct inode_operations *file_inode_ops;
>  	const struct file_operations *file_ops;
> +	const struct file_operations *dir_ops;
>  	const struct nlmclnt_operations *nlmclnt_ops;
>  
>  	int	(*getroot) (struct nfs_server *, struct nfs_fh *,
> diff --git a/include/uapi/linux/nfs.h b/include/uapi/linux/nfs.h
> index 946cb62d64b0..df87da39bc43 100644
> --- a/include/uapi/linux/nfs.h
> +++ b/include/uapi/linux/nfs.h
> @@ -9,6 +9,8 @@
>  #define _UAPI_LINUX_NFS_H
>  
>  #include <linux/types.h>
> +#include <asm/byteorder.h>
> +#include <linux/time.h>
>  
>  #define NFS_PROGRAM	100003
>  #define NFS_PORT	2049
> @@ -35,6 +37,94 @@
>  
>  #define NFS_PIPE_DIRNAME "nfs"
>  
> +/* NFS ioctls */
> +#define NFS_IOC_FILE_STATX_GET	_IOR('N', 2, struct nfs_ioctl_nfs4_statx)
> +#define NFS_IOC_FILE_STATX_SET	_IOW('N', 3, struct nfs_ioctl_nfs4_statx)
> +
> +/* Options for struct nfs_ioctl_nfs4_statx */
> +#define NFS_FA_OPTIONS_SYNC_AS_STAT			0x0000
> +#define NFS_FA_OPTIONS_FORCE_SYNC			0x2000 /* See statx */
> +#define NFS_FA_OPTIONS_DONT_SYNC			0x4000 /* See statx */
> +
> +#define NFS_FA_VALID_TIME_CREATE			0x00001UL
> +#define NFS_FA_VALID_TIME_BACKUP			0x00002UL
> +#define NFS_FA_VALID_ARCHIVE				0x00004UL
> +#define NFS_FA_VALID_HIDDEN				0x00008UL
> +#define NFS_FA_VALID_SYSTEM				0x00010UL
> +#define NFS_FA_VALID_OWNER				0x00020UL
> +#define NFS_FA_VALID_OWNER_GROUP			0x00040UL
> +#define NFS_FA_VALID_ATIME				0x00080UL
> +#define NFS_FA_VALID_MTIME				0x00100UL
> +#define NFS_FA_VALID_CTIME				0x00200UL
> +#define NFS_FA_VALID_OFFLINE				0x00400UL
> +#define NFS_FA_VALID_MODE				0x00800UL
> +#define NFS_FA_VALID_NLINK				0x01000UL
> +#define NFS_FA_VALID_BLKSIZE				0x02000UL
> +#define NFS_FA_VALID_INO				0x04000UL
> +#define NFS_FA_VALID_DEV				0x08000UL
> +#define NFS_FA_VALID_RDEV				0x10000UL
> +#define NFS_FA_VALID_SIZE				0x20000UL
> +#define NFS_FA_VALID_BLOCKS				0x40000UL
> +
> +#define NFS_FA_VALID_ALL_ATTR_0 ( NFS_FA_VALID_TIME_CREATE | \
> +		NFS_FA_VALID_TIME_BACKUP | \
> +		NFS_FA_VALID_ARCHIVE | \
> +		NFS_FA_VALID_HIDDEN | \
> +		NFS_FA_VALID_SYSTEM | \
> +		NFS_FA_VALID_OWNER | \
> +		NFS_FA_VALID_OWNER_GROUP | \
> +		NFS_FA_VALID_ATIME | \
> +		NFS_FA_VALID_MTIME | \
> +		NFS_FA_VALID_CTIME | \
> +		NFS_FA_VALID_OFFLINE | \
> +		NFS_FA_VALID_MODE | \
> +		NFS_FA_VALID_NLINK | \
> +		NFS_FA_VALID_BLKSIZE | \
> +		NFS_FA_VALID_INO | \
> +		NFS_FA_VALID_DEV | \
> +		NFS_FA_VALID_RDEV | \
> +		NFS_FA_VALID_SIZE | \
> +		NFS_FA_VALID_BLOCKS)
> +
> +#define NFS_FA_FLAG_ARCHIVE				(1UL << 0)
> +#define NFS_FA_FLAG_HIDDEN				(1UL << 1)
> +#define NFS_FA_FLAG_SYSTEM				(1UL << 2)
> +#define NFS_FA_FLAG_OFFLINE				(1UL << 3)
> +
> +struct nfs_ioctl_timespec {
> +	__s64		tv_sec;
> +	__s64		tv_nsec;
> +};
> +
> +struct nfs_ioctl_nfs4_statx {
> +	__s32		real_fd;		/* real FD to use,
> +						   -1 means use current file */
> +	__u32		fa_options;
> +
> +	__u64		fa_request[2];		/* Attributes to retrieve */
> +	__u64		fa_valid[2];		/* Attributes set */
> +
> +	struct nfs_ioctl_timespec fa_time_backup;/* Backup time */
> +	struct nfs_ioctl_timespec fa_btime;     /* Birth time */
> +	__u64		fa_flags;		/* Flag attributes */
> +	/* Ordinary attributes follow */
> +	struct nfs_ioctl_timespec fa_atime;	/* Access time */
> +	struct nfs_ioctl_timespec fa_mtime;	/* Modify time */
> +	struct nfs_ioctl_timespec fa_ctime;	/* Change time */
> +	__u32		fa_owner_uid;		/* Owner User ID */
> +	__u32		fa_group_gid;		/* Primary Group ID */
> +	__u32		fa_mode;		/* Mode */
> +	__u32	 	fa_nlink;
> +	__u32		fa_blksize;
> +	__u32		fa_spare;		/* Alignment */
> +	__u64		fa_ino;
> +	__u32		fa_dev;
> +	__u32		fa_rdev;
> +	__s64		fa_size;
> +	__s64		fa_blocks;
> +	__u64 		fa_padding[4];
> +};
> +
>  /*
>   * NFS stats. The good thing with these values is that NFSv3 errors are
>   * a superset of NFSv2 errors (with the exception of NFSERR_WFLUSH which
> -- 
> 2.33.1

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH 7/8] NFS: Support statx_get and statx_set ioctls
  2022-01-03 20:52               ` [PATCH 7/8] NFS: Support statx_get and statx_set ioctls J. Bruce Fields
@ 2022-01-03 20:56                 ` Trond Myklebust
  2022-01-03 21:47                   ` bfields
  0 siblings, 1 reply; 30+ messages in thread
From: Trond Myklebust @ 2022-01-03 20:56 UTC (permalink / raw)
  To: bfields, trondmy; +Cc: linux-nfs, anna.schumaker

On Mon, 2022-01-03 at 15:52 -0500, J. Bruce Fields wrote:
> On Fri, Dec 17, 2021 at 03:48:53PM -0500, trondmy@kernel.org wrote:
> > From: Richard Sharpe <richard.sharpe@primarydata.com>
> > 
> > Add support for returning all of the Windows attributes with a
> > statx
> > ioctl.
> 
> I suppose I'm just woodshedding, but "statx ioctl" is a little
> confusing--it doesn't have any actual connection with the statx
> system call, right?
> 
> But, why not add this to statx?

We could definitely add the attribute retrieval to the statx() system
call. I believe that Steve French did suggest that at one point. There
was push back because the number of applications that care is limited.
Perhaps there might be more interest now that we have more extensive
support for NTFS in the kernel.

However there is no statx() call to actually _set_ these attributes,
and that is a reason to stick with the ioctl() approach for now.


> 
> --b.
> 
> > Add support for setting all of the Windows attributes using an
> > ioctl.
> > 
> > Signed-off-by: Richard Sharpe <richard.sharpe@primarydata.com>
> > Signed-off-by: Lance Shelton <lance.shelton@hammerspace.com>
> > Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
> > ---
> >  fs/nfs/dir.c             |  24 +-
> >  fs/nfs/getroot.c         |   3 +-
> >  fs/nfs/inode.c           |  41 +++-
> >  fs/nfs/internal.h        |   8 +
> >  fs/nfs/nfs3proc.c        |   1 +
> >  fs/nfs/nfs4_fs.h         |  31 +++
> >  fs/nfs/nfs4file.c        | 511
> > +++++++++++++++++++++++++++++++++++++++
> >  fs/nfs/nfs4proc.c        | 124 ++++++++++
> >  fs/nfs/nfs4xdr.c         |  63 ++++-
> >  fs/nfs/nfstrace.c        |   5 +
> >  fs/nfs/nfstrace.h        |   5 +
> >  fs/nfs/proc.c            |   1 +
> >  include/linux/nfs_fs.h   |   1 +
> >  include/linux/nfs_xdr.h  |   3 +
> >  include/uapi/linux/nfs.h |  90 +++++++
> >  15 files changed, 887 insertions(+), 24 deletions(-)
> > 
> > diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
> > index 731d31015b6a..f6fc60822153 100644
> > --- a/fs/nfs/dir.c
> > +++ b/fs/nfs/dir.c
> > @@ -48,11 +48,6 @@
> >  
> >  /* #define NFS_DEBUG_VERBOSE 1 */
> >  
> > -static int nfs_opendir(struct inode *, struct file *);
> > -static int nfs_closedir(struct inode *, struct file *);
> > -static int nfs_readdir(struct file *, struct dir_context *);
> > -static int nfs_fsync_dir(struct file *, loff_t, loff_t, int);
> > -static loff_t nfs_llseek_dir(struct file *, loff_t, int);
> >  static void nfs_readdir_clear_array(struct page*);
> >  
> >  const struct file_operations nfs_dir_operations = {
> > @@ -63,6 +58,7 @@ const struct file_operations nfs_dir_operations =
> > {
> >         .release        = nfs_closedir,
> >         .fsync          = nfs_fsync_dir,
> >  };
> > +EXPORT_SYMBOL_GPL(nfs_dir_operations);
> >  
> >  const struct address_space_operations nfs_dir_aops = {
> >         .freepage = nfs_readdir_clear_array,
> > @@ -104,8 +100,7 @@ static void put_nfs_open_dir_context(struct
> > inode *dir, struct nfs_open_dir_cont
> >  /*
> >   * Open file
> >   */
> > -static int
> > -nfs_opendir(struct inode *inode, struct file *filp)
> > +int nfs_opendir(struct inode *inode, struct file *filp)
> >  {
> >         int res = 0;
> >         struct nfs_open_dir_context *ctx;
> > @@ -123,13 +118,14 @@ nfs_opendir(struct inode *inode, struct file
> > *filp)
> >  out:
> >         return res;
> >  }
> > +EXPORT_SYMBOL_GPL(nfs_opendir);
> >  
> > -static int
> > -nfs_closedir(struct inode *inode, struct file *filp)
> > +int nfs_closedir(struct inode *inode, struct file *filp)
> >  {
> >         put_nfs_open_dir_context(file_inode(filp), filp-
> > >private_data);
> >         return 0;
> >  }
> > +EXPORT_SYMBOL_GPL(nfs_closedir);
> >  
> >  struct nfs_cache_array_entry {
> >         u64 cookie;
> > @@ -1064,7 +1060,7 @@ static int uncached_readdir(struct
> > nfs_readdir_descriptor *desc)
> >     last cookie cache takes care of the common case of reading the
> >     whole directory.
> >   */
> > -static int nfs_readdir(struct file *file, struct dir_context *ctx)
> > +int nfs_readdir(struct file *file, struct dir_context *ctx)
> >  {
> >         struct dentry   *dentry = file_dentry(file);
> >         struct inode    *inode = d_inode(dentry);
> > @@ -1157,8 +1153,9 @@ static int nfs_readdir(struct file *file,
> > struct dir_context *ctx)
> >         dfprintk(FILE, "NFS: readdir(%pD2) returns %d\n", file,
> > res);
> >         return res;
> >  }
> > +EXPORT_SYMBOL_GPL(nfs_readdir);
> >  
> > -static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int
> > whence)
> > +loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int
> > whence)
> >  {
> >         struct nfs_open_dir_context *dir_ctx = filp->private_data;
> >  
> > @@ -1196,19 +1193,20 @@ static loff_t nfs_llseek_dir(struct file
> > *filp, loff_t offset, int whence)
> >         spin_unlock(&filp->f_lock);
> >         return offset;
> >  }
> > +EXPORT_SYMBOL_GPL(nfs_llseek_dir);
> >  
> >  /*
> >   * All directory operations under NFS are synchronous, so fsync()
> >   * is a dummy operation.
> >   */
> > -static int nfs_fsync_dir(struct file *filp, loff_t start, loff_t
> > end,
> > -                        int datasync)
> > +int nfs_fsync_dir(struct file *filp, loff_t start, loff_t end, int
> > datasync)
> >  {
> >         dfprintk(FILE, "NFS: fsync dir(%pD2) datasync %d\n", filp,
> > datasync);
> >  
> >         nfs_inc_stats(file_inode(filp), NFSIOS_VFSFSYNC);
> >         return 0;
> >  }
> > +EXPORT_SYMBOL_GPL(nfs_fsync_dir);
> >  
> >  /**
> >   * nfs_force_lookup_revalidate - Mark the directory as having
> > changed
> > diff --git a/fs/nfs/getroot.c b/fs/nfs/getroot.c
> > index 11ff2b2e060f..f872970d6240 100644
> > --- a/fs/nfs/getroot.c
> > +++ b/fs/nfs/getroot.c
> > @@ -127,7 +127,8 @@ int nfs_get_root(struct super_block *s, struct
> > fs_context *fc)
> >         if (server->caps & NFS_CAP_SECURITY_LABEL)
> >                 kflags |= SECURITY_LSM_NATIVE_LABELS;
> >         if (ctx->clone_data.sb) {
> > -               if (d_inode(fc->root)->i_fop !=
> > &nfs_dir_operations) {
> > +               if (d_inode(fc->root)->i_fop !=
> > +                   server->nfs_client->rpc_ops->dir_ops) {
> >                         error = -ESTALE;
> >                         goto error_splat_root;
> >                 }
> > diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
> > index 33f4410190b6..8da662a4953d 100644
> > --- a/fs/nfs/inode.c
> > +++ b/fs/nfs/inode.c
> > @@ -108,6 +108,7 @@ u64 nfs_compat_user_ino64(u64 fileid)
> >                 ino ^= fileid >> (sizeof(fileid)-sizeof(ino)) * 8;
> >         return ino;
> >  }
> > +EXPORT_SYMBOL_GPL(nfs_compat_user_ino64);
> >  
> >  int nfs_drop_inode(struct inode *inode)
> >  {
> > @@ -501,7 +502,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh
> > *fh, struct nfs_fattr *fattr)
> >                         nfs_inode_init_regular(nfsi);
> >                 } else if (S_ISDIR(inode->i_mode)) {
> >                         inode->i_op = NFS_SB(sb)->nfs_client-
> > >rpc_ops->dir_inode_ops;
> > -                       inode->i_fop = &nfs_dir_operations;
> > +                       inode->i_fop = NFS_SB(sb)->nfs_client-
> > >rpc_ops->dir_ops;
> >                         inode->i_data.a_ops = &nfs_dir_aops;
> >                         nfs_inode_init_dir(nfsi);
> >                         /* Deal with crossing mountpoints */
> > @@ -867,6 +868,44 @@ static u32 nfs_get_valid_attrmask(struct inode
> > *inode)
> >         return reply_mask;
> >  }
> >  
> > +static int nfs_getattr_revalidate_force(struct dentry *dentry)
> > +{
> > +       struct inode *inode = d_inode(dentry);
> > +       struct nfs_server *server = NFS_SERVER(inode);
> > +
> > +       if (!(server->flags & NFS_MOUNT_NOAC))
> > +               nfs_readdirplus_parent_cache_miss(dentry);
> > +       else
> > +               nfs_readdirplus_parent_cache_hit(dentry);
> > +       return __nfs_revalidate_inode(server, inode);
> > +}
> > +
> > +static int nfs_getattr_revalidate_none(struct dentry *dentry)
> > +{
> > +       nfs_readdirplus_parent_cache_hit(dentry);
> > +       return NFS_STALE(d_inode(dentry)) ? -ESTALE : 0;
> > +}
> > +
> > +static int nfs_getattr_revalidate_maybe(struct dentry *dentry,
> > +                                       unsigned long flags)
> > +{
> > +       if (nfs_check_cache_invalid(d_inode(dentry), flags))
> > +               return nfs_getattr_revalidate_force(dentry);
> > +       return nfs_getattr_revalidate_none(dentry);
> > +}
> > +
> > +int nfs_getattr_revalidate(const struct path *path,
> > +                          unsigned long flags,
> > +                          unsigned int query_flags)
> > +{
> > +       if (query_flags & AT_STATX_FORCE_SYNC)
> > +               return nfs_getattr_revalidate_force(path->dentry);
> > +       if (!(query_flags & AT_STATX_DONT_SYNC))
> > +               return nfs_getattr_revalidate_maybe(path->dentry,
> > flags);
> > +       return nfs_getattr_revalidate_none(path->dentry);
> > +}
> > +EXPORT_SYMBOL_GPL(nfs_getattr_revalidate);
> > +
> >  int nfs_getattr(struct user_namespace *mnt_userns, const struct
> > path *path,
> >                 struct kstat *stat, u32 request_mask, unsigned int
> > query_flags)
> >  {
> > diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
> > index 12f6acb483bb..9602a886f0f0 100644
> > --- a/fs/nfs/internal.h
> > +++ b/fs/nfs/internal.h
> > @@ -366,6 +366,12 @@ extern struct nfs_client
> > *nfs_init_client(struct nfs_client *clp,
> >                            const struct nfs_client_initdata *);
> >  
> >  /* dir.c */
> > +int nfs_opendir(struct inode *, struct file *);
> > +int nfs_closedir(struct inode *, struct file *);
> > +int nfs_readdir(struct file *file, struct dir_context *ctx);
> > +int nfs_fsync_dir(struct file *, loff_t, loff_t, int);
> > +loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int
> > whence);
> > +
> >  extern void nfs_advise_use_readdirplus(struct inode *dir);
> >  extern void nfs_force_use_readdirplus(struct inode *dir);
> >  extern unsigned long nfs_access_cache_count(struct shrinker
> > *shrink,
> > @@ -411,6 +417,8 @@ extern void nfs_set_cache_invalid(struct inode
> > *inode, unsigned long flags);
> >  extern bool nfs_check_cache_invalid(struct inode *, unsigned
> > long);
> >  extern int nfs_wait_bit_killable(struct wait_bit_key *key, int
> > mode);
> >  extern int nfs_wait_atomic_killable(atomic_t *p, unsigned int
> > mode);
> > +extern int nfs_getattr_revalidate(const struct path *path,
> > unsigned long flags,
> > +                                 unsigned int query_flags);
> >  
> >  /* super.c */
> >  extern const struct super_operations nfs_sops;
> > diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c
> > index 7100514d306b..091005e169b7 100644
> > --- a/fs/nfs/nfs3proc.c
> > +++ b/fs/nfs/nfs3proc.c
> > @@ -1018,6 +1018,7 @@ const struct nfs_rpc_ops nfs_v3_clientops = {
> >         .dir_inode_ops  = &nfs3_dir_inode_operations,
> >         .file_inode_ops = &nfs3_file_inode_operations,
> >         .file_ops       = &nfs_file_operations,
> > +       .dir_ops        = &nfs_dir_operations,
> >         .nlmclnt_ops    = &nlmclnt_fl_close_lock_ops,
> >         .getroot        = nfs3_proc_get_root,
> >         .submount       = nfs_submount,
> > diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
> > index ed5eaca6801e..9f21d8520e99 100644
> > --- a/fs/nfs/nfs4_fs.h
> > +++ b/fs/nfs/nfs4_fs.h
> > @@ -248,6 +248,34 @@ struct nfs4_opendata {
> >         int rpc_status;
> >  };
> >  
> > +struct nfs4_statx {
> > +       int             real_fd;                /* real FD to use,
> > +                                                  -1 means use
> > current file */
> > +       __u32           fa_options;             /* statx flags */
> > +       __u64           fa_request[2];          /* Attributes
> > requested */
> > +       __u64           fa_valid[2];            /* Attributes set
> > */
> > +
> > +       struct timespec64 fa_time_backup;       /* Backup time */
> > +       struct timespec64 fa_btime;             /* Birth time */
> > +       /* Flag attributes */
> > +       __u64 fa_flags;
> > +       struct timespec64 fa_atime;             /* Access time */
> > +       struct timespec64 fa_mtime;             /* Modify time */
> > +       struct timespec64 fa_ctime;             /* Change time */
> > +       kuid_t          fa_owner_uid;           /* Owner User ID */
> > +       kgid_t          fa_group_gid;           /* Primary Group ID
> > */
> > +        /* Normal stat fields after this */
> > +       __u32           fa_mode;                /* Mode */
> > +       unsigned int    fa_nlink;
> > +       __u32           fa_blksize;
> > +       __u32           fa_spare;               /* Alignment */
> > +       __u64           fa_ino;
> > +       dev_t           fa_dev;
> > +       dev_t           fa_rdev;
> > +       loff_t          fa_size;
> > +       __u64           fa_blocks;
> > +};
> > +
> >  struct nfs4_add_xprt_data {
> >         struct nfs_client       *clp;
> >         const struct cred       *cred;
> > @@ -315,6 +343,9 @@ extern int nfs4_set_rw_stateid(nfs4_stateid
> > *stateid,
> >                 const struct nfs_open_context *ctx,
> >                 const struct nfs_lock_context *l_ctx,
> >                 fmode_t fmode);
> > +int nfs4_set_nfs4_statx(struct inode *inode,
> > +               struct nfs4_statx *statx,
> > +               struct nfs_fattr *fattr);
> >  extern int nfs4_proc_getattr(struct nfs_server *server, struct
> > nfs_fh *fhandle,
> >                              struct nfs_fattr *fattr, struct inode
> > *inode);
> >  extern int update_open_stateid(struct nfs4_state *state,
> > diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c
> > index e79ae4cbc395..494ebc7cd1c0 100644
> > --- a/fs/nfs/nfs4file.c
> > +++ b/fs/nfs/nfs4file.c
> > @@ -9,6 +9,8 @@
> >  #include <linux/falloc.h>
> >  #include <linux/mount.h>
> >  #include <linux/nfs_fs.h>
> > +#include <linux/time32.h>
> > +#include <linux/compat.h>
> >  #include <linux/nfs_ssc.h>
> >  #include "delegation.h"
> >  #include "internal.h"
> > @@ -132,6 +134,503 @@ nfs4_file_flush(struct file *file, fl_owner_t
> > id)
> >         return filemap_check_wb_err(file->f_mapping, since);
> >  }
> >  
> > +static int nfs_get_timespec64(struct timespec64 *ts,
> > +                             const struct nfs_ioctl_timespec
> > __user *uts)
> > +{
> > +       __s64 dummy;
> > +       if (unlikely(get_user(dummy, &uts->tv_sec) != 0))
> > +               return EFAULT;
> > +       ts->tv_sec = dummy;
> > +       if (unlikely(get_user(dummy, &uts->tv_nsec) != 0))
> > +               return EFAULT;
> > +       ts->tv_nsec = dummy;
> > +       return 0;
> > +}
> > +
> > +static int nfs_put_timespec64(const struct timespec64 *ts,
> > +                             struct nfs_ioctl_timespec __user
> > *uts)
> > +{
> > +       __s64 dummy;
> > +
> > +       dummy = ts->tv_sec;
> > +       if (unlikely(put_user(dummy, &uts->tv_sec) != 0))
> > +               return EFAULT;
> > +       dummy = ts->tv_nsec;
> > +       if (unlikely(put_user(dummy, &uts->tv_nsec) != 0))
> > +               return EFAULT;
> > +       return 0;
> > +}
> > +
> > +static struct file *nfs4_get_real_file(struct file *src, unsigned
> > int fd)
> > +{
> > +       struct file *filp = fget_raw(fd);
> > +       int ret = -EBADF;
> > +
> > +       if (!filp)
> > +               goto out;
> > +       /* Validate that the files share the same underlying
> > filesystem */
> > +       ret = -EXDEV;
> > +       if (file_inode(filp)->i_sb != file_inode(src)->i_sb)
> > +               goto out_put;
> > +       return filp;
> > +out_put:
> > +       fput(filp);
> > +out:
> > +       return ERR_PTR(ret);
> > +}
> > +
> > +static unsigned long nfs4_statx_request_to_cache_validity(__u64
> > request,
> > +                                                         u64
> > fattr_supported)
> > +{
> > +       unsigned long ret = 0;
> > +
> > +       if (request & NFS_FA_VALID_ATIME)
> > +               ret |= NFS_INO_INVALID_ATIME;
> > +       if (request & NFS_FA_VALID_CTIME)
> > +               ret |= NFS_INO_INVALID_CTIME;
> > +       if (request & NFS_FA_VALID_MTIME)
> > +               ret |= NFS_INO_INVALID_MTIME;
> > +       if (request & NFS_FA_VALID_SIZE)
> > +               ret |= NFS_INO_INVALID_SIZE;
> > +
> > +       if (request & NFS_FA_VALID_MODE)
> > +               ret |= NFS_INO_INVALID_MODE;
> > +       if (request & (NFS_FA_VALID_OWNER |
> > NFS_FA_VALID_OWNER_GROUP))
> > +               ret |= NFS_INO_INVALID_OTHER;
> > +
> > +       if (request & NFS_FA_VALID_NLINK)
> > +               ret |= NFS_INO_INVALID_NLINK;
> > +       if (request & NFS_FA_VALID_BLOCKS)
> > +               ret |= NFS_INO_INVALID_BLOCKS;
> > +
> > +       if (request & NFS_FA_VALID_TIME_CREATE)
> > +               ret |= NFS_INO_INVALID_BTIME;
> > +
> > +       if (request & NFS_FA_VALID_ARCHIVE) {
> > +               if (fattr_supported & NFS_ATTR_FATTR_ARCHIVE)
> > +                       ret |= NFS_INO_INVALID_WINATTR;
> > +               else if (fattr_supported &
> > NFS_ATTR_FATTR_TIME_BACKUP)
> > +                       ret |= NFS_INO_INVALID_WINATTR |
> > NFS_INO_INVALID_MTIME;
> > +       }
> > +       if (request & (NFS_FA_VALID_TIME_BACKUP |
> > NFS_FA_VALID_HIDDEN |
> > +                      NFS_FA_VALID_SYSTEM | NFS_FA_VALID_OFFLINE))
> > +               ret |= NFS_INO_INVALID_WINATTR;
> > +
> > +       return ret ? (ret | NFS_INO_INVALID_CHANGE) : 0;
> > +}
> > +
> > +static long nfs4_ioctl_file_statx_get(struct file *dst_file,
> > +                                     struct nfs_ioctl_nfs4_statx
> > __user *uarg)
> > +{
> > +       struct nfs4_statx args = {
> > +               .real_fd = -1,
> > +               .fa_valid = { 0 },
> > +       };
> > +       struct inode *inode;
> > +       struct nfs_inode *nfsi;
> > +       struct nfs_server *server;
> > +       u64 fattr_supported;
> > +       unsigned long reval_attr;
> > +       unsigned int reval_flags;
> > +       __u32 tmp;
> > +       int ret;
> > +
> > +       /*
> > +        * We get the first word from the uarg as it tells us
> > whether
> > +        * to use the passed in struct file or use that fd to find
> > the
> > +        * struct file.
> > +        */
> > +       if (get_user(args.real_fd, &uarg->real_fd))
> > +               return -EFAULT;
> > +
> > +       if (get_user(args.fa_options, &uarg->fa_options))
> > +               return -EFAULT;
> > +
> > +       if (get_user(args.fa_request[0], &uarg->fa_request[0]))
> > +               return -EFAULT;
> > +
> > +       if (args.real_fd >= 0) {
> > +               dst_file = nfs4_get_real_file(dst_file,
> > args.real_fd);
> > +               if (IS_ERR(dst_file))
> > +                       return PTR_ERR(dst_file);
> > +       }
> > +
> > +       /*
> > +        * Backward compatibility: we stole the top 32 bits of
> > 'real_fd'
> > +        * to create the fa_options field, so if its value is -1,
> > then
> > +        * assume it is the high word of (__s64)real_fd == -1, and
> > just
> > +        * set it to zero.
> > +        */
> > +       if (args.fa_options == 0xFFFF)
> > +               args.fa_options = 0;
> > +
> > +       inode = file_inode(dst_file);
> > +       nfsi = NFS_I(inode);
> > +       server = NFS_SERVER(inode);
> > +       fattr_supported = server->fattr_valid;
> > +
> > +       trace_nfs_ioctl_file_statx_get_enter(inode);
> > +
> > +       if (args.fa_options & NFS_FA_OPTIONS_FORCE_SYNC)
> > +               reval_flags = AT_STATX_FORCE_SYNC;
> > +       else if (args.fa_options & NFS_FA_OPTIONS_DONT_SYNC)
> > +               reval_flags = AT_STATX_DONT_SYNC;
> > +       else
> > +               reval_flags = AT_STATX_SYNC_AS_STAT;
> > +
> > +       reval_attr =
> > nfs4_statx_request_to_cache_validity(args.fa_request[0],
> > +                                                        
> > fattr_supported);
> > +
> > +       if ((reval_attr & (NFS_INO_INVALID_CTIME |
> > NFS_INO_INVALID_MTIME)) &&
> > +           reval_flags != AT_STATX_DONT_SYNC && S_ISREG(inode-
> > >i_mode)) {
> > +               ret = filemap_write_and_wait(inode->i_mapping);
> > +               if (ret)
> > +                       goto out;
> > +       }
> > +
> > +       if ((dst_file->f_path.mnt->mnt_flags & MNT_NOATIME) ||
> > +           ((dst_file->f_path.mnt->mnt_flags & MNT_NODIRATIME) &&
> > +            S_ISDIR(inode->i_mode)))
> > +               reval_attr &= ~NFS_INO_INVALID_ATIME;
> > +
> > +       ret = nfs_getattr_revalidate(&dst_file->f_path, reval_attr,
> > +                                    reval_flags);
> > +       if (ret != 0)
> > +               goto out;
> > +
> > +       ret = -EFAULT;
> > +       if ((fattr_supported & NFS_ATTR_FATTR_OWNER) &&
> > +           (args.fa_request[0] & NFS_FA_VALID_OWNER)) {
> > +               tmp = from_kuid_munged(current_user_ns(), inode-
> > >i_uid);
> > +               if (unlikely(put_user(tmp, &uarg->fa_owner_uid) !=
> > 0))
> > +                       goto out;
> > +               args.fa_valid[0] |= NFS_FA_VALID_OWNER;
> > +       }
> > +
> > +       if ((fattr_supported & NFS_ATTR_FATTR_GROUP) &&
> > +           (args.fa_request[0] & NFS_FA_VALID_OWNER_GROUP)) {
> > +               tmp = from_kgid_munged(current_user_ns(), inode-
> > >i_gid);
> > +               if (unlikely(put_user(tmp, &uarg->fa_group_gid) !=
> > 0))
> > +                       goto out;
> > +               args.fa_valid[0] |= NFS_FA_VALID_OWNER_GROUP;
> > +       }
> > +
> > +       if ((fattr_supported & NFS_ATTR_FATTR_TIME_BACKUP) &&
> > +           (args.fa_request[0] & NFS_FA_VALID_TIME_BACKUP)) {
> > +               if (nfs_put_timespec64(&nfsi->timebackup, &uarg-
> > >fa_time_backup))
> > +                       goto out;
> > +               args.fa_valid[0] |= NFS_FA_VALID_TIME_BACKUP;
> > +       }
> > +
> > +       if ((fattr_supported & NFS_ATTR_FATTR_BTIME) &&
> > +           (args.fa_request[0] & NFS_FA_VALID_TIME_CREATE)) {
> > +               if (nfs_put_timespec64(&nfsi->btime, &uarg-
> > >fa_btime))
> > +                       goto out;
> > +               args.fa_valid[0] |= NFS_FA_VALID_TIME_CREATE;
> > +       }
> > +
> > +       /* atime, mtime, and ctime are all stored in the regular
> > inode,
> > +        * not the nfs inode.
> > +        */
> > +       if ((fattr_supported & NFS_ATTR_FATTR_ATIME) &&
> > +           (args.fa_request[0] & NFS_FA_VALID_ATIME)) {
> > +               if (nfs_put_timespec64(&inode->i_atime, &uarg-
> > >fa_atime))
> > +                       goto out;
> > +               args.fa_valid[0] |= NFS_FA_VALID_ATIME;
> > +       }
> > +
> > +       if ((fattr_supported & NFS_ATTR_FATTR_MTIME) &&
> > +           (args.fa_request[0] & NFS_FA_VALID_MTIME)) {
> > +               if (nfs_put_timespec64(&inode->i_mtime, &uarg-
> > >fa_mtime))
> > +                        goto out;
> > +               args.fa_valid[0] |= NFS_FA_VALID_MTIME;
> > +       }
> > +
> > +       if ((fattr_supported & NFS_ATTR_FATTR_CTIME) &&
> > +           (args.fa_request[0] & NFS_FA_VALID_CTIME)) {
> > +               if (nfs_put_timespec64(&inode->i_ctime, &uarg-
> > >fa_ctime))
> > +                       goto out;
> > +               args.fa_valid[0] |= NFS_FA_VALID_CTIME;
> > +       }
> > +
> > +        /*
> > +         * It looks like PDFS does not support or properly handle
> > the
> > +         * archive bit.
> > +         */
> > +       if ((fattr_supported & NFS_ATTR_FATTR_ARCHIVE) &&
> > +           (args.fa_request[0] & NFS_FA_VALID_ARCHIVE)) {
> > +               if (nfsi->archive)
> > +                       args.fa_flags |= NFS_FA_FLAG_ARCHIVE;
> > +               args.fa_valid[0] |= NFS_FA_VALID_ARCHIVE;
> > +       }
> > +
> > +       if ((fattr_supported & NFS_ATTR_FATTR_TIME_BACKUP) &&
> > +           (args.fa_request[0] & NFS_FA_VALID_ARCHIVE)) {
> > +               if (timespec64_compare(&inode->i_mtime, &nfsi-
> > >timebackup) > 0)
> > +                       args.fa_flags |= NFS_FA_FLAG_ARCHIVE;
> > +               args.fa_valid[0] |= NFS_FA_VALID_ARCHIVE;
> > +       }
> > +
> > +       if ((fattr_supported & NFS_ATTR_FATTR_HIDDEN) &&
> > +           (args.fa_request[0] & NFS_FA_VALID_HIDDEN)) {
> > +               if (nfsi->hidden)
> > +                       args.fa_flags |= NFS_FA_FLAG_HIDDEN;
> > +               args.fa_valid[0] |= NFS_FA_VALID_HIDDEN;
> > +       }
> > +       if ((fattr_supported & NFS_ATTR_FATTR_SYSTEM) &&
> > +           (args.fa_request[0] & NFS_FA_VALID_SYSTEM)) {
> > +               if (nfsi->system)
> > +                       args.fa_flags |= NFS_FA_FLAG_SYSTEM;
> > +               args.fa_valid[0] |= NFS_FA_VALID_SYSTEM;
> > +       }
> > +
> > +       if ((fattr_supported & NFS_ATTR_FATTR_OFFLINE) &&
> > +           (args.fa_request[0] & NFS_FA_VALID_OFFLINE)) {
> > +               if (nfsi->offline)
> > +                       args.fa_flags |= NFS_FA_FLAG_OFFLINE;
> > +               args.fa_valid[0] |= NFS_FA_VALID_OFFLINE;
> > +       }
> > +
> > +       if ((args.fa_valid[0] & (NFS_FA_VALID_ARCHIVE |
> > +                               NFS_FA_VALID_HIDDEN |
> > +                               NFS_FA_VALID_SYSTEM |
> > +                               NFS_FA_VALID_OFFLINE)) &&
> > +           put_user(args.fa_flags, &uarg->fa_flags))
> > +               goto out;
> > +
> > +       if ((fattr_supported & NFS_ATTR_FATTR_MODE) &&
> > +           (args.fa_request[0] & NFS_FA_VALID_MODE)) {
> > +               tmp = inode->i_mode;
> > +               /* This is an unsigned short we put into an __u32
> > */
> > +               if (unlikely(put_user(tmp, &uarg->fa_mode) != 0))
> > +                       goto out;
> > +               args.fa_valid[0] |= NFS_FA_VALID_MODE;
> > +       }
> > +
> > +       if ((fattr_supported & NFS_ATTR_FATTR_NLINK) &&
> > +           (args.fa_request[0] & NFS_FA_VALID_NLINK)) {
> > +               tmp = inode->i_nlink;
> > +               if (unlikely(put_user(tmp, &uarg->fa_nlink) != 0))
> > +                       goto out;
> > +               args.fa_valid[0] |= NFS_FA_VALID_NLINK;
> > +       }
> > +
> > +       if (args.fa_request[0] & NFS_FA_VALID_BLKSIZE) {
> > +               tmp = i_blocksize(inode);
> > +               if (S_ISDIR(inode->i_mode))
> > +                       tmp = NFS_SERVER(inode)->dtsize;
> > +               if (unlikely(put_user(tmp, &uarg->fa_blksize) !=
> > 0))
> > +                       goto out;
> > +               args.fa_valid[0] |= NFS_FA_VALID_BLKSIZE;
> > +       }
> > +
> > +       if (args.fa_request[0] & NFS_FA_VALID_INO) {
> > +               __u64 ino =
> > nfs_compat_user_ino64(NFS_FILEID(inode));
> > +               if (unlikely(put_user(ino, &uarg->fa_ino) != 0))
> > +                       goto out;
> > +               args.fa_valid[0] |= NFS_FA_VALID_INO;
> > +       }
> > +
> > +       if (args.fa_request[0] & NFS_FA_VALID_DEV) {
> > +               tmp = inode->i_sb->s_dev;
> > +               if (unlikely(put_user(tmp, &uarg->fa_dev) != 0))
> > +                       goto out;
> > +               args.fa_valid[0] |= NFS_FA_VALID_DEV;
> > +       }
> > +
> > +       if ((fattr_supported & NFS_ATTR_FATTR_RDEV) &&
> > +           (args.fa_request[0] & NFS_FA_VALID_RDEV)) {
> > +               tmp = inode->i_rdev;
> > +               if (unlikely(put_user(tmp, &uarg->fa_rdev) != 0))
> > +                       goto out;
> > +               args.fa_valid[0] |= NFS_FA_VALID_RDEV;
> > +       }
> > +
> > +       if ((fattr_supported & NFS_ATTR_FATTR_SIZE) &&
> > +           (args.fa_request[0] & NFS_FA_VALID_SIZE)) {
> > +               __s64 size = i_size_read(inode);
> > +               if (unlikely(put_user(size, &uarg->fa_size) != 0))
> > +                       goto out;
> > +               args.fa_valid[0] |= NFS_FA_VALID_SIZE;
> > +       }
> > +
> > +       if ((fattr_supported &
> > +            (NFS_ATTR_FATTR_BLOCKS_USED |
> > NFS_ATTR_FATTR_SPACE_USED)) &&
> > +           (args.fa_request[0] & NFS_FA_VALID_BLOCKS)) {
> > +               __s64 blocks = inode->i_blocks;
> > +               if (unlikely(put_user(blocks, &uarg->fa_blocks) !=
> > 0))
> > +                       goto out;
> > +               args.fa_valid[0] |= NFS_FA_VALID_BLOCKS;
> > +       }
> > +
> > +       if (unlikely(put_user(args.fa_valid[0], &uarg->fa_valid[0])
> > != 0))
> > +               goto out;
> > +       if (unlikely(put_user(args.fa_valid[1], &uarg->fa_valid[1])
> > != 0))
> > +               goto out;
> > +
> > +       ret = 0;
> > +out:
> > +       if (args.real_fd >= 0)
> > +               fput(dst_file);
> > +       trace_nfs_ioctl_file_statx_get_exit(inode, ret);
> > +       return ret;
> > +}
> > +
> > +static long nfs4_ioctl_file_statx_set(struct file *dst_file,
> > +                                     struct nfs_ioctl_nfs4_statx
> > __user *uarg)
> > +{
> > +       struct nfs4_statx args = {
> > +               .real_fd = -1,
> > +               .fa_valid = { 0 },
> > +       };
> > +       struct nfs_fattr *fattr = nfs_alloc_fattr();
> > +       struct inode *inode;
> > +       /*
> > +        * If you need a different error code below, you need to
> > set it
> > +        */
> > +       int ret = -EFAULT;
> > +
> > +       if (fattr == NULL)
> > +               return -ENOMEM;
> > +
> > +       /*
> > +        * We get the first u64 word from the uarg as it tells us
> > whether
> > +        * to use the passed in struct file or use that fd to find
> > the
> > +        * struct file.
> > +        */
> > +       if (get_user(args.real_fd, &uarg->real_fd))
> > +               goto out_free;
> > +
> > +       if (args.real_fd >= 0) {
> > +               dst_file = nfs4_get_real_file(dst_file,
> > args.real_fd);
> > +               if (IS_ERR(dst_file)) {
> > +                       ret = PTR_ERR(dst_file);
> > +                       goto out_free;
> > +               }
> > +       }
> > +       inode = file_inode(dst_file);
> > +       trace_nfs_ioctl_file_statx_set_enter(inode);
> > +
> > +       inode_lock(inode);
> > +
> > +       /* Write all dirty data */
> > +       if (S_ISREG(inode->i_mode)) {
> > +               ret = nfs_sync_inode(inode);
> > +               if (ret)
> > +                       goto out;
> > +       }
> > +
> > +       ret = -EFAULT;
> > +       if (get_user(args.fa_valid[0], &uarg->fa_valid[0]))
> > +               goto out;
> > +       args.fa_valid[0] &= NFS_FA_VALID_ALL_ATTR_0;
> > +
> > +       if (args.fa_valid[0] & NFS_FA_VALID_OWNER) {
> > +               uid_t uid;
> > +
> > +               if (unlikely(get_user(uid, &uarg->fa_owner_uid) !=
> > 0))
> > +                       goto out;
> > +               args.fa_owner_uid = make_kuid(current_user_ns(),
> > uid);
> > +               if (!uid_valid(args.fa_owner_uid)) {
> > +                       ret = -EINVAL;
> > +                       goto out;
> > +               }
> > +       }
> > +
> > +       if (args.fa_valid[0] & NFS_FA_VALID_OWNER_GROUP) {
> > +               gid_t gid;
> > +
> > +               if (unlikely(get_user(gid, &uarg->fa_group_gid) !=
> > 0))
> > +                       goto out;
> > +               args.fa_group_gid = make_kgid(current_user_ns(),
> > gid);
> > +               if (!gid_valid(args.fa_group_gid)) {
> > +                       ret = -EINVAL;
> > +                       goto out;
> > +               }
> > +       }
> > +
> > +       if ((args.fa_valid[0] & (NFS_FA_VALID_ARCHIVE |
> > +                                       NFS_FA_VALID_HIDDEN |
> > +                                       NFS_FA_VALID_SYSTEM)) &&
> > +           get_user(args.fa_flags, &uarg->fa_flags))
> > +               goto out;
> > +
> > +       if ((args.fa_valid[0] & NFS_FA_VALID_TIME_CREATE) &&
> > +           nfs_get_timespec64(&args.fa_btime, &uarg->fa_btime))
> > +               goto out;
> > +
> > +       if ((args.fa_valid[0] & NFS_FA_VALID_ATIME) &&
> > +           nfs_get_timespec64(&args.fa_atime, &uarg->fa_atime))
> > +               goto out;
> > +
> > +       if ((args.fa_valid[0] & NFS_FA_VALID_MTIME) &&
> > +           nfs_get_timespec64(&args.fa_mtime, &uarg->fa_mtime))
> > +               goto out;
> > +
> > +       if (args.fa_valid[0] & NFS_FA_VALID_TIME_BACKUP) {
> > +               if (nfs_get_timespec64(&args.fa_time_backup, &uarg-
> > >fa_time_backup))
> > +                       goto out;
> > +       } else if ((args.fa_valid[0] & NFS_FA_VALID_ARCHIVE) &&
> > +                       !(NFS_SERVER(inode)->fattr_valid &
> > NFS_ATTR_FATTR_ARCHIVE)) {
> > +               args.fa_valid[0] |= NFS_FA_VALID_TIME_BACKUP;
> > +               if (!(args.fa_flags & NFS_FA_FLAG_ARCHIVE)) {
> > +                       nfs_revalidate_inode(inode,
> > NFS_INO_INVALID_MTIME);
> > +                       args.fa_time_backup.tv_sec = inode-
> > >i_mtime.tv_sec;
> > +                       args.fa_time_backup.tv_nsec = inode-
> > >i_mtime.tv_nsec;
> > +               } else if (args.fa_valid[0] &
> > NFS_FA_VALID_TIME_CREATE)
> > +                       args.fa_time_backup = args.fa_btime;
> > +               else {
> > +                       nfs_revalidate_inode(inode,
> > NFS_INO_INVALID_BTIME);
> > +                       args.fa_time_backup = NFS_I(inode)->btime;
> > +               }
> > +       }
> > +
> > +        if (args.fa_valid[0] & NFS_FA_VALID_SIZE) {
> > +               if (copy_from_user(&args.fa_size, &uarg->fa_size,
> > +                                       sizeof(args.fa_size)))
> > +                       goto out;
> > +               ret = inode_newsize_ok(inode,args.fa_size);
> > +               if (ret)
> > +                       goto out;
> > +               if (args.fa_size == i_size_read(inode))
> > +                       args.fa_valid[0] &= ~NFS_FA_VALID_SIZE;
> > +       }
> > +
> > +       /*
> > +        * No need to update the inode because that is done in
> > nfs4_set_nfs4_statx
> > +        */
> > +       ret = nfs4_set_nfs4_statx(inode, &args, fattr);
> > +
> > +out:
> > +       inode_unlock(inode);
> > +       if (args.real_fd >= 0)
> > +               fput(dst_file);
> > +       trace_nfs_ioctl_file_statx_set_exit(inode, ret);
> > +out_free:
> > +       nfs_free_fattr(fattr);
> > +       return ret;
> > +}
> > +
> > +static long nfs4_ioctl(struct file *file, unsigned int cmd,
> > unsigned long arg)
> > +{
> > +       void __user *argp = (void __user *)arg;
> > +       long ret;
> > +
> > +       switch (cmd) {
> > +       case NFS_IOC_FILE_STATX_GET:
> > +               ret = nfs4_ioctl_file_statx_get(file, argp);
> > +               break;
> > +       case NFS_IOC_FILE_STATX_SET:
> > +               ret = nfs4_ioctl_file_statx_set(file, argp);
> > +               break;
> > +       default:
> > +               ret = -ENOIOCTLCMD;
> > +       }
> > +
> > +       dprintk("%s: file=%pD2, cmd=%u, ret=%ld\n", __func__, file,
> > cmd, ret);
> > +       return ret;
> > +}
> > +
> >  #ifdef CONFIG_NFS_V4_2
> >  static ssize_t __nfs4_copy_file_range(struct file *file_in, loff_t
> > pos_in,
> >                                       struct file *file_out, loff_t
> > pos_out,
> > @@ -187,6 +686,7 @@ static ssize_t __nfs4_copy_file_range(struct
> > file *file_in, loff_t pos_in,
> >         return ret;
> >  }
> >  
> > +
> >  static ssize_t nfs4_copy_file_range(struct file *file_in, loff_t
> > pos_in,
> >                                     struct file *file_out, loff_t
> > pos_out,
> >                                     size_t count, unsigned int
> > flags)
> > @@ -461,4 +961,15 @@ const struct file_operations
> > nfs4_file_operations = {
> >  #else
> >         .llseek         = nfs_file_llseek,
> >  #endif
> > +       .unlocked_ioctl = nfs4_ioctl,
> > +};
> > +
> > +const struct file_operations nfs4_dir_operations = {
> > +       .llseek         = nfs_llseek_dir,
> > +       .read           = generic_read_dir,
> > +       .iterate_shared = nfs_readdir,
> > +       .open           = nfs_opendir,
> > +       .release        = nfs_closedir,
> > +       .fsync          = nfs_fsync_dir,
> > +       .unlocked_ioctl = nfs4_ioctl,
> >  };
> > diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
> > index d497616ca149..7c032583ffa2 100644
> > --- a/fs/nfs/nfs4proc.c
> > +++ b/fs/nfs/nfs4proc.c
> > @@ -7959,6 +7959,129 @@ static int _nfs41_proc_get_locations(struct
> > inode *inode,
> >  
> >  #endif /* CONFIG_NFS_V4_1 */
> >  
> > +static int _nfs4_set_nfs4_statx(struct inode *inode,
> > +               struct nfs4_statx *statx,
> > +               struct nfs_fattr *fattr)
> > +{
> > +       const __u64 statx_win = NFS_FA_VALID_TIME_CREATE |
> > +                               NFS_FA_VALID_TIME_BACKUP |
> > +                               NFS_FA_VALID_ARCHIVE |
> > NFS_FA_VALID_HIDDEN |
> > +                               NFS_FA_VALID_SYSTEM;
> > +       struct iattr sattr = {0};
> > +       struct nfs_server *server = NFS_SERVER(inode);
> > +       __u32 bitmask[3];
> > +       struct nfs_setattrargs arg = {
> > +               .fh             = NFS_FH(inode),
> > +               .iap            = &sattr,
> > +               .server         = server,
> > +               .bitmask        = bitmask,
> > +               .statx          = statx,
> > +       };
> > +       struct nfs_setattrres res = {
> > +               .fattr          = fattr,
> > +               .server         = server,
> > +       };
> > +       struct rpc_message msg = {
> > +               .rpc_proc       =
> > &nfs4_procedures[NFSPROC4_CLNT_SETATTR],
> > +               .rpc_argp       = &arg,
> > +               .rpc_resp       = &res,
> > +       };
> > +       int status;
> > +
> > +       nfs4_bitmap_copy_adjust(
> > +               bitmask, server->attr_bitmask, inode,
> > +               NFS_INO_INVALID_CHANGE | NFS_INO_INVALID_CTIME |
> > +                       NFS_INO_INVALID_SIZE |
> > NFS_INO_INVALID_OTHER |
> > +                       NFS_INO_INVALID_BTIME |
> > NFS_INO_INVALID_WINATTR);
> > +       /* Use the iattr structure to set atime and mtime since
> > handling already
> > +        * exists for them using the iattr struct in the
> > encode_attrs()
> > +        * (xdr encoding) routine.
> > +        */
> > +       if (statx && (statx->fa_valid[0] & NFS_FA_VALID_MTIME)) {
> > +               sattr.ia_valid |= ATTR_MTIME_SET;
> > +               sattr.ia_mtime.tv_sec = statx->fa_mtime.tv_sec;
> > +               sattr.ia_mtime.tv_nsec = statx->fa_mtime.tv_nsec;
> > +       }
> > +
> > +       if (statx && (statx->fa_valid[0] & NFS_FA_VALID_ATIME)) {
> > +               sattr.ia_valid |= ATTR_ATIME_SET;
> > +               sattr.ia_atime.tv_sec = statx->fa_atime.tv_sec;
> > +               sattr.ia_atime.tv_nsec = statx->fa_atime.tv_nsec;
> > +       }
> > +
> > +       if (statx && (statx->fa_valid[0] & NFS_FA_VALID_OWNER)) {
> > +               sattr.ia_valid |= ATTR_UID;
> > +               sattr.ia_uid = statx->fa_owner_uid;
> > +       }
> > +
> > +       if (statx && (statx->fa_valid[0] &
> > NFS_FA_VALID_OWNER_GROUP)) {
> > +               sattr.ia_valid |= ATTR_GID;
> > +               sattr.ia_gid = statx->fa_group_gid;
> > +       }
> > +
> > +       if (statx && (statx->fa_valid[0] & NFS_FA_VALID_SIZE)) {
> > +               sattr.ia_valid |= ATTR_SIZE;
> > +               sattr.ia_size = statx->fa_size;
> > +       }
> > +
> > +       nfs4_stateid_copy(&arg.stateid, &zero_stateid);
> > +
> > +       status = nfs4_call_sync(server->client, server, &msg,
> > &arg.seq_args, &res.seq_res, 1);
> > +       if (!status) {
> > +               if (statx->fa_valid[0] & statx_win) {
> > +                       struct nfs_inode *nfsi = NFS_I(inode);
> > +
> > +                       spin_lock(&inode->i_lock);
> > +                       if (statx->fa_valid[0] &
> > NFS_FA_VALID_TIME_CREATE)
> > +                               nfsi->btime = statx->fa_btime;
> > +                       if (statx->fa_valid[0] &
> > NFS_FA_VALID_TIME_BACKUP)
> > +                               nfsi->timebackup = statx-
> > >fa_time_backup;
> > +                       if (statx->fa_valid[0] &
> > NFS_FA_VALID_ARCHIVE)
> > +                               nfsi->archive = (statx->fa_flags &
> > +                                               
> > NFS_FA_FLAG_ARCHIVE) != 0;
> > +                       if (statx->fa_valid[0] &
> > NFS_FA_VALID_SYSTEM)
> > +                               nfsi->system = (statx->fa_flags &
> > +                                               NFS_FA_FLAG_SYSTEM)
> > != 0;
> > +                       if (statx->fa_valid[0] &
> > NFS_FA_VALID_HIDDEN)
> > +                               nfsi->hidden = (statx->fa_flags &
> > +                                               NFS_FA_FLAG_HIDDEN)
> > != 0;
> > +                       if (statx->fa_valid[0] &
> > NFS_FA_VALID_OFFLINE)
> > +                               nfsi->offline = (statx->fa_flags &
> > +                                               
> > NFS_FA_FLAG_OFFLINE) != 0;
> > +
> > +                       nfsi->cache_validity &=
> > ~NFS_INO_INVALID_CTIME;
> > +                       if (fattr->valid & NFS_ATTR_FATTR_CTIME)
> > +                               inode->i_ctime = fattr->ctime;
> > +                       else
> > +                               nfs_set_cache_invalid(
> > +                                       inode,
> > NFS_INO_INVALID_CHANGE |
> > +                                                 
> > NFS_INO_INVALID_CTIME);
> > +                       spin_unlock(&inode->i_lock);
> > +               }
> > +
> > +               nfs_setattr_update_inode(inode, &sattr, fattr);
> > +       } else
> > +               dprintk("%s failed: %d\n", __func__, status);
> > +
> > +       return status;
> > +}
> > +
> > +int nfs4_set_nfs4_statx(struct inode *inode,
> > +               struct nfs4_statx *statx,
> > +               struct nfs_fattr *fattr)
> > +{
> > +       struct nfs4_exception exception = { };
> > +       struct nfs_server *server = NFS_SERVER(inode);
> > +       int err;
> > +
> > +       do {
> > +               err = nfs4_handle_exception(server,
> > +                               _nfs4_set_nfs4_statx(inode, statx,
> > fattr),
> > +                               &exception);
> > +       } while (exception.retry);
> > +       return err;
> > +}
> > +
> >  /**
> >   * nfs4_proc_get_locations - discover locations for a migrated
> > FSID
> >   * @inode: inode on FSID that is migrating
> > @@ -10419,6 +10542,7 @@ const struct nfs_rpc_ops nfs_v4_clientops =
> > {
> >         .dir_inode_ops  = &nfs4_dir_inode_operations,
> >         .file_inode_ops = &nfs4_file_inode_operations,
> >         .file_ops       = &nfs4_file_operations,
> > +       .dir_ops        = &nfs4_dir_operations,
> >         .getroot        = nfs4_proc_get_root,
> >         .submount       = nfs4_submount,
> >         .try_get_tree   = nfs4_try_get_tree,
> > diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
> > index d2c240effc87..e5300d7ed712 100644
> > --- a/fs/nfs/nfs4xdr.c
> > +++ b/fs/nfs/nfs4xdr.c
> > @@ -129,12 +129,15 @@ static int decode_layoutget(struct xdr_stream
> > *xdr, struct rpc_rqst *req,
> >                                 nfs4_fattr_value_maxsz)
> >  #define decode_getattr_maxsz    (op_decode_hdr_maxsz +
> > nfs4_fattr_maxsz)
> >  #define encode_attrs_maxsz     (nfs4_fattr_bitmap_maxsz + \
> > -                                1 + 2 + 1 + \
> > +                                1 + 2 + 1 + 1 + 1 + \
> >                                 nfs4_owner_maxsz + \
> >                                 nfs4_group_maxsz + \
> > -                               nfs4_label_maxsz + \
> > +                               1 + \
> > +                               1 + nfstime4_maxsz + \
> > +                               nfstime4_maxsz + nfstime4_maxsz + \
> >                                 1 + nfstime4_maxsz + \
> > -                               1 + nfstime4_maxsz)
> > +                               nfs4_label_maxsz + \
> > +                               2)
> >  #define encode_savefh_maxsz     (op_encode_hdr_maxsz)
> >  #define decode_savefh_maxsz     (op_decode_hdr_maxsz)
> >  #define encode_restorefh_maxsz  (op_encode_hdr_maxsz)
> > @@ -1081,6 +1084,7 @@ xdr_encode_nfstime4(__be32 *p, const struct
> > timespec64 *t)
> >  static void encode_attrs(struct xdr_stream *xdr, const struct
> > iattr *iap,
> >                                 const struct nfs4_label *label,
> >                                 const umode_t *umask,
> > +                               const struct nfs4_statx *statx,
> >                                 const struct nfs_server *server,
> >                                 const uint32_t attrmask[])
> >  {
> > @@ -1153,6 +1157,34 @@ static void encode_attrs(struct xdr_stream
> > *xdr, const struct iattr *iap,
> >                 }
> >         }
> >  
> > +       if (statx && (statx->fa_valid[0] &
> > NFS_FA_VALID_TIME_BACKUP) &&
> > +           (attrmask[1] & FATTR4_WORD1_TIME_BACKUP)) {
> > +               bmval[1] |= FATTR4_WORD1_TIME_BACKUP;
> > +               len += (nfstime4_maxsz << 2);
> > +       }
> > +       if (statx && (statx->fa_valid[0] &
> > NFS_FA_VALID_TIME_CREATE) &&
> > +           (attrmask[1] & FATTR4_WORD1_TIME_CREATE)) {
> > +               bmval[1] |= FATTR4_WORD1_TIME_CREATE;
> > +               len += (nfstime4_maxsz << 2);
> > +       }
> > +
> > +       if (statx && (statx->fa_valid[0] & NFS_FA_VALID_ARCHIVE) &&
> > +          (attrmask[0] & FATTR4_WORD0_ARCHIVE)) {
> > +               bmval[0] |= FATTR4_WORD0_ARCHIVE;
> > +               len += 4;
> > +       }
> > +       if (statx && (statx->fa_valid[0] & NFS_FA_VALID_HIDDEN) &&
> > +          (attrmask[0] & FATTR4_WORD0_HIDDEN)) {
> > +               bmval[0] |= FATTR4_WORD0_HIDDEN;
> > +               len += 4;
> > +       }
> > +
> > +       if (statx && (statx->fa_valid[0] & NFS_FA_VALID_SYSTEM) &&
> > +          (attrmask[1] & FATTR4_WORD1_SYSTEM)) {
> > +               bmval[1] |= FATTR4_WORD1_SYSTEM;
> > +               len += 4;
> > +       }
> > +
> >         if (label && (attrmask[2] & FATTR4_WORD2_SECURITY_LABEL)) {
> >                 len += 4 + 4 + 4 + (XDR_QUADLEN(label->len) << 2);
> >                 bmval[2] |= FATTR4_WORD2_SECURITY_LABEL;
> > @@ -1163,12 +1195,21 @@ static void encode_attrs(struct xdr_stream
> > *xdr, const struct iattr *iap,
> >  
> >         if (bmval[0] & FATTR4_WORD0_SIZE)
> >                 p = xdr_encode_hyper(p, iap->ia_size);
> > +       if (bmval[0] & FATTR4_WORD0_ARCHIVE)
> > +               *p++ = (statx->fa_flags & NFS_FA_FLAG_ARCHIVE) ?
> > +                       cpu_to_be32(1) : cpu_to_be32(0);
> > +       if (bmval[0] & FATTR4_WORD0_HIDDEN)
> > +               *p++ = (statx->fa_flags & NFS_FA_FLAG_HIDDEN) ?
> > +                       cpu_to_be32(1) : cpu_to_be32(0);
> >         if (bmval[1] & FATTR4_WORD1_MODE)
> >                 *p++ = cpu_to_be32(iap->ia_mode & S_IALLUGO);
> >         if (bmval[1] & FATTR4_WORD1_OWNER)
> >                 p = xdr_encode_opaque(p, owner_name,
> > owner_namelen);
> >         if (bmval[1] & FATTR4_WORD1_OWNER_GROUP)
> >                 p = xdr_encode_opaque(p, owner_group,
> > owner_grouplen);
> > +       if (bmval[1] & FATTR4_WORD1_SYSTEM)
> > +               *p++ = (statx->fa_flags & NFS_FA_FLAG_SYSTEM) ?
> > +                       cpu_to_be32(1) : cpu_to_be32(0);
> >         if (bmval[1] & FATTR4_WORD1_TIME_ACCESS_SET) {
> >                 if (iap->ia_valid & ATTR_ATIME_SET) {
> >                         *p++ =
> > cpu_to_be32(NFS4_SET_TO_CLIENT_TIME);
> > @@ -1176,6 +1217,10 @@ static void encode_attrs(struct xdr_stream
> > *xdr, const struct iattr *iap,
> >                 } else
> >                         *p++ =
> > cpu_to_be32(NFS4_SET_TO_SERVER_TIME);
> >         }
> > +       if (bmval[1] & FATTR4_WORD1_TIME_BACKUP)
> > +               p = xdr_encode_nfstime4(p, &statx->fa_time_backup);
> > +       if (bmval[1] & FATTR4_WORD1_TIME_CREATE)
> > +               p = xdr_encode_nfstime4(p, &statx->fa_btime);
> >         if (bmval[1] & FATTR4_WORD1_TIME_MODIFY_SET) {
> >                 if (iap->ia_valid & ATTR_MTIME_SET) {
> >                         *p++ =
> > cpu_to_be32(NFS4_SET_TO_CLIENT_TIME);
> > @@ -1248,7 +1293,7 @@ static void encode_create(struct xdr_stream
> > *xdr, const struct nfs4_create_arg *
> >  
> >         encode_string(xdr, create->name->len, create->name->name);
> >         encode_attrs(xdr, create->attrs, create->label, &create-
> > >umask,
> > -                       create->server, create->server-
> > >attr_bitmask);
> > +                    NULL, create->server, create->server-
> > >attr_bitmask);
> >  }
> >  
> >  static void encode_getattr(struct xdr_stream *xdr,
> > @@ -1434,12 +1479,12 @@ static inline void encode_createmode(struct
> > xdr_stream *xdr, const struct nfs_op
> >         case NFS4_CREATE_UNCHECKED:
> >                 *p = cpu_to_be32(NFS4_CREATE_UNCHECKED);
> >                 encode_attrs(xdr, arg->u.attrs, arg->label, &arg-
> > >umask,
> > -                               arg->server, arg->server-
> > >attr_bitmask);
> > +                            NULL, arg->server, arg->server-
> > >attr_bitmask);
> >                 break;
> >         case NFS4_CREATE_GUARDED:
> >                 *p = cpu_to_be32(NFS4_CREATE_GUARDED);
> >                 encode_attrs(xdr, arg->u.attrs, arg->label, &arg-
> > >umask,
> > -                               arg->server, arg->server-
> > >attr_bitmask);
> > +                            NULL, arg->server, arg->server-
> > >attr_bitmask);
> >                 break;
> >         case NFS4_CREATE_EXCLUSIVE:
> >                 *p = cpu_to_be32(NFS4_CREATE_EXCLUSIVE);
> > @@ -1449,7 +1494,7 @@ static inline void encode_createmode(struct
> > xdr_stream *xdr, const struct nfs_op
> >                 *p = cpu_to_be32(NFS4_CREATE_EXCLUSIVE4_1);
> >                 encode_nfs4_verifier(xdr, &arg->u.verifier);
> >                 encode_attrs(xdr, arg->u.attrs, arg->label, &arg-
> > >umask,
> > -                               arg->server, arg->server-
> > >exclcreat_bitmask);
> > +                            NULL, arg->server, arg->server-
> > >exclcreat_bitmask);
> >         }
> >  }
> >  
> > @@ -1712,8 +1757,8 @@ static void encode_setattr(struct xdr_stream
> > *xdr, const struct nfs_setattrargs
> >  {
> >         encode_op_hdr(xdr, OP_SETATTR, decode_setattr_maxsz, hdr);
> >         encode_nfs4_stateid(xdr, &arg->stateid);
> > -       encode_attrs(xdr, arg->iap, arg->label, NULL, server,
> > -                       server->attr_bitmask);
> > +       encode_attrs(xdr, arg->iap, arg->label, NULL, arg->statx,
> > server,
> > +                    server->attr_bitmask);
> >  }
> >  
> >  static void encode_setclientid(struct xdr_stream *xdr, const
> > struct nfs4_setclientid *setclientid, struct compound_hdr *hdr)
> > diff --git a/fs/nfs/nfstrace.c b/fs/nfs/nfstrace.c
> > index 5d1bfccbb4da..0b88deb0216e 100644
> > --- a/fs/nfs/nfstrace.c
> > +++ b/fs/nfs/nfstrace.c
> > @@ -9,6 +9,11 @@
> >  #define CREATE_TRACE_POINTS
> >  #include "nfstrace.h"
> >  
> > +EXPORT_TRACEPOINT_SYMBOL_GPL(nfs_ioctl_file_statx_get_enter);
> > +EXPORT_TRACEPOINT_SYMBOL_GPL(nfs_ioctl_file_statx_get_exit);
> > +EXPORT_TRACEPOINT_SYMBOL_GPL(nfs_ioctl_file_statx_set_enter);
> > +EXPORT_TRACEPOINT_SYMBOL_GPL(nfs_ioctl_file_statx_set_exit);
> > +
> >  EXPORT_TRACEPOINT_SYMBOL_GPL(nfs_fsync_enter);
> >  EXPORT_TRACEPOINT_SYMBOL_GPL(nfs_fsync_exit);
> >  EXPORT_TRACEPOINT_SYMBOL_GPL(nfs_xdr_status);
> > diff --git a/fs/nfs/nfstrace.h b/fs/nfs/nfstrace.h
> > index 2ef7cff8a4ba..b67dd087fb47 100644
> > --- a/fs/nfs/nfstrace.h
> > +++ b/fs/nfs/nfstrace.h
> > @@ -166,6 +166,11 @@ DEFINE_NFS_INODE_EVENT_DONE(nfs_fsync_exit);
> >  DEFINE_NFS_INODE_EVENT(nfs_access_enter);
> >  DEFINE_NFS_INODE_EVENT_DONE(nfs_set_cache_invalid);
> >  
> > +DEFINE_NFS_INODE_EVENT(nfs_ioctl_file_statx_get_enter);
> > +DEFINE_NFS_INODE_EVENT_DONE(nfs_ioctl_file_statx_get_exit);
> > +DEFINE_NFS_INODE_EVENT(nfs_ioctl_file_statx_set_enter);
> > +DEFINE_NFS_INODE_EVENT_DONE(nfs_ioctl_file_statx_set_exit);
> > +
> >  TRACE_EVENT(nfs_access_exit,
> >                 TP_PROTO(
> >                         const struct inode *inode,
> > diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c
> > index 73dcaa99fa9b..8fd96d93630a 100644
> > --- a/fs/nfs/proc.c
> > +++ b/fs/nfs/proc.c
> > @@ -717,6 +717,7 @@ const struct nfs_rpc_ops nfs_v2_clientops = {
> >         .dir_inode_ops  = &nfs_dir_inode_operations,
> >         .file_inode_ops = &nfs_file_inode_operations,
> >         .file_ops       = &nfs_file_operations,
> > +       .dir_ops        = &nfs_dir_operations,
> >         .getroot        = nfs_proc_get_root,
> >         .submount       = nfs_submount,
> >         .try_get_tree   = nfs_try_get_tree,
> > diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
> > index 058fc11338d9..0c3a5859f7f3 100644
> > --- a/include/linux/nfs_fs.h
> > +++ b/include/linux/nfs_fs.h
> > @@ -501,6 +501,7 @@ extern __be32 root_nfs_parse_addr(char *name);
> > /*__init*/
> >  extern const struct file_operations nfs_file_operations;
> >  #if IS_ENABLED(CONFIG_NFS_V4)
> >  extern const struct file_operations nfs4_file_operations;
> > +extern const struct file_operations nfs4_dir_operations;
> >  #endif /* CONFIG_NFS_V4 */
> >  extern const struct address_space_operations nfs_file_aops;
> >  extern const struct address_space_operations nfs_dir_aops;
> > diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
> > index 0d5b11c1bfec..9ce61f680a13 100644
> > --- a/include/linux/nfs_xdr.h
> > +++ b/include/linux/nfs_xdr.h
> > @@ -812,6 +812,7 @@ struct nfs_createargs {
> >         struct iattr *          sattr;
> >  };
> >  
> > +struct nfs4_statx;
> >  struct nfs_setattrargs {
> >         struct nfs4_sequence_args       seq_args;
> >         struct nfs_fh *                 fh;
> > @@ -820,6 +821,7 @@ struct nfs_setattrargs {
> >         const struct nfs_server *       server; /* Needed for name
> > mapping */
> >         const u32 *                     bitmask;
> >         const struct nfs4_label         *label;
> > +       const struct nfs4_statx         *statx;
> >  };
> >  
> >  struct nfs_setaclargs {
> > @@ -1744,6 +1746,7 @@ struct nfs_rpc_ops {
> >         const struct inode_operations *dir_inode_ops;
> >         const struct inode_operations *file_inode_ops;
> >         const struct file_operations *file_ops;
> > +       const struct file_operations *dir_ops;
> >         const struct nlmclnt_operations *nlmclnt_ops;
> >  
> >         int     (*getroot) (struct nfs_server *, struct nfs_fh *,
> > diff --git a/include/uapi/linux/nfs.h b/include/uapi/linux/nfs.h
> > index 946cb62d64b0..df87da39bc43 100644
> > --- a/include/uapi/linux/nfs.h
> > +++ b/include/uapi/linux/nfs.h
> > @@ -9,6 +9,8 @@
> >  #define _UAPI_LINUX_NFS_H
> >  
> >  #include <linux/types.h>
> > +#include <asm/byteorder.h>
> > +#include <linux/time.h>
> >  
> >  #define NFS_PROGRAM    100003
> >  #define NFS_PORT       2049
> > @@ -35,6 +37,94 @@
> >  
> >  #define NFS_PIPE_DIRNAME "nfs"
> >  
> > +/* NFS ioctls */
> > +#define NFS_IOC_FILE_STATX_GET _IOR('N', 2, struct
> > nfs_ioctl_nfs4_statx)
> > +#define NFS_IOC_FILE_STATX_SET _IOW('N', 3, struct
> > nfs_ioctl_nfs4_statx)
> > +
> > +/* Options for struct nfs_ioctl_nfs4_statx */
> > +#define NFS_FA_OPTIONS_SYNC_AS_STAT                    0x0000
> > +#define NFS_FA_OPTIONS_FORCE_SYNC                      0x2000 /*
> > See statx */
> > +#define NFS_FA_OPTIONS_DONT_SYNC                       0x4000 /*
> > See statx */
> > +
> > +#define NFS_FA_VALID_TIME_CREATE                       0x00001UL
> > +#define NFS_FA_VALID_TIME_BACKUP                       0x00002UL
> > +#define NFS_FA_VALID_ARCHIVE                           0x00004UL
> > +#define NFS_FA_VALID_HIDDEN                            0x00008UL
> > +#define NFS_FA_VALID_SYSTEM                            0x00010UL
> > +#define NFS_FA_VALID_OWNER                             0x00020UL
> > +#define NFS_FA_VALID_OWNER_GROUP                       0x00040UL
> > +#define NFS_FA_VALID_ATIME                             0x00080UL
> > +#define NFS_FA_VALID_MTIME                             0x00100UL
> > +#define NFS_FA_VALID_CTIME                             0x00200UL
> > +#define NFS_FA_VALID_OFFLINE                           0x00400UL
> > +#define NFS_FA_VALID_MODE                              0x00800UL
> > +#define NFS_FA_VALID_NLINK                             0x01000UL
> > +#define NFS_FA_VALID_BLKSIZE                           0x02000UL
> > +#define NFS_FA_VALID_INO                               0x04000UL
> > +#define NFS_FA_VALID_DEV                               0x08000UL
> > +#define NFS_FA_VALID_RDEV                              0x10000UL
> > +#define NFS_FA_VALID_SIZE                              0x20000UL
> > +#define NFS_FA_VALID_BLOCKS                            0x40000UL
> > +
> > +#define NFS_FA_VALID_ALL_ATTR_0 ( NFS_FA_VALID_TIME_CREATE | \
> > +               NFS_FA_VALID_TIME_BACKUP | \
> > +               NFS_FA_VALID_ARCHIVE | \
> > +               NFS_FA_VALID_HIDDEN | \
> > +               NFS_FA_VALID_SYSTEM | \
> > +               NFS_FA_VALID_OWNER | \
> > +               NFS_FA_VALID_OWNER_GROUP | \
> > +               NFS_FA_VALID_ATIME | \
> > +               NFS_FA_VALID_MTIME | \
> > +               NFS_FA_VALID_CTIME | \
> > +               NFS_FA_VALID_OFFLINE | \
> > +               NFS_FA_VALID_MODE | \
> > +               NFS_FA_VALID_NLINK | \
> > +               NFS_FA_VALID_BLKSIZE | \
> > +               NFS_FA_VALID_INO | \
> > +               NFS_FA_VALID_DEV | \
> > +               NFS_FA_VALID_RDEV | \
> > +               NFS_FA_VALID_SIZE | \
> > +               NFS_FA_VALID_BLOCKS)
> > +
> > +#define NFS_FA_FLAG_ARCHIVE                            (1UL << 0)
> > +#define NFS_FA_FLAG_HIDDEN                             (1UL << 1)
> > +#define NFS_FA_FLAG_SYSTEM                             (1UL << 2)
> > +#define NFS_FA_FLAG_OFFLINE                            (1UL << 3)
> > +
> > +struct nfs_ioctl_timespec {
> > +       __s64           tv_sec;
> > +       __s64           tv_nsec;
> > +};
> > +
> > +struct nfs_ioctl_nfs4_statx {
> > +       __s32           real_fd;                /* real FD to use,
> > +                                                  -1 means use
> > current file */
> > +       __u32           fa_options;
> > +
> > +       __u64           fa_request[2];          /* Attributes to
> > retrieve */
> > +       __u64           fa_valid[2];            /* Attributes set
> > */
> > +
> > +       struct nfs_ioctl_timespec fa_time_backup;/* Backup time */
> > +       struct nfs_ioctl_timespec fa_btime;     /* Birth time */
> > +       __u64           fa_flags;               /* Flag attributes
> > */
> > +       /* Ordinary attributes follow */
> > +       struct nfs_ioctl_timespec fa_atime;     /* Access time */
> > +       struct nfs_ioctl_timespec fa_mtime;     /* Modify time */
> > +       struct nfs_ioctl_timespec fa_ctime;     /* Change time */
> > +       __u32           fa_owner_uid;           /* Owner User ID */
> > +       __u32           fa_group_gid;           /* Primary Group ID
> > */
> > +       __u32           fa_mode;                /* Mode */
> > +       __u32           fa_nlink;
> > +       __u32           fa_blksize;
> > +       __u32           fa_spare;               /* Alignment */
> > +       __u64           fa_ino;
> > +       __u32           fa_dev;
> > +       __u32           fa_rdev;
> > +       __s64           fa_size;
> > +       __s64           fa_blocks;
> > +       __u64           fa_padding[4];
> > +};
> > +
> >  /*
> >   * NFS stats. The good thing with these values is that NFSv3
> > errors are
> >   * a superset of NFSv2 errors (with the exception of NFSERR_WFLUSH
> > which
> > -- 
> > 2.33.1

-- 
Trond Myklebust
Linux NFS client maintainer, Hammerspace
trond.myklebust@hammerspace.com



^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH 7/8] NFS: Support statx_get and statx_set ioctls
  2022-01-03 20:56                 ` Trond Myklebust
@ 2022-01-03 21:47                   ` bfields
  0 siblings, 0 replies; 30+ messages in thread
From: bfields @ 2022-01-03 21:47 UTC (permalink / raw)
  To: Trond Myklebust; +Cc: trondmy, linux-nfs, anna.schumaker

On Mon, Jan 03, 2022 at 08:56:22PM +0000, Trond Myklebust wrote:
> On Mon, 2022-01-03 at 15:52 -0500, J. Bruce Fields wrote:
> > On Fri, Dec 17, 2021 at 03:48:53PM -0500, trondmy@kernel.org wrote:
> > > From: Richard Sharpe <richard.sharpe@primarydata.com>
> > > 
> > > Add support for returning all of the Windows attributes with a
> > > statx
> > > ioctl.
> > 
> > I suppose I'm just woodshedding, but "statx ioctl" is a little
> > confusing--it doesn't have any actual connection with the statx
> > system call, right?
> > 
> > But, why not add this to statx?
> 
> We could definitely add the attribute retrieval to the statx() system
> call. I believe that Steve French did suggest that at one point. There
> was push back because the number of applications that care is limited.
> Perhaps there might be more interest now that we have more extensive
> support for NTFS in the kernel.
> 
> However there is no statx() call to actually _set_ these attributes,
> and that is a reason to stick with the ioctl() approach for now.

Oh, got it, makes sense.

--b.

> 
> 
> > 
> > --b.
> > 
> > > Add support for setting all of the Windows attributes using an
> > > ioctl.
> > > 
> > > Signed-off-by: Richard Sharpe <richard.sharpe@primarydata.com>
> > > Signed-off-by: Lance Shelton <lance.shelton@hammerspace.com>
> > > Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
> > > ---
> > >  fs/nfs/dir.c             |  24 +-
> > >  fs/nfs/getroot.c         |   3 +-
> > >  fs/nfs/inode.c           |  41 +++-
> > >  fs/nfs/internal.h        |   8 +
> > >  fs/nfs/nfs3proc.c        |   1 +
> > >  fs/nfs/nfs4_fs.h         |  31 +++
> > >  fs/nfs/nfs4file.c        | 511
> > > +++++++++++++++++++++++++++++++++++++++
> > >  fs/nfs/nfs4proc.c        | 124 ++++++++++
> > >  fs/nfs/nfs4xdr.c         |  63 ++++-
> > >  fs/nfs/nfstrace.c        |   5 +
> > >  fs/nfs/nfstrace.h        |   5 +
> > >  fs/nfs/proc.c            |   1 +
> > >  include/linux/nfs_fs.h   |   1 +
> > >  include/linux/nfs_xdr.h  |   3 +
> > >  include/uapi/linux/nfs.h |  90 +++++++
> > >  15 files changed, 887 insertions(+), 24 deletions(-)
> > > 
> > > diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
> > > index 731d31015b6a..f6fc60822153 100644
> > > --- a/fs/nfs/dir.c
> > > +++ b/fs/nfs/dir.c
> > > @@ -48,11 +48,6 @@
> > >  
> > >  /* #define NFS_DEBUG_VERBOSE 1 */
> > >  
> > > -static int nfs_opendir(struct inode *, struct file *);
> > > -static int nfs_closedir(struct inode *, struct file *);
> > > -static int nfs_readdir(struct file *, struct dir_context *);
> > > -static int nfs_fsync_dir(struct file *, loff_t, loff_t, int);
> > > -static loff_t nfs_llseek_dir(struct file *, loff_t, int);
> > >  static void nfs_readdir_clear_array(struct page*);
> > >  
> > >  const struct file_operations nfs_dir_operations = {
> > > @@ -63,6 +58,7 @@ const struct file_operations nfs_dir_operations =
> > > {
> > >         .release        = nfs_closedir,
> > >         .fsync          = nfs_fsync_dir,
> > >  };
> > > +EXPORT_SYMBOL_GPL(nfs_dir_operations);
> > >  
> > >  const struct address_space_operations nfs_dir_aops = {
> > >         .freepage = nfs_readdir_clear_array,
> > > @@ -104,8 +100,7 @@ static void put_nfs_open_dir_context(struct
> > > inode *dir, struct nfs_open_dir_cont
> > >  /*
> > >   * Open file
> > >   */
> > > -static int
> > > -nfs_opendir(struct inode *inode, struct file *filp)
> > > +int nfs_opendir(struct inode *inode, struct file *filp)
> > >  {
> > >         int res = 0;
> > >         struct nfs_open_dir_context *ctx;
> > > @@ -123,13 +118,14 @@ nfs_opendir(struct inode *inode, struct file
> > > *filp)
> > >  out:
> > >         return res;
> > >  }
> > > +EXPORT_SYMBOL_GPL(nfs_opendir);
> > >  
> > > -static int
> > > -nfs_closedir(struct inode *inode, struct file *filp)
> > > +int nfs_closedir(struct inode *inode, struct file *filp)
> > >  {
> > >         put_nfs_open_dir_context(file_inode(filp), filp-
> > > >private_data);
> > >         return 0;
> > >  }
> > > +EXPORT_SYMBOL_GPL(nfs_closedir);
> > >  
> > >  struct nfs_cache_array_entry {
> > >         u64 cookie;
> > > @@ -1064,7 +1060,7 @@ static int uncached_readdir(struct
> > > nfs_readdir_descriptor *desc)
> > >     last cookie cache takes care of the common case of reading the
> > >     whole directory.
> > >   */
> > > -static int nfs_readdir(struct file *file, struct dir_context *ctx)
> > > +int nfs_readdir(struct file *file, struct dir_context *ctx)
> > >  {
> > >         struct dentry   *dentry = file_dentry(file);
> > >         struct inode    *inode = d_inode(dentry);
> > > @@ -1157,8 +1153,9 @@ static int nfs_readdir(struct file *file,
> > > struct dir_context *ctx)
> > >         dfprintk(FILE, "NFS: readdir(%pD2) returns %d\n", file,
> > > res);
> > >         return res;
> > >  }
> > > +EXPORT_SYMBOL_GPL(nfs_readdir);
> > >  
> > > -static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int
> > > whence)
> > > +loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int
> > > whence)
> > >  {
> > >         struct nfs_open_dir_context *dir_ctx = filp->private_data;
> > >  
> > > @@ -1196,19 +1193,20 @@ static loff_t nfs_llseek_dir(struct file
> > > *filp, loff_t offset, int whence)
> > >         spin_unlock(&filp->f_lock);
> > >         return offset;
> > >  }
> > > +EXPORT_SYMBOL_GPL(nfs_llseek_dir);
> > >  
> > >  /*
> > >   * All directory operations under NFS are synchronous, so fsync()
> > >   * is a dummy operation.
> > >   */
> > > -static int nfs_fsync_dir(struct file *filp, loff_t start, loff_t
> > > end,
> > > -                        int datasync)
> > > +int nfs_fsync_dir(struct file *filp, loff_t start, loff_t end, int
> > > datasync)
> > >  {
> > >         dfprintk(FILE, "NFS: fsync dir(%pD2) datasync %d\n", filp,
> > > datasync);
> > >  
> > >         nfs_inc_stats(file_inode(filp), NFSIOS_VFSFSYNC);
> > >         return 0;
> > >  }
> > > +EXPORT_SYMBOL_GPL(nfs_fsync_dir);
> > >  
> > >  /**
> > >   * nfs_force_lookup_revalidate - Mark the directory as having
> > > changed
> > > diff --git a/fs/nfs/getroot.c b/fs/nfs/getroot.c
> > > index 11ff2b2e060f..f872970d6240 100644
> > > --- a/fs/nfs/getroot.c
> > > +++ b/fs/nfs/getroot.c
> > > @@ -127,7 +127,8 @@ int nfs_get_root(struct super_block *s, struct
> > > fs_context *fc)
> > >         if (server->caps & NFS_CAP_SECURITY_LABEL)
> > >                 kflags |= SECURITY_LSM_NATIVE_LABELS;
> > >         if (ctx->clone_data.sb) {
> > > -               if (d_inode(fc->root)->i_fop !=
> > > &nfs_dir_operations) {
> > > +               if (d_inode(fc->root)->i_fop !=
> > > +                   server->nfs_client->rpc_ops->dir_ops) {
> > >                         error = -ESTALE;
> > >                         goto error_splat_root;
> > >                 }
> > > diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
> > > index 33f4410190b6..8da662a4953d 100644
> > > --- a/fs/nfs/inode.c
> > > +++ b/fs/nfs/inode.c
> > > @@ -108,6 +108,7 @@ u64 nfs_compat_user_ino64(u64 fileid)
> > >                 ino ^= fileid >> (sizeof(fileid)-sizeof(ino)) * 8;
> > >         return ino;
> > >  }
> > > +EXPORT_SYMBOL_GPL(nfs_compat_user_ino64);
> > >  
> > >  int nfs_drop_inode(struct inode *inode)
> > >  {
> > > @@ -501,7 +502,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh
> > > *fh, struct nfs_fattr *fattr)
> > >                         nfs_inode_init_regular(nfsi);
> > >                 } else if (S_ISDIR(inode->i_mode)) {
> > >                         inode->i_op = NFS_SB(sb)->nfs_client-
> > > >rpc_ops->dir_inode_ops;
> > > -                       inode->i_fop = &nfs_dir_operations;
> > > +                       inode->i_fop = NFS_SB(sb)->nfs_client-
> > > >rpc_ops->dir_ops;
> > >                         inode->i_data.a_ops = &nfs_dir_aops;
> > >                         nfs_inode_init_dir(nfsi);
> > >                         /* Deal with crossing mountpoints */
> > > @@ -867,6 +868,44 @@ static u32 nfs_get_valid_attrmask(struct inode
> > > *inode)
> > >         return reply_mask;
> > >  }
> > >  
> > > +static int nfs_getattr_revalidate_force(struct dentry *dentry)
> > > +{
> > > +       struct inode *inode = d_inode(dentry);
> > > +       struct nfs_server *server = NFS_SERVER(inode);
> > > +
> > > +       if (!(server->flags & NFS_MOUNT_NOAC))
> > > +               nfs_readdirplus_parent_cache_miss(dentry);
> > > +       else
> > > +               nfs_readdirplus_parent_cache_hit(dentry);
> > > +       return __nfs_revalidate_inode(server, inode);
> > > +}
> > > +
> > > +static int nfs_getattr_revalidate_none(struct dentry *dentry)
> > > +{
> > > +       nfs_readdirplus_parent_cache_hit(dentry);
> > > +       return NFS_STALE(d_inode(dentry)) ? -ESTALE : 0;
> > > +}
> > > +
> > > +static int nfs_getattr_revalidate_maybe(struct dentry *dentry,
> > > +                                       unsigned long flags)
> > > +{
> > > +       if (nfs_check_cache_invalid(d_inode(dentry), flags))
> > > +               return nfs_getattr_revalidate_force(dentry);
> > > +       return nfs_getattr_revalidate_none(dentry);
> > > +}
> > > +
> > > +int nfs_getattr_revalidate(const struct path *path,
> > > +                          unsigned long flags,
> > > +                          unsigned int query_flags)
> > > +{
> > > +       if (query_flags & AT_STATX_FORCE_SYNC)
> > > +               return nfs_getattr_revalidate_force(path->dentry);
> > > +       if (!(query_flags & AT_STATX_DONT_SYNC))
> > > +               return nfs_getattr_revalidate_maybe(path->dentry,
> > > flags);
> > > +       return nfs_getattr_revalidate_none(path->dentry);
> > > +}
> > > +EXPORT_SYMBOL_GPL(nfs_getattr_revalidate);
> > > +
> > >  int nfs_getattr(struct user_namespace *mnt_userns, const struct
> > > path *path,
> > >                 struct kstat *stat, u32 request_mask, unsigned int
> > > query_flags)
> > >  {
> > > diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
> > > index 12f6acb483bb..9602a886f0f0 100644
> > > --- a/fs/nfs/internal.h
> > > +++ b/fs/nfs/internal.h
> > > @@ -366,6 +366,12 @@ extern struct nfs_client
> > > *nfs_init_client(struct nfs_client *clp,
> > >                            const struct nfs_client_initdata *);
> > >  
> > >  /* dir.c */
> > > +int nfs_opendir(struct inode *, struct file *);
> > > +int nfs_closedir(struct inode *, struct file *);
> > > +int nfs_readdir(struct file *file, struct dir_context *ctx);
> > > +int nfs_fsync_dir(struct file *, loff_t, loff_t, int);
> > > +loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int
> > > whence);
> > > +
> > >  extern void nfs_advise_use_readdirplus(struct inode *dir);
> > >  extern void nfs_force_use_readdirplus(struct inode *dir);
> > >  extern unsigned long nfs_access_cache_count(struct shrinker
> > > *shrink,
> > > @@ -411,6 +417,8 @@ extern void nfs_set_cache_invalid(struct inode
> > > *inode, unsigned long flags);
> > >  extern bool nfs_check_cache_invalid(struct inode *, unsigned
> > > long);
> > >  extern int nfs_wait_bit_killable(struct wait_bit_key *key, int
> > > mode);
> > >  extern int nfs_wait_atomic_killable(atomic_t *p, unsigned int
> > > mode);
> > > +extern int nfs_getattr_revalidate(const struct path *path,
> > > unsigned long flags,
> > > +                                 unsigned int query_flags);
> > >  
> > >  /* super.c */
> > >  extern const struct super_operations nfs_sops;
> > > diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c
> > > index 7100514d306b..091005e169b7 100644
> > > --- a/fs/nfs/nfs3proc.c
> > > +++ b/fs/nfs/nfs3proc.c
> > > @@ -1018,6 +1018,7 @@ const struct nfs_rpc_ops nfs_v3_clientops = {
> > >         .dir_inode_ops  = &nfs3_dir_inode_operations,
> > >         .file_inode_ops = &nfs3_file_inode_operations,
> > >         .file_ops       = &nfs_file_operations,
> > > +       .dir_ops        = &nfs_dir_operations,
> > >         .nlmclnt_ops    = &nlmclnt_fl_close_lock_ops,
> > >         .getroot        = nfs3_proc_get_root,
> > >         .submount       = nfs_submount,
> > > diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
> > > index ed5eaca6801e..9f21d8520e99 100644
> > > --- a/fs/nfs/nfs4_fs.h
> > > +++ b/fs/nfs/nfs4_fs.h
> > > @@ -248,6 +248,34 @@ struct nfs4_opendata {
> > >         int rpc_status;
> > >  };
> > >  
> > > +struct nfs4_statx {
> > > +       int             real_fd;                /* real FD to use,
> > > +                                                  -1 means use
> > > current file */
> > > +       __u32           fa_options;             /* statx flags */
> > > +       __u64           fa_request[2];          /* Attributes
> > > requested */
> > > +       __u64           fa_valid[2];            /* Attributes set
> > > */
> > > +
> > > +       struct timespec64 fa_time_backup;       /* Backup time */
> > > +       struct timespec64 fa_btime;             /* Birth time */
> > > +       /* Flag attributes */
> > > +       __u64 fa_flags;
> > > +       struct timespec64 fa_atime;             /* Access time */
> > > +       struct timespec64 fa_mtime;             /* Modify time */
> > > +       struct timespec64 fa_ctime;             /* Change time */
> > > +       kuid_t          fa_owner_uid;           /* Owner User ID */
> > > +       kgid_t          fa_group_gid;           /* Primary Group ID
> > > */
> > > +        /* Normal stat fields after this */
> > > +       __u32           fa_mode;                /* Mode */
> > > +       unsigned int    fa_nlink;
> > > +       __u32           fa_blksize;
> > > +       __u32           fa_spare;               /* Alignment */
> > > +       __u64           fa_ino;
> > > +       dev_t           fa_dev;
> > > +       dev_t           fa_rdev;
> > > +       loff_t          fa_size;
> > > +       __u64           fa_blocks;
> > > +};
> > > +
> > >  struct nfs4_add_xprt_data {
> > >         struct nfs_client       *clp;
> > >         const struct cred       *cred;
> > > @@ -315,6 +343,9 @@ extern int nfs4_set_rw_stateid(nfs4_stateid
> > > *stateid,
> > >                 const struct nfs_open_context *ctx,
> > >                 const struct nfs_lock_context *l_ctx,
> > >                 fmode_t fmode);
> > > +int nfs4_set_nfs4_statx(struct inode *inode,
> > > +               struct nfs4_statx *statx,
> > > +               struct nfs_fattr *fattr);
> > >  extern int nfs4_proc_getattr(struct nfs_server *server, struct
> > > nfs_fh *fhandle,
> > >                              struct nfs_fattr *fattr, struct inode
> > > *inode);
> > >  extern int update_open_stateid(struct nfs4_state *state,
> > > diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c
> > > index e79ae4cbc395..494ebc7cd1c0 100644
> > > --- a/fs/nfs/nfs4file.c
> > > +++ b/fs/nfs/nfs4file.c
> > > @@ -9,6 +9,8 @@
> > >  #include <linux/falloc.h>
> > >  #include <linux/mount.h>
> > >  #include <linux/nfs_fs.h>
> > > +#include <linux/time32.h>
> > > +#include <linux/compat.h>
> > >  #include <linux/nfs_ssc.h>
> > >  #include "delegation.h"
> > >  #include "internal.h"
> > > @@ -132,6 +134,503 @@ nfs4_file_flush(struct file *file, fl_owner_t
> > > id)
> > >         return filemap_check_wb_err(file->f_mapping, since);
> > >  }
> > >  
> > > +static int nfs_get_timespec64(struct timespec64 *ts,
> > > +                             const struct nfs_ioctl_timespec
> > > __user *uts)
> > > +{
> > > +       __s64 dummy;
> > > +       if (unlikely(get_user(dummy, &uts->tv_sec) != 0))
> > > +               return EFAULT;
> > > +       ts->tv_sec = dummy;
> > > +       if (unlikely(get_user(dummy, &uts->tv_nsec) != 0))
> > > +               return EFAULT;
> > > +       ts->tv_nsec = dummy;
> > > +       return 0;
> > > +}
> > > +
> > > +static int nfs_put_timespec64(const struct timespec64 *ts,
> > > +                             struct nfs_ioctl_timespec __user
> > > *uts)
> > > +{
> > > +       __s64 dummy;
> > > +
> > > +       dummy = ts->tv_sec;
> > > +       if (unlikely(put_user(dummy, &uts->tv_sec) != 0))
> > > +               return EFAULT;
> > > +       dummy = ts->tv_nsec;
> > > +       if (unlikely(put_user(dummy, &uts->tv_nsec) != 0))
> > > +               return EFAULT;
> > > +       return 0;
> > > +}
> > > +
> > > +static struct file *nfs4_get_real_file(struct file *src, unsigned
> > > int fd)
> > > +{
> > > +       struct file *filp = fget_raw(fd);
> > > +       int ret = -EBADF;
> > > +
> > > +       if (!filp)
> > > +               goto out;
> > > +       /* Validate that the files share the same underlying
> > > filesystem */
> > > +       ret = -EXDEV;
> > > +       if (file_inode(filp)->i_sb != file_inode(src)->i_sb)
> > > +               goto out_put;
> > > +       return filp;
> > > +out_put:
> > > +       fput(filp);
> > > +out:
> > > +       return ERR_PTR(ret);
> > > +}
> > > +
> > > +static unsigned long nfs4_statx_request_to_cache_validity(__u64
> > > request,
> > > +                                                         u64
> > > fattr_supported)
> > > +{
> > > +       unsigned long ret = 0;
> > > +
> > > +       if (request & NFS_FA_VALID_ATIME)
> > > +               ret |= NFS_INO_INVALID_ATIME;
> > > +       if (request & NFS_FA_VALID_CTIME)
> > > +               ret |= NFS_INO_INVALID_CTIME;
> > > +       if (request & NFS_FA_VALID_MTIME)
> > > +               ret |= NFS_INO_INVALID_MTIME;
> > > +       if (request & NFS_FA_VALID_SIZE)
> > > +               ret |= NFS_INO_INVALID_SIZE;
> > > +
> > > +       if (request & NFS_FA_VALID_MODE)
> > > +               ret |= NFS_INO_INVALID_MODE;
> > > +       if (request & (NFS_FA_VALID_OWNER |
> > > NFS_FA_VALID_OWNER_GROUP))
> > > +               ret |= NFS_INO_INVALID_OTHER;
> > > +
> > > +       if (request & NFS_FA_VALID_NLINK)
> > > +               ret |= NFS_INO_INVALID_NLINK;
> > > +       if (request & NFS_FA_VALID_BLOCKS)
> > > +               ret |= NFS_INO_INVALID_BLOCKS;
> > > +
> > > +       if (request & NFS_FA_VALID_TIME_CREATE)
> > > +               ret |= NFS_INO_INVALID_BTIME;
> > > +
> > > +       if (request & NFS_FA_VALID_ARCHIVE) {
> > > +               if (fattr_supported & NFS_ATTR_FATTR_ARCHIVE)
> > > +                       ret |= NFS_INO_INVALID_WINATTR;
> > > +               else if (fattr_supported &
> > > NFS_ATTR_FATTR_TIME_BACKUP)
> > > +                       ret |= NFS_INO_INVALID_WINATTR |
> > > NFS_INO_INVALID_MTIME;
> > > +       }
> > > +       if (request & (NFS_FA_VALID_TIME_BACKUP |
> > > NFS_FA_VALID_HIDDEN |
> > > +                      NFS_FA_VALID_SYSTEM | NFS_FA_VALID_OFFLINE))
> > > +               ret |= NFS_INO_INVALID_WINATTR;
> > > +
> > > +       return ret ? (ret | NFS_INO_INVALID_CHANGE) : 0;
> > > +}
> > > +
> > > +static long nfs4_ioctl_file_statx_get(struct file *dst_file,
> > > +                                     struct nfs_ioctl_nfs4_statx
> > > __user *uarg)
> > > +{
> > > +       struct nfs4_statx args = {
> > > +               .real_fd = -1,
> > > +               .fa_valid = { 0 },
> > > +       };
> > > +       struct inode *inode;
> > > +       struct nfs_inode *nfsi;
> > > +       struct nfs_server *server;
> > > +       u64 fattr_supported;
> > > +       unsigned long reval_attr;
> > > +       unsigned int reval_flags;
> > > +       __u32 tmp;
> > > +       int ret;
> > > +
> > > +       /*
> > > +        * We get the first word from the uarg as it tells us
> > > whether
> > > +        * to use the passed in struct file or use that fd to find
> > > the
> > > +        * struct file.
> > > +        */
> > > +       if (get_user(args.real_fd, &uarg->real_fd))
> > > +               return -EFAULT;
> > > +
> > > +       if (get_user(args.fa_options, &uarg->fa_options))
> > > +               return -EFAULT;
> > > +
> > > +       if (get_user(args.fa_request[0], &uarg->fa_request[0]))
> > > +               return -EFAULT;
> > > +
> > > +       if (args.real_fd >= 0) {
> > > +               dst_file = nfs4_get_real_file(dst_file,
> > > args.real_fd);
> > > +               if (IS_ERR(dst_file))
> > > +                       return PTR_ERR(dst_file);
> > > +       }
> > > +
> > > +       /*
> > > +        * Backward compatibility: we stole the top 32 bits of
> > > 'real_fd'
> > > +        * to create the fa_options field, so if its value is -1,
> > > then
> > > +        * assume it is the high word of (__s64)real_fd == -1, and
> > > just
> > > +        * set it to zero.
> > > +        */
> > > +       if (args.fa_options == 0xFFFF)
> > > +               args.fa_options = 0;
> > > +
> > > +       inode = file_inode(dst_file);
> > > +       nfsi = NFS_I(inode);
> > > +       server = NFS_SERVER(inode);
> > > +       fattr_supported = server->fattr_valid;
> > > +
> > > +       trace_nfs_ioctl_file_statx_get_enter(inode);
> > > +
> > > +       if (args.fa_options & NFS_FA_OPTIONS_FORCE_SYNC)
> > > +               reval_flags = AT_STATX_FORCE_SYNC;
> > > +       else if (args.fa_options & NFS_FA_OPTIONS_DONT_SYNC)
> > > +               reval_flags = AT_STATX_DONT_SYNC;
> > > +       else
> > > +               reval_flags = AT_STATX_SYNC_AS_STAT;
> > > +
> > > +       reval_attr =
> > > nfs4_statx_request_to_cache_validity(args.fa_request[0],
> > > +                                                        
> > > fattr_supported);
> > > +
> > > +       if ((reval_attr & (NFS_INO_INVALID_CTIME |
> > > NFS_INO_INVALID_MTIME)) &&
> > > +           reval_flags != AT_STATX_DONT_SYNC && S_ISREG(inode-
> > > >i_mode)) {
> > > +               ret = filemap_write_and_wait(inode->i_mapping);
> > > +               if (ret)
> > > +                       goto out;
> > > +       }
> > > +
> > > +       if ((dst_file->f_path.mnt->mnt_flags & MNT_NOATIME) ||
> > > +           ((dst_file->f_path.mnt->mnt_flags & MNT_NODIRATIME) &&
> > > +            S_ISDIR(inode->i_mode)))
> > > +               reval_attr &= ~NFS_INO_INVALID_ATIME;
> > > +
> > > +       ret = nfs_getattr_revalidate(&dst_file->f_path, reval_attr,
> > > +                                    reval_flags);
> > > +       if (ret != 0)
> > > +               goto out;
> > > +
> > > +       ret = -EFAULT;
> > > +       if ((fattr_supported & NFS_ATTR_FATTR_OWNER) &&
> > > +           (args.fa_request[0] & NFS_FA_VALID_OWNER)) {
> > > +               tmp = from_kuid_munged(current_user_ns(), inode-
> > > >i_uid);
> > > +               if (unlikely(put_user(tmp, &uarg->fa_owner_uid) !=
> > > 0))
> > > +                       goto out;
> > > +               args.fa_valid[0] |= NFS_FA_VALID_OWNER;
> > > +       }
> > > +
> > > +       if ((fattr_supported & NFS_ATTR_FATTR_GROUP) &&
> > > +           (args.fa_request[0] & NFS_FA_VALID_OWNER_GROUP)) {
> > > +               tmp = from_kgid_munged(current_user_ns(), inode-
> > > >i_gid);
> > > +               if (unlikely(put_user(tmp, &uarg->fa_group_gid) !=
> > > 0))
> > > +                       goto out;
> > > +               args.fa_valid[0] |= NFS_FA_VALID_OWNER_GROUP;
> > > +       }
> > > +
> > > +       if ((fattr_supported & NFS_ATTR_FATTR_TIME_BACKUP) &&
> > > +           (args.fa_request[0] & NFS_FA_VALID_TIME_BACKUP)) {
> > > +               if (nfs_put_timespec64(&nfsi->timebackup, &uarg-
> > > >fa_time_backup))
> > > +                       goto out;
> > > +               args.fa_valid[0] |= NFS_FA_VALID_TIME_BACKUP;
> > > +       }
> > > +
> > > +       if ((fattr_supported & NFS_ATTR_FATTR_BTIME) &&
> > > +           (args.fa_request[0] & NFS_FA_VALID_TIME_CREATE)) {
> > > +               if (nfs_put_timespec64(&nfsi->btime, &uarg-
> > > >fa_btime))
> > > +                       goto out;
> > > +               args.fa_valid[0] |= NFS_FA_VALID_TIME_CREATE;
> > > +       }
> > > +
> > > +       /* atime, mtime, and ctime are all stored in the regular
> > > inode,
> > > +        * not the nfs inode.
> > > +        */
> > > +       if ((fattr_supported & NFS_ATTR_FATTR_ATIME) &&
> > > +           (args.fa_request[0] & NFS_FA_VALID_ATIME)) {
> > > +               if (nfs_put_timespec64(&inode->i_atime, &uarg-
> > > >fa_atime))
> > > +                       goto out;
> > > +               args.fa_valid[0] |= NFS_FA_VALID_ATIME;
> > > +       }
> > > +
> > > +       if ((fattr_supported & NFS_ATTR_FATTR_MTIME) &&
> > > +           (args.fa_request[0] & NFS_FA_VALID_MTIME)) {
> > > +               if (nfs_put_timespec64(&inode->i_mtime, &uarg-
> > > >fa_mtime))
> > > +                        goto out;
> > > +               args.fa_valid[0] |= NFS_FA_VALID_MTIME;
> > > +       }
> > > +
> > > +       if ((fattr_supported & NFS_ATTR_FATTR_CTIME) &&
> > > +           (args.fa_request[0] & NFS_FA_VALID_CTIME)) {
> > > +               if (nfs_put_timespec64(&inode->i_ctime, &uarg-
> > > >fa_ctime))
> > > +                       goto out;
> > > +               args.fa_valid[0] |= NFS_FA_VALID_CTIME;
> > > +       }
> > > +
> > > +        /*
> > > +         * It looks like PDFS does not support or properly handle
> > > the
> > > +         * archive bit.
> > > +         */
> > > +       if ((fattr_supported & NFS_ATTR_FATTR_ARCHIVE) &&
> > > +           (args.fa_request[0] & NFS_FA_VALID_ARCHIVE)) {
> > > +               if (nfsi->archive)
> > > +                       args.fa_flags |= NFS_FA_FLAG_ARCHIVE;
> > > +               args.fa_valid[0] |= NFS_FA_VALID_ARCHIVE;
> > > +       }
> > > +
> > > +       if ((fattr_supported & NFS_ATTR_FATTR_TIME_BACKUP) &&
> > > +           (args.fa_request[0] & NFS_FA_VALID_ARCHIVE)) {
> > > +               if (timespec64_compare(&inode->i_mtime, &nfsi-
> > > >timebackup) > 0)
> > > +                       args.fa_flags |= NFS_FA_FLAG_ARCHIVE;
> > > +               args.fa_valid[0] |= NFS_FA_VALID_ARCHIVE;
> > > +       }
> > > +
> > > +       if ((fattr_supported & NFS_ATTR_FATTR_HIDDEN) &&
> > > +           (args.fa_request[0] & NFS_FA_VALID_HIDDEN)) {
> > > +               if (nfsi->hidden)
> > > +                       args.fa_flags |= NFS_FA_FLAG_HIDDEN;
> > > +               args.fa_valid[0] |= NFS_FA_VALID_HIDDEN;
> > > +       }
> > > +       if ((fattr_supported & NFS_ATTR_FATTR_SYSTEM) &&
> > > +           (args.fa_request[0] & NFS_FA_VALID_SYSTEM)) {
> > > +               if (nfsi->system)
> > > +                       args.fa_flags |= NFS_FA_FLAG_SYSTEM;
> > > +               args.fa_valid[0] |= NFS_FA_VALID_SYSTEM;
> > > +       }
> > > +
> > > +       if ((fattr_supported & NFS_ATTR_FATTR_OFFLINE) &&
> > > +           (args.fa_request[0] & NFS_FA_VALID_OFFLINE)) {
> > > +               if (nfsi->offline)
> > > +                       args.fa_flags |= NFS_FA_FLAG_OFFLINE;
> > > +               args.fa_valid[0] |= NFS_FA_VALID_OFFLINE;
> > > +       }
> > > +
> > > +       if ((args.fa_valid[0] & (NFS_FA_VALID_ARCHIVE |
> > > +                               NFS_FA_VALID_HIDDEN |
> > > +                               NFS_FA_VALID_SYSTEM |
> > > +                               NFS_FA_VALID_OFFLINE)) &&
> > > +           put_user(args.fa_flags, &uarg->fa_flags))
> > > +               goto out;
> > > +
> > > +       if ((fattr_supported & NFS_ATTR_FATTR_MODE) &&
> > > +           (args.fa_request[0] & NFS_FA_VALID_MODE)) {
> > > +               tmp = inode->i_mode;
> > > +               /* This is an unsigned short we put into an __u32
> > > */
> > > +               if (unlikely(put_user(tmp, &uarg->fa_mode) != 0))
> > > +                       goto out;
> > > +               args.fa_valid[0] |= NFS_FA_VALID_MODE;
> > > +       }
> > > +
> > > +       if ((fattr_supported & NFS_ATTR_FATTR_NLINK) &&
> > > +           (args.fa_request[0] & NFS_FA_VALID_NLINK)) {
> > > +               tmp = inode->i_nlink;
> > > +               if (unlikely(put_user(tmp, &uarg->fa_nlink) != 0))
> > > +                       goto out;
> > > +               args.fa_valid[0] |= NFS_FA_VALID_NLINK;
> > > +       }
> > > +
> > > +       if (args.fa_request[0] & NFS_FA_VALID_BLKSIZE) {
> > > +               tmp = i_blocksize(inode);
> > > +               if (S_ISDIR(inode->i_mode))
> > > +                       tmp = NFS_SERVER(inode)->dtsize;
> > > +               if (unlikely(put_user(tmp, &uarg->fa_blksize) !=
> > > 0))
> > > +                       goto out;
> > > +               args.fa_valid[0] |= NFS_FA_VALID_BLKSIZE;
> > > +       }
> > > +
> > > +       if (args.fa_request[0] & NFS_FA_VALID_INO) {
> > > +               __u64 ino =
> > > nfs_compat_user_ino64(NFS_FILEID(inode));
> > > +               if (unlikely(put_user(ino, &uarg->fa_ino) != 0))
> > > +                       goto out;
> > > +               args.fa_valid[0] |= NFS_FA_VALID_INO;
> > > +       }
> > > +
> > > +       if (args.fa_request[0] & NFS_FA_VALID_DEV) {
> > > +               tmp = inode->i_sb->s_dev;
> > > +               if (unlikely(put_user(tmp, &uarg->fa_dev) != 0))
> > > +                       goto out;
> > > +               args.fa_valid[0] |= NFS_FA_VALID_DEV;
> > > +       }
> > > +
> > > +       if ((fattr_supported & NFS_ATTR_FATTR_RDEV) &&
> > > +           (args.fa_request[0] & NFS_FA_VALID_RDEV)) {
> > > +               tmp = inode->i_rdev;
> > > +               if (unlikely(put_user(tmp, &uarg->fa_rdev) != 0))
> > > +                       goto out;
> > > +               args.fa_valid[0] |= NFS_FA_VALID_RDEV;
> > > +       }
> > > +
> > > +       if ((fattr_supported & NFS_ATTR_FATTR_SIZE) &&
> > > +           (args.fa_request[0] & NFS_FA_VALID_SIZE)) {
> > > +               __s64 size = i_size_read(inode);
> > > +               if (unlikely(put_user(size, &uarg->fa_size) != 0))
> > > +                       goto out;
> > > +               args.fa_valid[0] |= NFS_FA_VALID_SIZE;
> > > +       }
> > > +
> > > +       if ((fattr_supported &
> > > +            (NFS_ATTR_FATTR_BLOCKS_USED |
> > > NFS_ATTR_FATTR_SPACE_USED)) &&
> > > +           (args.fa_request[0] & NFS_FA_VALID_BLOCKS)) {
> > > +               __s64 blocks = inode->i_blocks;
> > > +               if (unlikely(put_user(blocks, &uarg->fa_blocks) !=
> > > 0))
> > > +                       goto out;
> > > +               args.fa_valid[0] |= NFS_FA_VALID_BLOCKS;
> > > +       }
> > > +
> > > +       if (unlikely(put_user(args.fa_valid[0], &uarg->fa_valid[0])
> > > != 0))
> > > +               goto out;
> > > +       if (unlikely(put_user(args.fa_valid[1], &uarg->fa_valid[1])
> > > != 0))
> > > +               goto out;
> > > +
> > > +       ret = 0;
> > > +out:
> > > +       if (args.real_fd >= 0)
> > > +               fput(dst_file);
> > > +       trace_nfs_ioctl_file_statx_get_exit(inode, ret);
> > > +       return ret;
> > > +}
> > > +
> > > +static long nfs4_ioctl_file_statx_set(struct file *dst_file,
> > > +                                     struct nfs_ioctl_nfs4_statx
> > > __user *uarg)
> > > +{
> > > +       struct nfs4_statx args = {
> > > +               .real_fd = -1,
> > > +               .fa_valid = { 0 },
> > > +       };
> > > +       struct nfs_fattr *fattr = nfs_alloc_fattr();
> > > +       struct inode *inode;
> > > +       /*
> > > +        * If you need a different error code below, you need to
> > > set it
> > > +        */
> > > +       int ret = -EFAULT;
> > > +
> > > +       if (fattr == NULL)
> > > +               return -ENOMEM;
> > > +
> > > +       /*
> > > +        * We get the first u64 word from the uarg as it tells us
> > > whether
> > > +        * to use the passed in struct file or use that fd to find
> > > the
> > > +        * struct file.
> > > +        */
> > > +       if (get_user(args.real_fd, &uarg->real_fd))
> > > +               goto out_free;
> > > +
> > > +       if (args.real_fd >= 0) {
> > > +               dst_file = nfs4_get_real_file(dst_file,
> > > args.real_fd);
> > > +               if (IS_ERR(dst_file)) {
> > > +                       ret = PTR_ERR(dst_file);
> > > +                       goto out_free;
> > > +               }
> > > +       }
> > > +       inode = file_inode(dst_file);
> > > +       trace_nfs_ioctl_file_statx_set_enter(inode);
> > > +
> > > +       inode_lock(inode);
> > > +
> > > +       /* Write all dirty data */
> > > +       if (S_ISREG(inode->i_mode)) {
> > > +               ret = nfs_sync_inode(inode);
> > > +               if (ret)
> > > +                       goto out;
> > > +       }
> > > +
> > > +       ret = -EFAULT;
> > > +       if (get_user(args.fa_valid[0], &uarg->fa_valid[0]))
> > > +               goto out;
> > > +       args.fa_valid[0] &= NFS_FA_VALID_ALL_ATTR_0;
> > > +
> > > +       if (args.fa_valid[0] & NFS_FA_VALID_OWNER) {
> > > +               uid_t uid;
> > > +
> > > +               if (unlikely(get_user(uid, &uarg->fa_owner_uid) !=
> > > 0))
> > > +                       goto out;
> > > +               args.fa_owner_uid = make_kuid(current_user_ns(),
> > > uid);
> > > +               if (!uid_valid(args.fa_owner_uid)) {
> > > +                       ret = -EINVAL;
> > > +                       goto out;
> > > +               }
> > > +       }
> > > +
> > > +       if (args.fa_valid[0] & NFS_FA_VALID_OWNER_GROUP) {
> > > +               gid_t gid;
> > > +
> > > +               if (unlikely(get_user(gid, &uarg->fa_group_gid) !=
> > > 0))
> > > +                       goto out;
> > > +               args.fa_group_gid = make_kgid(current_user_ns(),
> > > gid);
> > > +               if (!gid_valid(args.fa_group_gid)) {
> > > +                       ret = -EINVAL;
> > > +                       goto out;
> > > +               }
> > > +       }
> > > +
> > > +       if ((args.fa_valid[0] & (NFS_FA_VALID_ARCHIVE |
> > > +                                       NFS_FA_VALID_HIDDEN |
> > > +                                       NFS_FA_VALID_SYSTEM)) &&
> > > +           get_user(args.fa_flags, &uarg->fa_flags))
> > > +               goto out;
> > > +
> > > +       if ((args.fa_valid[0] & NFS_FA_VALID_TIME_CREATE) &&
> > > +           nfs_get_timespec64(&args.fa_btime, &uarg->fa_btime))
> > > +               goto out;
> > > +
> > > +       if ((args.fa_valid[0] & NFS_FA_VALID_ATIME) &&
> > > +           nfs_get_timespec64(&args.fa_atime, &uarg->fa_atime))
> > > +               goto out;
> > > +
> > > +       if ((args.fa_valid[0] & NFS_FA_VALID_MTIME) &&
> > > +           nfs_get_timespec64(&args.fa_mtime, &uarg->fa_mtime))
> > > +               goto out;
> > > +
> > > +       if (args.fa_valid[0] & NFS_FA_VALID_TIME_BACKUP) {
> > > +               if (nfs_get_timespec64(&args.fa_time_backup, &uarg-
> > > >fa_time_backup))
> > > +                       goto out;
> > > +       } else if ((args.fa_valid[0] & NFS_FA_VALID_ARCHIVE) &&
> > > +                       !(NFS_SERVER(inode)->fattr_valid &
> > > NFS_ATTR_FATTR_ARCHIVE)) {
> > > +               args.fa_valid[0] |= NFS_FA_VALID_TIME_BACKUP;
> > > +               if (!(args.fa_flags & NFS_FA_FLAG_ARCHIVE)) {
> > > +                       nfs_revalidate_inode(inode,
> > > NFS_INO_INVALID_MTIME);
> > > +                       args.fa_time_backup.tv_sec = inode-
> > > >i_mtime.tv_sec;
> > > +                       args.fa_time_backup.tv_nsec = inode-
> > > >i_mtime.tv_nsec;
> > > +               } else if (args.fa_valid[0] &
> > > NFS_FA_VALID_TIME_CREATE)
> > > +                       args.fa_time_backup = args.fa_btime;
> > > +               else {
> > > +                       nfs_revalidate_inode(inode,
> > > NFS_INO_INVALID_BTIME);
> > > +                       args.fa_time_backup = NFS_I(inode)->btime;
> > > +               }
> > > +       }
> > > +
> > > +        if (args.fa_valid[0] & NFS_FA_VALID_SIZE) {
> > > +               if (copy_from_user(&args.fa_size, &uarg->fa_size,
> > > +                                       sizeof(args.fa_size)))
> > > +                       goto out;
> > > +               ret = inode_newsize_ok(inode,args.fa_size);
> > > +               if (ret)
> > > +                       goto out;
> > > +               if (args.fa_size == i_size_read(inode))
> > > +                       args.fa_valid[0] &= ~NFS_FA_VALID_SIZE;
> > > +       }
> > > +
> > > +       /*
> > > +        * No need to update the inode because that is done in
> > > nfs4_set_nfs4_statx
> > > +        */
> > > +       ret = nfs4_set_nfs4_statx(inode, &args, fattr);
> > > +
> > > +out:
> > > +       inode_unlock(inode);
> > > +       if (args.real_fd >= 0)
> > > +               fput(dst_file);
> > > +       trace_nfs_ioctl_file_statx_set_exit(inode, ret);
> > > +out_free:
> > > +       nfs_free_fattr(fattr);
> > > +       return ret;
> > > +}
> > > +
> > > +static long nfs4_ioctl(struct file *file, unsigned int cmd,
> > > unsigned long arg)
> > > +{
> > > +       void __user *argp = (void __user *)arg;
> > > +       long ret;
> > > +
> > > +       switch (cmd) {
> > > +       case NFS_IOC_FILE_STATX_GET:
> > > +               ret = nfs4_ioctl_file_statx_get(file, argp);
> > > +               break;
> > > +       case NFS_IOC_FILE_STATX_SET:
> > > +               ret = nfs4_ioctl_file_statx_set(file, argp);
> > > +               break;
> > > +       default:
> > > +               ret = -ENOIOCTLCMD;
> > > +       }
> > > +
> > > +       dprintk("%s: file=%pD2, cmd=%u, ret=%ld\n", __func__, file,
> > > cmd, ret);
> > > +       return ret;
> > > +}
> > > +
> > >  #ifdef CONFIG_NFS_V4_2
> > >  static ssize_t __nfs4_copy_file_range(struct file *file_in, loff_t
> > > pos_in,
> > >                                       struct file *file_out, loff_t
> > > pos_out,
> > > @@ -187,6 +686,7 @@ static ssize_t __nfs4_copy_file_range(struct
> > > file *file_in, loff_t pos_in,
> > >         return ret;
> > >  }
> > >  
> > > +
> > >  static ssize_t nfs4_copy_file_range(struct file *file_in, loff_t
> > > pos_in,
> > >                                     struct file *file_out, loff_t
> > > pos_out,
> > >                                     size_t count, unsigned int
> > > flags)
> > > @@ -461,4 +961,15 @@ const struct file_operations
> > > nfs4_file_operations = {
> > >  #else
> > >         .llseek         = nfs_file_llseek,
> > >  #endif
> > > +       .unlocked_ioctl = nfs4_ioctl,
> > > +};
> > > +
> > > +const struct file_operations nfs4_dir_operations = {
> > > +       .llseek         = nfs_llseek_dir,
> > > +       .read           = generic_read_dir,
> > > +       .iterate_shared = nfs_readdir,
> > > +       .open           = nfs_opendir,
> > > +       .release        = nfs_closedir,
> > > +       .fsync          = nfs_fsync_dir,
> > > +       .unlocked_ioctl = nfs4_ioctl,
> > >  };
> > > diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
> > > index d497616ca149..7c032583ffa2 100644
> > > --- a/fs/nfs/nfs4proc.c
> > > +++ b/fs/nfs/nfs4proc.c
> > > @@ -7959,6 +7959,129 @@ static int _nfs41_proc_get_locations(struct
> > > inode *inode,
> > >  
> > >  #endif /* CONFIG_NFS_V4_1 */
> > >  
> > > +static int _nfs4_set_nfs4_statx(struct inode *inode,
> > > +               struct nfs4_statx *statx,
> > > +               struct nfs_fattr *fattr)
> > > +{
> > > +       const __u64 statx_win = NFS_FA_VALID_TIME_CREATE |
> > > +                               NFS_FA_VALID_TIME_BACKUP |
> > > +                               NFS_FA_VALID_ARCHIVE |
> > > NFS_FA_VALID_HIDDEN |
> > > +                               NFS_FA_VALID_SYSTEM;
> > > +       struct iattr sattr = {0};
> > > +       struct nfs_server *server = NFS_SERVER(inode);
> > > +       __u32 bitmask[3];
> > > +       struct nfs_setattrargs arg = {
> > > +               .fh             = NFS_FH(inode),
> > > +               .iap            = &sattr,
> > > +               .server         = server,
> > > +               .bitmask        = bitmask,
> > > +               .statx          = statx,
> > > +       };
> > > +       struct nfs_setattrres res = {
> > > +               .fattr          = fattr,
> > > +               .server         = server,
> > > +       };
> > > +       struct rpc_message msg = {
> > > +               .rpc_proc       =
> > > &nfs4_procedures[NFSPROC4_CLNT_SETATTR],
> > > +               .rpc_argp       = &arg,
> > > +               .rpc_resp       = &res,
> > > +       };
> > > +       int status;
> > > +
> > > +       nfs4_bitmap_copy_adjust(
> > > +               bitmask, server->attr_bitmask, inode,
> > > +               NFS_INO_INVALID_CHANGE | NFS_INO_INVALID_CTIME |
> > > +                       NFS_INO_INVALID_SIZE |
> > > NFS_INO_INVALID_OTHER |
> > > +                       NFS_INO_INVALID_BTIME |
> > > NFS_INO_INVALID_WINATTR);
> > > +       /* Use the iattr structure to set atime and mtime since
> > > handling already
> > > +        * exists for them using the iattr struct in the
> > > encode_attrs()
> > > +        * (xdr encoding) routine.
> > > +        */
> > > +       if (statx && (statx->fa_valid[0] & NFS_FA_VALID_MTIME)) {
> > > +               sattr.ia_valid |= ATTR_MTIME_SET;
> > > +               sattr.ia_mtime.tv_sec = statx->fa_mtime.tv_sec;
> > > +               sattr.ia_mtime.tv_nsec = statx->fa_mtime.tv_nsec;
> > > +       }
> > > +
> > > +       if (statx && (statx->fa_valid[0] & NFS_FA_VALID_ATIME)) {
> > > +               sattr.ia_valid |= ATTR_ATIME_SET;
> > > +               sattr.ia_atime.tv_sec = statx->fa_atime.tv_sec;
> > > +               sattr.ia_atime.tv_nsec = statx->fa_atime.tv_nsec;
> > > +       }
> > > +
> > > +       if (statx && (statx->fa_valid[0] & NFS_FA_VALID_OWNER)) {
> > > +               sattr.ia_valid |= ATTR_UID;
> > > +               sattr.ia_uid = statx->fa_owner_uid;
> > > +       }
> > > +
> > > +       if (statx && (statx->fa_valid[0] &
> > > NFS_FA_VALID_OWNER_GROUP)) {
> > > +               sattr.ia_valid |= ATTR_GID;
> > > +               sattr.ia_gid = statx->fa_group_gid;
> > > +       }
> > > +
> > > +       if (statx && (statx->fa_valid[0] & NFS_FA_VALID_SIZE)) {
> > > +               sattr.ia_valid |= ATTR_SIZE;
> > > +               sattr.ia_size = statx->fa_size;
> > > +       }
> > > +
> > > +       nfs4_stateid_copy(&arg.stateid, &zero_stateid);
> > > +
> > > +       status = nfs4_call_sync(server->client, server, &msg,
> > > &arg.seq_args, &res.seq_res, 1);
> > > +       if (!status) {
> > > +               if (statx->fa_valid[0] & statx_win) {
> > > +                       struct nfs_inode *nfsi = NFS_I(inode);
> > > +
> > > +                       spin_lock(&inode->i_lock);
> > > +                       if (statx->fa_valid[0] &
> > > NFS_FA_VALID_TIME_CREATE)
> > > +                               nfsi->btime = statx->fa_btime;
> > > +                       if (statx->fa_valid[0] &
> > > NFS_FA_VALID_TIME_BACKUP)
> > > +                               nfsi->timebackup = statx-
> > > >fa_time_backup;
> > > +                       if (statx->fa_valid[0] &
> > > NFS_FA_VALID_ARCHIVE)
> > > +                               nfsi->archive = (statx->fa_flags &
> > > +                                               
> > > NFS_FA_FLAG_ARCHIVE) != 0;
> > > +                       if (statx->fa_valid[0] &
> > > NFS_FA_VALID_SYSTEM)
> > > +                               nfsi->system = (statx->fa_flags &
> > > +                                               NFS_FA_FLAG_SYSTEM)
> > > != 0;
> > > +                       if (statx->fa_valid[0] &
> > > NFS_FA_VALID_HIDDEN)
> > > +                               nfsi->hidden = (statx->fa_flags &
> > > +                                               NFS_FA_FLAG_HIDDEN)
> > > != 0;
> > > +                       if (statx->fa_valid[0] &
> > > NFS_FA_VALID_OFFLINE)
> > > +                               nfsi->offline = (statx->fa_flags &
> > > +                                               
> > > NFS_FA_FLAG_OFFLINE) != 0;
> > > +
> > > +                       nfsi->cache_validity &=
> > > ~NFS_INO_INVALID_CTIME;
> > > +                       if (fattr->valid & NFS_ATTR_FATTR_CTIME)
> > > +                               inode->i_ctime = fattr->ctime;
> > > +                       else
> > > +                               nfs_set_cache_invalid(
> > > +                                       inode,
> > > NFS_INO_INVALID_CHANGE |
> > > +                                                 
> > > NFS_INO_INVALID_CTIME);
> > > +                       spin_unlock(&inode->i_lock);
> > > +               }
> > > +
> > > +               nfs_setattr_update_inode(inode, &sattr, fattr);
> > > +       } else
> > > +               dprintk("%s failed: %d\n", __func__, status);
> > > +
> > > +       return status;
> > > +}
> > > +
> > > +int nfs4_set_nfs4_statx(struct inode *inode,
> > > +               struct nfs4_statx *statx,
> > > +               struct nfs_fattr *fattr)
> > > +{
> > > +       struct nfs4_exception exception = { };
> > > +       struct nfs_server *server = NFS_SERVER(inode);
> > > +       int err;
> > > +
> > > +       do {
> > > +               err = nfs4_handle_exception(server,
> > > +                               _nfs4_set_nfs4_statx(inode, statx,
> > > fattr),
> > > +                               &exception);
> > > +       } while (exception.retry);
> > > +       return err;
> > > +}
> > > +
> > >  /**
> > >   * nfs4_proc_get_locations - discover locations for a migrated
> > > FSID
> > >   * @inode: inode on FSID that is migrating
> > > @@ -10419,6 +10542,7 @@ const struct nfs_rpc_ops nfs_v4_clientops =
> > > {
> > >         .dir_inode_ops  = &nfs4_dir_inode_operations,
> > >         .file_inode_ops = &nfs4_file_inode_operations,
> > >         .file_ops       = &nfs4_file_operations,
> > > +       .dir_ops        = &nfs4_dir_operations,
> > >         .getroot        = nfs4_proc_get_root,
> > >         .submount       = nfs4_submount,
> > >         .try_get_tree   = nfs4_try_get_tree,
> > > diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
> > > index d2c240effc87..e5300d7ed712 100644
> > > --- a/fs/nfs/nfs4xdr.c
> > > +++ b/fs/nfs/nfs4xdr.c
> > > @@ -129,12 +129,15 @@ static int decode_layoutget(struct xdr_stream
> > > *xdr, struct rpc_rqst *req,
> > >                                 nfs4_fattr_value_maxsz)
> > >  #define decode_getattr_maxsz    (op_decode_hdr_maxsz +
> > > nfs4_fattr_maxsz)
> > >  #define encode_attrs_maxsz     (nfs4_fattr_bitmap_maxsz + \
> > > -                                1 + 2 + 1 + \
> > > +                                1 + 2 + 1 + 1 + 1 + \
> > >                                 nfs4_owner_maxsz + \
> > >                                 nfs4_group_maxsz + \
> > > -                               nfs4_label_maxsz + \
> > > +                               1 + \
> > > +                               1 + nfstime4_maxsz + \
> > > +                               nfstime4_maxsz + nfstime4_maxsz + \
> > >                                 1 + nfstime4_maxsz + \
> > > -                               1 + nfstime4_maxsz)
> > > +                               nfs4_label_maxsz + \
> > > +                               2)
> > >  #define encode_savefh_maxsz     (op_encode_hdr_maxsz)
> > >  #define decode_savefh_maxsz     (op_decode_hdr_maxsz)
> > >  #define encode_restorefh_maxsz  (op_encode_hdr_maxsz)
> > > @@ -1081,6 +1084,7 @@ xdr_encode_nfstime4(__be32 *p, const struct
> > > timespec64 *t)
> > >  static void encode_attrs(struct xdr_stream *xdr, const struct
> > > iattr *iap,
> > >                                 const struct nfs4_label *label,
> > >                                 const umode_t *umask,
> > > +                               const struct nfs4_statx *statx,
> > >                                 const struct nfs_server *server,
> > >                                 const uint32_t attrmask[])
> > >  {
> > > @@ -1153,6 +1157,34 @@ static void encode_attrs(struct xdr_stream
> > > *xdr, const struct iattr *iap,
> > >                 }
> > >         }
> > >  
> > > +       if (statx && (statx->fa_valid[0] &
> > > NFS_FA_VALID_TIME_BACKUP) &&
> > > +           (attrmask[1] & FATTR4_WORD1_TIME_BACKUP)) {
> > > +               bmval[1] |= FATTR4_WORD1_TIME_BACKUP;
> > > +               len += (nfstime4_maxsz << 2);
> > > +       }
> > > +       if (statx && (statx->fa_valid[0] &
> > > NFS_FA_VALID_TIME_CREATE) &&
> > > +           (attrmask[1] & FATTR4_WORD1_TIME_CREATE)) {
> > > +               bmval[1] |= FATTR4_WORD1_TIME_CREATE;
> > > +               len += (nfstime4_maxsz << 2);
> > > +       }
> > > +
> > > +       if (statx && (statx->fa_valid[0] & NFS_FA_VALID_ARCHIVE) &&
> > > +          (attrmask[0] & FATTR4_WORD0_ARCHIVE)) {
> > > +               bmval[0] |= FATTR4_WORD0_ARCHIVE;
> > > +               len += 4;
> > > +       }
> > > +       if (statx && (statx->fa_valid[0] & NFS_FA_VALID_HIDDEN) &&
> > > +          (attrmask[0] & FATTR4_WORD0_HIDDEN)) {
> > > +               bmval[0] |= FATTR4_WORD0_HIDDEN;
> > > +               len += 4;
> > > +       }
> > > +
> > > +       if (statx && (statx->fa_valid[0] & NFS_FA_VALID_SYSTEM) &&
> > > +          (attrmask[1] & FATTR4_WORD1_SYSTEM)) {
> > > +               bmval[1] |= FATTR4_WORD1_SYSTEM;
> > > +               len += 4;
> > > +       }
> > > +
> > >         if (label && (attrmask[2] & FATTR4_WORD2_SECURITY_LABEL)) {
> > >                 len += 4 + 4 + 4 + (XDR_QUADLEN(label->len) << 2);
> > >                 bmval[2] |= FATTR4_WORD2_SECURITY_LABEL;
> > > @@ -1163,12 +1195,21 @@ static void encode_attrs(struct xdr_stream
> > > *xdr, const struct iattr *iap,
> > >  
> > >         if (bmval[0] & FATTR4_WORD0_SIZE)
> > >                 p = xdr_encode_hyper(p, iap->ia_size);
> > > +       if (bmval[0] & FATTR4_WORD0_ARCHIVE)
> > > +               *p++ = (statx->fa_flags & NFS_FA_FLAG_ARCHIVE) ?
> > > +                       cpu_to_be32(1) : cpu_to_be32(0);
> > > +       if (bmval[0] & FATTR4_WORD0_HIDDEN)
> > > +               *p++ = (statx->fa_flags & NFS_FA_FLAG_HIDDEN) ?
> > > +                       cpu_to_be32(1) : cpu_to_be32(0);
> > >         if (bmval[1] & FATTR4_WORD1_MODE)
> > >                 *p++ = cpu_to_be32(iap->ia_mode & S_IALLUGO);
> > >         if (bmval[1] & FATTR4_WORD1_OWNER)
> > >                 p = xdr_encode_opaque(p, owner_name,
> > > owner_namelen);
> > >         if (bmval[1] & FATTR4_WORD1_OWNER_GROUP)
> > >                 p = xdr_encode_opaque(p, owner_group,
> > > owner_grouplen);
> > > +       if (bmval[1] & FATTR4_WORD1_SYSTEM)
> > > +               *p++ = (statx->fa_flags & NFS_FA_FLAG_SYSTEM) ?
> > > +                       cpu_to_be32(1) : cpu_to_be32(0);
> > >         if (bmval[1] & FATTR4_WORD1_TIME_ACCESS_SET) {
> > >                 if (iap->ia_valid & ATTR_ATIME_SET) {
> > >                         *p++ =
> > > cpu_to_be32(NFS4_SET_TO_CLIENT_TIME);
> > > @@ -1176,6 +1217,10 @@ static void encode_attrs(struct xdr_stream
> > > *xdr, const struct iattr *iap,
> > >                 } else
> > >                         *p++ =
> > > cpu_to_be32(NFS4_SET_TO_SERVER_TIME);
> > >         }
> > > +       if (bmval[1] & FATTR4_WORD1_TIME_BACKUP)
> > > +               p = xdr_encode_nfstime4(p, &statx->fa_time_backup);
> > > +       if (bmval[1] & FATTR4_WORD1_TIME_CREATE)
> > > +               p = xdr_encode_nfstime4(p, &statx->fa_btime);
> > >         if (bmval[1] & FATTR4_WORD1_TIME_MODIFY_SET) {
> > >                 if (iap->ia_valid & ATTR_MTIME_SET) {
> > >                         *p++ =
> > > cpu_to_be32(NFS4_SET_TO_CLIENT_TIME);
> > > @@ -1248,7 +1293,7 @@ static void encode_create(struct xdr_stream
> > > *xdr, const struct nfs4_create_arg *
> > >  
> > >         encode_string(xdr, create->name->len, create->name->name);
> > >         encode_attrs(xdr, create->attrs, create->label, &create-
> > > >umask,
> > > -                       create->server, create->server-
> > > >attr_bitmask);
> > > +                    NULL, create->server, create->server-
> > > >attr_bitmask);
> > >  }
> > >  
> > >  static void encode_getattr(struct xdr_stream *xdr,
> > > @@ -1434,12 +1479,12 @@ static inline void encode_createmode(struct
> > > xdr_stream *xdr, const struct nfs_op
> > >         case NFS4_CREATE_UNCHECKED:
> > >                 *p = cpu_to_be32(NFS4_CREATE_UNCHECKED);
> > >                 encode_attrs(xdr, arg->u.attrs, arg->label, &arg-
> > > >umask,
> > > -                               arg->server, arg->server-
> > > >attr_bitmask);
> > > +                            NULL, arg->server, arg->server-
> > > >attr_bitmask);
> > >                 break;
> > >         case NFS4_CREATE_GUARDED:
> > >                 *p = cpu_to_be32(NFS4_CREATE_GUARDED);
> > >                 encode_attrs(xdr, arg->u.attrs, arg->label, &arg-
> > > >umask,
> > > -                               arg->server, arg->server-
> > > >attr_bitmask);
> > > +                            NULL, arg->server, arg->server-
> > > >attr_bitmask);
> > >                 break;
> > >         case NFS4_CREATE_EXCLUSIVE:
> > >                 *p = cpu_to_be32(NFS4_CREATE_EXCLUSIVE);
> > > @@ -1449,7 +1494,7 @@ static inline void encode_createmode(struct
> > > xdr_stream *xdr, const struct nfs_op
> > >                 *p = cpu_to_be32(NFS4_CREATE_EXCLUSIVE4_1);
> > >                 encode_nfs4_verifier(xdr, &arg->u.verifier);
> > >                 encode_attrs(xdr, arg->u.attrs, arg->label, &arg-
> > > >umask,
> > > -                               arg->server, arg->server-
> > > >exclcreat_bitmask);
> > > +                            NULL, arg->server, arg->server-
> > > >exclcreat_bitmask);
> > >         }
> > >  }
> > >  
> > > @@ -1712,8 +1757,8 @@ static void encode_setattr(struct xdr_stream
> > > *xdr, const struct nfs_setattrargs
> > >  {
> > >         encode_op_hdr(xdr, OP_SETATTR, decode_setattr_maxsz, hdr);
> > >         encode_nfs4_stateid(xdr, &arg->stateid);
> > > -       encode_attrs(xdr, arg->iap, arg->label, NULL, server,
> > > -                       server->attr_bitmask);
> > > +       encode_attrs(xdr, arg->iap, arg->label, NULL, arg->statx,
> > > server,
> > > +                    server->attr_bitmask);
> > >  }
> > >  
> > >  static void encode_setclientid(struct xdr_stream *xdr, const
> > > struct nfs4_setclientid *setclientid, struct compound_hdr *hdr)
> > > diff --git a/fs/nfs/nfstrace.c b/fs/nfs/nfstrace.c
> > > index 5d1bfccbb4da..0b88deb0216e 100644
> > > --- a/fs/nfs/nfstrace.c
> > > +++ b/fs/nfs/nfstrace.c
> > > @@ -9,6 +9,11 @@
> > >  #define CREATE_TRACE_POINTS
> > >  #include "nfstrace.h"
> > >  
> > > +EXPORT_TRACEPOINT_SYMBOL_GPL(nfs_ioctl_file_statx_get_enter);
> > > +EXPORT_TRACEPOINT_SYMBOL_GPL(nfs_ioctl_file_statx_get_exit);
> > > +EXPORT_TRACEPOINT_SYMBOL_GPL(nfs_ioctl_file_statx_set_enter);
> > > +EXPORT_TRACEPOINT_SYMBOL_GPL(nfs_ioctl_file_statx_set_exit);
> > > +
> > >  EXPORT_TRACEPOINT_SYMBOL_GPL(nfs_fsync_enter);
> > >  EXPORT_TRACEPOINT_SYMBOL_GPL(nfs_fsync_exit);
> > >  EXPORT_TRACEPOINT_SYMBOL_GPL(nfs_xdr_status);
> > > diff --git a/fs/nfs/nfstrace.h b/fs/nfs/nfstrace.h
> > > index 2ef7cff8a4ba..b67dd087fb47 100644
> > > --- a/fs/nfs/nfstrace.h
> > > +++ b/fs/nfs/nfstrace.h
> > > @@ -166,6 +166,11 @@ DEFINE_NFS_INODE_EVENT_DONE(nfs_fsync_exit);
> > >  DEFINE_NFS_INODE_EVENT(nfs_access_enter);
> > >  DEFINE_NFS_INODE_EVENT_DONE(nfs_set_cache_invalid);
> > >  
> > > +DEFINE_NFS_INODE_EVENT(nfs_ioctl_file_statx_get_enter);
> > > +DEFINE_NFS_INODE_EVENT_DONE(nfs_ioctl_file_statx_get_exit);
> > > +DEFINE_NFS_INODE_EVENT(nfs_ioctl_file_statx_set_enter);
> > > +DEFINE_NFS_INODE_EVENT_DONE(nfs_ioctl_file_statx_set_exit);
> > > +
> > >  TRACE_EVENT(nfs_access_exit,
> > >                 TP_PROTO(
> > >                         const struct inode *inode,
> > > diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c
> > > index 73dcaa99fa9b..8fd96d93630a 100644
> > > --- a/fs/nfs/proc.c
> > > +++ b/fs/nfs/proc.c
> > > @@ -717,6 +717,7 @@ const struct nfs_rpc_ops nfs_v2_clientops = {
> > >         .dir_inode_ops  = &nfs_dir_inode_operations,
> > >         .file_inode_ops = &nfs_file_inode_operations,
> > >         .file_ops       = &nfs_file_operations,
> > > +       .dir_ops        = &nfs_dir_operations,
> > >         .getroot        = nfs_proc_get_root,
> > >         .submount       = nfs_submount,
> > >         .try_get_tree   = nfs_try_get_tree,
> > > diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
> > > index 058fc11338d9..0c3a5859f7f3 100644
> > > --- a/include/linux/nfs_fs.h
> > > +++ b/include/linux/nfs_fs.h
> > > @@ -501,6 +501,7 @@ extern __be32 root_nfs_parse_addr(char *name);
> > > /*__init*/
> > >  extern const struct file_operations nfs_file_operations;
> > >  #if IS_ENABLED(CONFIG_NFS_V4)
> > >  extern const struct file_operations nfs4_file_operations;
> > > +extern const struct file_operations nfs4_dir_operations;
> > >  #endif /* CONFIG_NFS_V4 */
> > >  extern const struct address_space_operations nfs_file_aops;
> > >  extern const struct address_space_operations nfs_dir_aops;
> > > diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
> > > index 0d5b11c1bfec..9ce61f680a13 100644
> > > --- a/include/linux/nfs_xdr.h
> > > +++ b/include/linux/nfs_xdr.h
> > > @@ -812,6 +812,7 @@ struct nfs_createargs {
> > >         struct iattr *          sattr;
> > >  };
> > >  
> > > +struct nfs4_statx;
> > >  struct nfs_setattrargs {
> > >         struct nfs4_sequence_args       seq_args;
> > >         struct nfs_fh *                 fh;
> > > @@ -820,6 +821,7 @@ struct nfs_setattrargs {
> > >         const struct nfs_server *       server; /* Needed for name
> > > mapping */
> > >         const u32 *                     bitmask;
> > >         const struct nfs4_label         *label;
> > > +       const struct nfs4_statx         *statx;
> > >  };
> > >  
> > >  struct nfs_setaclargs {
> > > @@ -1744,6 +1746,7 @@ struct nfs_rpc_ops {
> > >         const struct inode_operations *dir_inode_ops;
> > >         const struct inode_operations *file_inode_ops;
> > >         const struct file_operations *file_ops;
> > > +       const struct file_operations *dir_ops;
> > >         const struct nlmclnt_operations *nlmclnt_ops;
> > >  
> > >         int     (*getroot) (struct nfs_server *, struct nfs_fh *,
> > > diff --git a/include/uapi/linux/nfs.h b/include/uapi/linux/nfs.h
> > > index 946cb62d64b0..df87da39bc43 100644
> > > --- a/include/uapi/linux/nfs.h
> > > +++ b/include/uapi/linux/nfs.h
> > > @@ -9,6 +9,8 @@
> > >  #define _UAPI_LINUX_NFS_H
> > >  
> > >  #include <linux/types.h>
> > > +#include <asm/byteorder.h>
> > > +#include <linux/time.h>
> > >  
> > >  #define NFS_PROGRAM    100003
> > >  #define NFS_PORT       2049
> > > @@ -35,6 +37,94 @@
> > >  
> > >  #define NFS_PIPE_DIRNAME "nfs"
> > >  
> > > +/* NFS ioctls */
> > > +#define NFS_IOC_FILE_STATX_GET _IOR('N', 2, struct
> > > nfs_ioctl_nfs4_statx)
> > > +#define NFS_IOC_FILE_STATX_SET _IOW('N', 3, struct
> > > nfs_ioctl_nfs4_statx)
> > > +
> > > +/* Options for struct nfs_ioctl_nfs4_statx */
> > > +#define NFS_FA_OPTIONS_SYNC_AS_STAT                    0x0000
> > > +#define NFS_FA_OPTIONS_FORCE_SYNC                      0x2000 /*
> > > See statx */
> > > +#define NFS_FA_OPTIONS_DONT_SYNC                       0x4000 /*
> > > See statx */
> > > +
> > > +#define NFS_FA_VALID_TIME_CREATE                       0x00001UL
> > > +#define NFS_FA_VALID_TIME_BACKUP                       0x00002UL
> > > +#define NFS_FA_VALID_ARCHIVE                           0x00004UL
> > > +#define NFS_FA_VALID_HIDDEN                            0x00008UL
> > > +#define NFS_FA_VALID_SYSTEM                            0x00010UL
> > > +#define NFS_FA_VALID_OWNER                             0x00020UL
> > > +#define NFS_FA_VALID_OWNER_GROUP                       0x00040UL
> > > +#define NFS_FA_VALID_ATIME                             0x00080UL
> > > +#define NFS_FA_VALID_MTIME                             0x00100UL
> > > +#define NFS_FA_VALID_CTIME                             0x00200UL
> > > +#define NFS_FA_VALID_OFFLINE                           0x00400UL
> > > +#define NFS_FA_VALID_MODE                              0x00800UL
> > > +#define NFS_FA_VALID_NLINK                             0x01000UL
> > > +#define NFS_FA_VALID_BLKSIZE                           0x02000UL
> > > +#define NFS_FA_VALID_INO                               0x04000UL
> > > +#define NFS_FA_VALID_DEV                               0x08000UL
> > > +#define NFS_FA_VALID_RDEV                              0x10000UL
> > > +#define NFS_FA_VALID_SIZE                              0x20000UL
> > > +#define NFS_FA_VALID_BLOCKS                            0x40000UL
> > > +
> > > +#define NFS_FA_VALID_ALL_ATTR_0 ( NFS_FA_VALID_TIME_CREATE | \
> > > +               NFS_FA_VALID_TIME_BACKUP | \
> > > +               NFS_FA_VALID_ARCHIVE | \
> > > +               NFS_FA_VALID_HIDDEN | \
> > > +               NFS_FA_VALID_SYSTEM | \
> > > +               NFS_FA_VALID_OWNER | \
> > > +               NFS_FA_VALID_OWNER_GROUP | \
> > > +               NFS_FA_VALID_ATIME | \
> > > +               NFS_FA_VALID_MTIME | \
> > > +               NFS_FA_VALID_CTIME | \
> > > +               NFS_FA_VALID_OFFLINE | \
> > > +               NFS_FA_VALID_MODE | \
> > > +               NFS_FA_VALID_NLINK | \
> > > +               NFS_FA_VALID_BLKSIZE | \
> > > +               NFS_FA_VALID_INO | \
> > > +               NFS_FA_VALID_DEV | \
> > > +               NFS_FA_VALID_RDEV | \
> > > +               NFS_FA_VALID_SIZE | \
> > > +               NFS_FA_VALID_BLOCKS)
> > > +
> > > +#define NFS_FA_FLAG_ARCHIVE                            (1UL << 0)
> > > +#define NFS_FA_FLAG_HIDDEN                             (1UL << 1)
> > > +#define NFS_FA_FLAG_SYSTEM                             (1UL << 2)
> > > +#define NFS_FA_FLAG_OFFLINE                            (1UL << 3)
> > > +
> > > +struct nfs_ioctl_timespec {
> > > +       __s64           tv_sec;
> > > +       __s64           tv_nsec;
> > > +};
> > > +
> > > +struct nfs_ioctl_nfs4_statx {
> > > +       __s32           real_fd;                /* real FD to use,
> > > +                                                  -1 means use
> > > current file */
> > > +       __u32           fa_options;
> > > +
> > > +       __u64           fa_request[2];          /* Attributes to
> > > retrieve */
> > > +       __u64           fa_valid[2];            /* Attributes set
> > > */
> > > +
> > > +       struct nfs_ioctl_timespec fa_time_backup;/* Backup time */
> > > +       struct nfs_ioctl_timespec fa_btime;     /* Birth time */
> > > +       __u64           fa_flags;               /* Flag attributes
> > > */
> > > +       /* Ordinary attributes follow */
> > > +       struct nfs_ioctl_timespec fa_atime;     /* Access time */
> > > +       struct nfs_ioctl_timespec fa_mtime;     /* Modify time */
> > > +       struct nfs_ioctl_timespec fa_ctime;     /* Change time */
> > > +       __u32           fa_owner_uid;           /* Owner User ID */
> > > +       __u32           fa_group_gid;           /* Primary Group ID
> > > */
> > > +       __u32           fa_mode;                /* Mode */
> > > +       __u32           fa_nlink;
> > > +       __u32           fa_blksize;
> > > +       __u32           fa_spare;               /* Alignment */
> > > +       __u64           fa_ino;
> > > +       __u32           fa_dev;
> > > +       __u32           fa_rdev;
> > > +       __s64           fa_size;
> > > +       __s64           fa_blocks;
> > > +       __u64           fa_padding[4];
> > > +};
> > > +
> > >  /*
> > >   * NFS stats. The good thing with these values is that NFSv3
> > > errors are
> > >   * a superset of NFSv2 errors (with the exception of NFSERR_WFLUSH
> > > which
> > > -- 
> > > 2.33.1
> 
> -- 
> Trond Myklebust
> Linux NFS client maintainer, Hammerspace
> trond.myklebust@hammerspace.com
> 
> 

^ permalink raw reply	[flat|nested] 30+ messages in thread

* RE: [PATCH 0/8] Support btime and other NFSv4 specific attributes
  2022-01-03 20:51   ` Trond Myklebust
@ 2022-01-05 15:05     ` Ondrej Valousek
  2022-01-05 15:10       ` Trond Myklebust
  2022-01-05 15:10       ` bfields
  0 siblings, 2 replies; 30+ messages in thread
From: Ondrej Valousek @ 2022-01-05 15:05 UTC (permalink / raw)
  To: Trond Myklebust, bfields, trondmy; +Cc: linux-nfs, anna.schumaker

Hi all,
Sorry for confusion and maybe dumb questions:
- The aim is to transfer these attributes via RFC8276 (File System Extended attributes in NFSv4)?
- AFAIK support for RFC8276 in NFS (only version 4.2) server is since kernel 5.9, right? NFS client supports these as well?
- The patches below implements the feature in both, nfs client AND server, right?

I am bit confused as "btime" does not seem to be stored as extended attribute in most local filesystems (checked ext4) but is in standard inode structure.
Thanks,
Ondrej


-----Original Message-----
From: Trond Myklebust <trondmy@hammerspace.com>
Sent: pondělí 3. ledna 2022 21:52
To: bfields@fieldses.org; trondmy@kernel.org
Cc: linux-nfs@vger.kernel.org; anna.schumaker@netapp.com
Subject: Re: [PATCH 0/8] Support btime and other NFSv4 specific attributes

On Mon, 2022-01-03 at 15:51 -0500, J. Bruce Fields wrote:
> On Fri, Dec 17, 2021 at 03:48:46PM -0500, trondmy@kernel.org wrote:
> > From: Trond Myklebust <trond.myklebust@hammerspace.com>
> >
> > NFSv4 has support for a number of extra attributes that are of
> > interest to Samba when it is used to re-export a filesystem to
> > Windows clients.
> > Aside from the btime, which is of interest in statx(), Windows
> > clients have an interest in determining the status of the 'hidden',
> > and 'system'
> > flags.
> > Backup programs want to read the 'archive' flags and the 'time
> > backup'
> > attribute.
> > Finally, the 'offline' flag can tell whether or not a file needs to
> > be staged by an HSM system before it can be read or written to.
> >
> > The patch series also adds an ioctl() to allow userspace retrieval
> > and setting of these attributes where appropriate. It also adds an
> > ioctl()
> > to allow retrieval of the raw NFSv4 ACCESS information, to allow
> > more fine grained determination of the user's access rights to a
> > file or directory. All of this information is of use for Samba.
>
> Same question, what filesystem and server are you testing against?
>

Same answer.

> --b.
>
> >
> > Anne Marie Merritt (3):
> >   nfs: Add timecreate to nfs inode
> >   nfs: Add 'archive', 'hidden' and 'system' fields to nfs inode
> >   nfs: Add 'time backup' to nfs inode
> >
> > Richard Sharpe (1):
> >   NFS: Support statx_get and statx_set ioctls
> >
> > Trond Myklebust (4):
> >   NFS: Expand the type of nfs_fattr->valid
> >   NFS: Return the file btime in the statx results when appropriate
> >   NFSv4: Support the offline bit
> >   NFSv4: Add an ioctl to allow retrieval of the NFS raw ACCESS mask
> >
> >  fs/nfs/dir.c              |  71 ++---
> >  fs/nfs/getroot.c          |   3 +-
> >  fs/nfs/inode.c            | 147 +++++++++-
> >  fs/nfs/internal.h         |  10 +
> >  fs/nfs/nfs3proc.c         |   1 +
> >  fs/nfs/nfs4_fs.h          |  31 +++
> >  fs/nfs/nfs4file.c         | 550
> > ++++++++++++++++++++++++++++++++++++++
> >  fs/nfs/nfs4proc.c         | 175 +++++++++++-
> >  fs/nfs/nfs4trace.h        |   8 +-
> >  fs/nfs/nfs4xdr.c          | 240 +++++++++++++++--
> >  fs/nfs/nfstrace.c         |   5 +
> >  fs/nfs/nfstrace.h         |   9 +-
> >  fs/nfs/proc.c             |   1 +
> >  include/linux/nfs4.h      |   1 +
> >  include/linux/nfs_fs.h    |  15 ++
> >  include/linux/nfs_fs_sb.h |   2 +-
> >  include/linux/nfs_xdr.h   |  80 ++++--
> >  include/uapi/linux/nfs.h  | 101 +++++++
> >  18 files changed, 1356 insertions(+), 94 deletions(-)
> >
> > --
> > 2.33.1

--
Trond Myklebust
Linux NFS client maintainer, Hammerspace trond.myklebust@hammerspace.com


Legal Disclaimer: This e-mail communication (and any attachment/s) is confidential and contains proprietary information, some or all of which may be legally privileged. It is intended solely for the use of the individual or entity to which it is addressed. Access to this email by anyone else is unauthorized. If you are not the intended recipient, any disclosure, copying, distribution or any action taken or omitted to be taken in reliance on it, is prohibited and may be unlawful.

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH 0/8] Support btime and other NFSv4 specific attributes
  2022-01-05 15:05     ` Ondrej Valousek
@ 2022-01-05 15:10       ` Trond Myklebust
  2022-01-05 15:10       ` bfields
  1 sibling, 0 replies; 30+ messages in thread
From: Trond Myklebust @ 2022-01-05 15:10 UTC (permalink / raw)
  To: bfields, ondrej.valousek.xm, trondmy; +Cc: linux-nfs, anna.schumaker

On Wed, 2022-01-05 at 15:05 +0000, Ondrej Valousek wrote:
> Hi all,
> Sorry for confusion and maybe dumb questions:
> - The aim is to transfer these attributes via RFC8276 (File System
> Extended attributes in NFSv4)?

No.

> - AFAIK support for RFC8276 in NFS (only version 4.2) server is since
> kernel 5.9, right? NFS client supports these as well?
> - The patches below implements the feature in both, nfs client AND
> server, right?
> 
> I am bit confused as "btime" does not seem to be stored as extended
> attribute in most local filesystems (checked ext4) but is in standard
> inode structure.

All these attributes are defined as regular attributes in rfc7530. All
this code does is add the standard NFSv4 encoders/decoders for these
attributes and adds the ioctl() to set/retrieve them all.

There is no need to hack the NFS protocol to retrieve or set them using
the xattr stuff.

> Thanks,
> Ondrej
> 
> 
> -----Original Message-----
> From: Trond Myklebust <trondmy@hammerspace.com>
> Sent: pondělí 3. ledna 2022 21:52
> To: bfields@fieldses.org; trondmy@kernel.org
> Cc: linux-nfs@vger.kernel.org; anna.schumaker@netapp.com
> Subject: Re: [PATCH 0/8] Support btime and other NFSv4 specific
> attributes
> 
> On Mon, 2022-01-03 at 15:51 -0500, J. Bruce Fields wrote:
> > On Fri, Dec 17, 2021 at 03:48:46PM -0500, trondmy@kernel.org wrote:
> > > From: Trond Myklebust <trond.myklebust@hammerspace.com>
> > > 
> > > NFSv4 has support for a number of extra attributes that are of
> > > interest to Samba when it is used to re-export a filesystem to
> > > Windows clients.
> > > Aside from the btime, which is of interest in statx(), Windows
> > > clients have an interest in determining the status of the
> > > 'hidden',
> > > and 'system'
> > > flags.
> > > Backup programs want to read the 'archive' flags and the 'time
> > > backup'
> > > attribute.
> > > Finally, the 'offline' flag can tell whether or not a file needs
> > > to
> > > be staged by an HSM system before it can be read or written to.
> > > 
> > > The patch series also adds an ioctl() to allow userspace
> > > retrieval
> > > and setting of these attributes where appropriate. It also adds
> > > an
> > > ioctl()
> > > to allow retrieval of the raw NFSv4 ACCESS information, to allow
> > > more fine grained determination of the user's access rights to a
> > > file or directory. All of this information is of use for Samba.
> > 
> > Same question, what filesystem and server are you testing against?
> > 
> 
> Same answer.
> 
> > --b.
> > 
> > > 
> > > Anne Marie Merritt (3):
> > >   nfs: Add timecreate to nfs inode
> > >   nfs: Add 'archive', 'hidden' and 'system' fields to nfs inode
> > >   nfs: Add 'time backup' to nfs inode
> > > 
> > > Richard Sharpe (1):
> > >   NFS: Support statx_get and statx_set ioctls
> > > 
> > > Trond Myklebust (4):
> > >   NFS: Expand the type of nfs_fattr->valid
> > >   NFS: Return the file btime in the statx results when
> > > appropriate
> > >   NFSv4: Support the offline bit
> > >   NFSv4: Add an ioctl to allow retrieval of the NFS raw ACCESS
> > > mask
> > > 
> > >  fs/nfs/dir.c              |  71 ++---
> > >  fs/nfs/getroot.c          |   3 +-
> > >  fs/nfs/inode.c            | 147 +++++++++-
> > >  fs/nfs/internal.h         |  10 +
> > >  fs/nfs/nfs3proc.c         |   1 +
> > >  fs/nfs/nfs4_fs.h          |  31 +++
> > >  fs/nfs/nfs4file.c         | 550
> > > ++++++++++++++++++++++++++++++++++++++
> > >  fs/nfs/nfs4proc.c         | 175 +++++++++++-
> > >  fs/nfs/nfs4trace.h        |   8 +-
> > >  fs/nfs/nfs4xdr.c          | 240 +++++++++++++++--
> > >  fs/nfs/nfstrace.c         |   5 +
> > >  fs/nfs/nfstrace.h         |   9 +-
> > >  fs/nfs/proc.c             |   1 +
> > >  include/linux/nfs4.h      |   1 +
> > >  include/linux/nfs_fs.h    |  15 ++
> > >  include/linux/nfs_fs_sb.h |   2 +-
> > >  include/linux/nfs_xdr.h   |  80 ++++--
> > >  include/uapi/linux/nfs.h  | 101 +++++++
> > >  18 files changed, 1356 insertions(+), 94 deletions(-)
> > > 
> > > --
> > > 2.33.1
> 
> --
> Trond Myklebust
> Linux NFS client maintainer, Hammerspace
> trond.myklebust@hammerspace.com
> 
> 
> Legal Disclaimer: This e-mail communication (and any attachment/s) is
> confidential and contains proprietary information, some or all of
> which may be legally privileged. It is intended solely for the use of
> the individual or entity to which it is addressed. Access to this
> email by anyone else is unauthorized. If you are not the intended
> recipient, any disclosure, copying, distribution or any action taken
> or omitted to be taken in reliance on it, is prohibited and may be
> unlawful.

-- 
Trond Myklebust
CTO, Hammerspace Inc
4984 El Camino Real, Suite 208
Los Altos, CA 94022
​
www.hammer.space


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH 0/8] Support btime and other NFSv4 specific attributes
  2022-01-05 15:05     ` Ondrej Valousek
  2022-01-05 15:10       ` Trond Myklebust
@ 2022-01-05 15:10       ` bfields
  2022-01-05 15:40         ` Ondrej Valousek
  1 sibling, 1 reply; 30+ messages in thread
From: bfields @ 2022-01-05 15:10 UTC (permalink / raw)
  To: Ondrej Valousek; +Cc: Trond Myklebust, trondmy, linux-nfs, anna.schumaker

On Wed, Jan 05, 2022 at 03:05:17PM +0000, Ondrej Valousek wrote:
> Sorry for confusion and maybe dumb questions:
> - The aim is to transfer these attributes via RFC8276 (File System Extended attributes in NFSv4)?

No, NFSv4 defines attributes for all of these.

RFC8276 attributes are purely for user-defined attribues, not for
anything that the server or filesystem gives specific meaning.

> - AFAIK support for RFC8276 in NFS (only version 4.2) server is since kernel 5.9, right? NFS client supports these as well?
> - The patches below implements the feature in both, nfs client AND server, right?

Client only.

These would probably be quite easy to support on the server side when
the filesystem supports them, but nobody's volunteered to implement
that yet; patches welcome.

--b.

^ permalink raw reply	[flat|nested] 30+ messages in thread

* RE: [PATCH 0/8] Support btime and other NFSv4 specific attributes
  2022-01-05 15:10       ` bfields
@ 2022-01-05 15:40         ` Ondrej Valousek
  2022-01-05 15:54           ` bfields
  0 siblings, 1 reply; 30+ messages in thread
From: Ondrej Valousek @ 2022-01-05 15:40 UTC (permalink / raw)
  To: bfields, Ondrej Valousek
  Cc: Trond Myklebust, trondmy, linux-nfs, anna.schumaker


>> - AFAIK support for RFC8276 in NFS (only version 4.2) server is since kernel 5.9, right? NFS client supports these as well?
>> - The patches below implements the feature in both, nfs client AND server, right?

> Client only.

Right, but then it will be only useful if we use non-linux based NFS server right?

I mean simply:
1. $ stat /tmp/foo.txt   --> shows birth date
2. # exportfs \*/tmp
3. # mount 127.0.0.1:/tmp /mnt
4. $ stat /mnt/foo.txt   --> no birth date shown
Legal Disclaimer: This e-mail communication (and any attachment/s) is confidential and contains proprietary information, some or all of which may be legally privileged. It is intended solely for the use of the individual or entity to which it is addressed. Access to this email by anyone else is unauthorized. If you are not the intended recipient, any disclosure, copying, distribution or any action taken or omitted to be taken in reliance on it, is prohibited and may be unlawful.

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH 0/8] Support btime and other NFSv4 specific attributes
  2022-01-05 15:40         ` Ondrej Valousek
@ 2022-01-05 15:54           ` bfields
  2022-01-06  9:31             ` Ondrej Valousek
  0 siblings, 1 reply; 30+ messages in thread
From: bfields @ 2022-01-05 15:54 UTC (permalink / raw)
  To: Ondrej Valousek; +Cc: Trond Myklebust, trondmy, linux-nfs, anna.schumaker

On Wed, Jan 05, 2022 at 03:40:19PM +0000, Ondrej Valousek wrote:
> 
> >> - AFAIK support for RFC8276 in NFS (only version 4.2) server is since kernel 5.9, right? NFS client supports these as well?
> >> - The patches below implements the feature in both, nfs client AND server, right?
> 
> > Client only.
> 
> Right, but then it will be only useful if we use non-linux based NFS server right?
> 
> I mean simply:
> 1. $ stat /tmp/foo.txt   --> shows birth date
> 2. # exportfs \*/tmp
> 3. # mount 127.0.0.1:/tmp /mnt
> 4. $ stat /mnt/foo.txt   --> no birth date shown

Right.

Fixing that's likely just a few lines of code added to
fs/nfsd/nfs4xdr.c:nfsd4_encode_fattr().  Patches welcome.

--b.

^ permalink raw reply	[flat|nested] 30+ messages in thread

* RE: [PATCH 0/8] Support btime and other NFSv4 specific attributes
  2022-01-05 15:54           ` bfields
@ 2022-01-06  9:31             ` Ondrej Valousek
  2022-01-06 14:15               ` Trond Myklebust
  0 siblings, 1 reply; 30+ messages in thread
From: Ondrej Valousek @ 2022-01-06  9:31 UTC (permalink / raw)
  To: bfields, Ondrej Valousek
  Cc: Trond Myklebust, trondmy, linux-nfs, anna.schumaker

Looks like this should do I guess...

diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 5a93a5db4fb0..be47e1dd6da5 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -3265,6 +3265,14 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
                p = xdr_encode_hyper(p, (s64)stat.mtime.tv_sec);
                *p++ = cpu_to_be32(stat.mtime.tv_nsec);
        }
+       /* support for btime here */
+        if (bmval1 & FATTR4_WORD1_TIME_CREATE) {
+                p = xdr_reserve_space(xdr, 12);
+                if (!p)
+                        goto out_resource;
+                p = xdr_encode_hyper(p, (s64)stat.btime.tv_sec);
+                *p++ = cpu_to_be32(stat.btime.tv_nsec);
+        }
        if (bmval1 & FATTR4_WORD1_MOUNTED_ON_FILEID) {
                struct kstat parent_stat;
                u64 ino = stat.ino;



-----Original Message-----
From: bfields@fieldses.org <bfields@fieldses.org>
Sent: středa 5. ledna 2022 16:55
To: Ondrej Valousek <ondrej.valousek.xm@renesas.com>
Cc: Trond Myklebust <trondmy@hammerspace.com>; trondmy@kernel.org; linux-nfs@vger.kernel.org; anna.schumaker@netapp.com
Subject: Re: [PATCH 0/8] Support btime and other NFSv4 specific attributes

On Wed, Jan 05, 2022 at 03:40:19PM +0000, Ondrej Valousek wrote:
>
> >> - AFAIK support for RFC8276 in NFS (only version 4.2) server is since kernel 5.9, right? NFS client supports these as well?
> >> - The patches below implements the feature in both, nfs client AND server, right?
>
> > Client only.
>
> Right, but then it will be only useful if we use non-linux based NFS server right?
>
> I mean simply:
> 1. $ stat /tmp/foo.txt   --> shows birth date
> 2. # exportfs \*/tmp
> 3. # mount 127.0.0.1:/tmp /mnt
> 4. $ stat /mnt/foo.txt   --> no birth date shown

Right.

Fixing that's likely just a few lines of code added to fs/nfsd/nfs4xdr.c:nfsd4_encode_fattr().  Patches welcome.

--b.
Legal Disclaimer: This e-mail communication (and any attachment/s) is confidential and contains proprietary information, some or all of which may be legally privileged. It is intended solely for the use of the individual or entity to which it is addressed. Access to this email by anyone else is unauthorized. If you are not the intended recipient, any disclosure, copying, distribution or any action taken or omitted to be taken in reliance on it, is prohibited and may be unlawful.

^ permalink raw reply related	[flat|nested] 30+ messages in thread

* Re: [PATCH 0/8] Support btime and other NFSv4 specific attributes
  2022-01-06  9:31             ` Ondrej Valousek
@ 2022-01-06 14:15               ` Trond Myklebust
  2022-01-06 14:19                 ` Ondrej Valousek
  0 siblings, 1 reply; 30+ messages in thread
From: Trond Myklebust @ 2022-01-06 14:15 UTC (permalink / raw)
  To: bfields, ondrej.valousek.xm; +Cc: linux-nfs, anna.schumaker, trondmy

On Thu, 2022-01-06 at 09:31 +0000, Ondrej Valousek wrote:
> Looks like this should do I guess...
> 
> diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
> index 5a93a5db4fb0..be47e1dd6da5 100644
> --- a/fs/nfsd/nfs4xdr.c
> +++ b/fs/nfsd/nfs4xdr.c
> @@ -3265,6 +3265,14 @@ nfsd4_encode_fattr(struct xdr_stream *xdr,
> struct svc_fh *fhp,
>                 p = xdr_encode_hyper(p, (s64)stat.mtime.tv_sec);
>                 *p++ = cpu_to_be32(stat.mtime.tv_nsec);
>         }
> +       /* support for btime here */
> +        if (bmval1 & FATTR4_WORD1_TIME_CREATE) {
> +                p = xdr_reserve_space(xdr, 12);
> +                if (!p)
> +                        goto out_resource;
> +                p = xdr_encode_hyper(p, (s64)stat.btime.tv_sec);
> +                *p++ = cpu_to_be32(stat.btime.tv_nsec);
> +        }
>         if (bmval1 & FATTR4_WORD1_MOUNTED_ON_FILEID) {
>                 struct kstat parent_stat;
>                 u64 ino = stat.ino;
> 

You also need to update the value of NFSD4_SUPPORTED_ATTRS_WORD1 to
reflect the new support for FATTR4_WORD1_TIME_CREATE.


-- 
Trond Myklebust
Linux NFS client maintainer, Hammerspace
trond.myklebust@hammerspace.com



^ permalink raw reply	[flat|nested] 30+ messages in thread

* RE: [PATCH 0/8] Support btime and other NFSv4 specific attributes
  2022-01-06 14:15               ` Trond Myklebust
@ 2022-01-06 14:19                 ` Ondrej Valousek
  2022-01-06 14:28                   ` bfields
  0 siblings, 1 reply; 30+ messages in thread
From: Ondrej Valousek @ 2022-01-06 14:19 UTC (permalink / raw)
  To: Trond Myklebust, bfields, ondrej.valousek.xm
  Cc: linux-nfs, anna.schumaker, trondmy

> You also need to update the value of NFSD4_SUPPORTED_ATTRS_WORD1 to reflect the new support for FATTR4_WORD1_TIME_CREATE.

Yes, I realized that one shortly after I sent the mail.
Just going to try this patch:

[ondrejv@skynet19 /opt/kernel/linux-git/fs/nfsd]$ git diff
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 5a93a5db4fb0..be47e1dd6da5 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -3265,6 +3265,14 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
                p = xdr_encode_hyper(p, (s64)stat.mtime.tv_sec);
                *p++ = cpu_to_be32(stat.mtime.tv_nsec);
        }
+       /* support for btime here */
+        if (bmval1 & FATTR4_WORD1_TIME_CREATE) {
+                p = xdr_reserve_space(xdr, 12);
+                if (!p)
+                        goto out_resource;
+                p = xdr_encode_hyper(p, (s64)stat.btime.tv_sec);
+                *p++ = cpu_to_be32(stat.btime.tv_nsec);
+        }
        if (bmval1 & FATTR4_WORD1_MOUNTED_ON_FILEID) {
                struct kstat parent_stat;
                u64 ino = stat.ino;
diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h
index 498e5a489826..5ef056ce7591 100644
--- a/fs/nfsd/nfsd.h
+++ b/fs/nfsd/nfsd.h
@@ -364,7 +364,7 @@ void                nfsd_lockd_shutdown(void);
  | FATTR4_WORD1_OWNER          | FATTR4_WORD1_OWNER_GROUP  | FATTR4_WORD1_RAWDEV           \
  | FATTR4_WORD1_SPACE_AVAIL     | FATTR4_WORD1_SPACE_FREE   | FATTR4_WORD1_SPACE_TOTAL      \
  | FATTR4_WORD1_SPACE_USED      | FATTR4_WORD1_TIME_ACCESS  | FATTR4_WORD1_TIME_ACCESS_SET  \
- | FATTR4_WORD1_TIME_DELTA   | FATTR4_WORD1_TIME_METADATA    \
+ | FATTR4_WORD1_TIME_DELTA   | FATTR4_WORD1_TIME_METADATA   | FATTR4_WORD1_TIME_CREATE      \
  | FATTR4_WORD1_TIME_MODIFY     | FATTR4_WORD1_TIME_MODIFY_SET | FATTR4_WORD1_MOUNTED_ON_FILEID)

 #define NFSD4_SUPPORTED_ATTRS_WORD2 0


... will see

Legal Disclaimer: This e-mail communication (and any attachment/s) is confidential and contains proprietary information, some or all of which may be legally privileged. It is intended solely for the use of the individual or entity to which it is addressed. Access to this email by anyone else is unauthorized. If you are not the intended recipient, any disclosure, copying, distribution or any action taken or omitted to be taken in reliance on it, is prohibited and may be unlawful.

^ permalink raw reply related	[flat|nested] 30+ messages in thread

* Re: [PATCH 0/8] Support btime and other NFSv4 specific attributes
  2022-01-06 14:19                 ` Ondrej Valousek
@ 2022-01-06 14:28                   ` bfields
  2022-01-06 14:36                     ` bfields
  0 siblings, 1 reply; 30+ messages in thread
From: bfields @ 2022-01-06 14:28 UTC (permalink / raw)
  To: Ondrej Valousek; +Cc: Trond Myklebust, linux-nfs, anna.schumaker, trondmy

On Thu, Jan 06, 2022 at 02:19:22PM +0000, Ondrej Valousek wrote:
> > You also need to update the value of NFSD4_SUPPORTED_ATTRS_WORD1 to reflect the new support for FATTR4_WORD1_TIME_CREATE.
> 
> Yes, I realized that one shortly after I sent the mail.
> Just going to try this patch:

Thanks!

Don't we want to vary support depending on the filesystem, though?  Is
there a way to query that?

--b.

> 
> [ondrejv@skynet19 /opt/kernel/linux-git/fs/nfsd]$ git diff
> diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
> index 5a93a5db4fb0..be47e1dd6da5 100644
> --- a/fs/nfsd/nfs4xdr.c
> +++ b/fs/nfsd/nfs4xdr.c
> @@ -3265,6 +3265,14 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
>                 p = xdr_encode_hyper(p, (s64)stat.mtime.tv_sec);
>                 *p++ = cpu_to_be32(stat.mtime.tv_nsec);
>         }
> +       /* support for btime here */
> +        if (bmval1 & FATTR4_WORD1_TIME_CREATE) {
> +                p = xdr_reserve_space(xdr, 12);
> +                if (!p)
> +                        goto out_resource;
> +                p = xdr_encode_hyper(p, (s64)stat.btime.tv_sec);
> +                *p++ = cpu_to_be32(stat.btime.tv_nsec);
> +        }
>         if (bmval1 & FATTR4_WORD1_MOUNTED_ON_FILEID) {
>                 struct kstat parent_stat;
>                 u64 ino = stat.ino;
> diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h
> index 498e5a489826..5ef056ce7591 100644
> --- a/fs/nfsd/nfsd.h
> +++ b/fs/nfsd/nfsd.h
> @@ -364,7 +364,7 @@ void                nfsd_lockd_shutdown(void);
>   | FATTR4_WORD1_OWNER          | FATTR4_WORD1_OWNER_GROUP  | FATTR4_WORD1_RAWDEV           \
>   | FATTR4_WORD1_SPACE_AVAIL     | FATTR4_WORD1_SPACE_FREE   | FATTR4_WORD1_SPACE_TOTAL      \
>   | FATTR4_WORD1_SPACE_USED      | FATTR4_WORD1_TIME_ACCESS  | FATTR4_WORD1_TIME_ACCESS_SET  \
> - | FATTR4_WORD1_TIME_DELTA   | FATTR4_WORD1_TIME_METADATA    \
> + | FATTR4_WORD1_TIME_DELTA   | FATTR4_WORD1_TIME_METADATA   | FATTR4_WORD1_TIME_CREATE      \
>   | FATTR4_WORD1_TIME_MODIFY     | FATTR4_WORD1_TIME_MODIFY_SET | FATTR4_WORD1_MOUNTED_ON_FILEID)
> 
>  #define NFSD4_SUPPORTED_ATTRS_WORD2 0
> 
> 
> ... will see
> 
> Legal Disclaimer: This e-mail communication (and any attachment/s) is confidential and contains proprietary information, some or all of which may be legally privileged. It is intended solely for the use of the individual or entity to which it is addressed. Access to this email by anyone else is unauthorized. If you are not the intended recipient, any disclosure, copying, distribution or any action taken or omitted to be taken in reliance on it, is prohibited and may be unlawful.

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH 0/8] Support btime and other NFSv4 specific attributes
  2022-01-06 14:28                   ` bfields
@ 2022-01-06 14:36                     ` bfields
  2022-01-06 14:52                       ` Ondrej Valousek
  0 siblings, 1 reply; 30+ messages in thread
From: bfields @ 2022-01-06 14:36 UTC (permalink / raw)
  To: Ondrej Valousek; +Cc: Trond Myklebust, linux-nfs, anna.schumaker, trondmy

On Thu, Jan 06, 2022 at 09:28:12AM -0500, bfields@fieldses.org wrote:
> On Thu, Jan 06, 2022 at 02:19:22PM +0000, Ondrej Valousek wrote:
> > > You also need to update the value of NFSD4_SUPPORTED_ATTRS_WORD1 to reflect the new support for FATTR4_WORD1_TIME_CREATE.
> > 
> > Yes, I realized that one shortly after I sent the mail.
> > Just going to try this patch:
> 
> Thanks!
> 
> Don't we want to vary support depending on the filesystem, though?  Is
> there a way to query that?

Poking around a bit...  looks like we need to check stat->result_mask &
STATX_BTIME.  And use that to adjust the value of bmval0 at the top of
encode_fattr, and make the below encoding conditional on it.

?

--b.

> 
> --b.
> 
> > 
> > [ondrejv@skynet19 /opt/kernel/linux-git/fs/nfsd]$ git diff
> > diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
> > index 5a93a5db4fb0..be47e1dd6da5 100644
> > --- a/fs/nfsd/nfs4xdr.c
> > +++ b/fs/nfsd/nfs4xdr.c
> > @@ -3265,6 +3265,14 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
> >                 p = xdr_encode_hyper(p, (s64)stat.mtime.tv_sec);
> >                 *p++ = cpu_to_be32(stat.mtime.tv_nsec);
> >         }
> > +       /* support for btime here */
> > +        if (bmval1 & FATTR4_WORD1_TIME_CREATE) {
> > +                p = xdr_reserve_space(xdr, 12);
> > +                if (!p)
> > +                        goto out_resource;
> > +                p = xdr_encode_hyper(p, (s64)stat.btime.tv_sec);
> > +                *p++ = cpu_to_be32(stat.btime.tv_nsec);
> > +        }
> >         if (bmval1 & FATTR4_WORD1_MOUNTED_ON_FILEID) {
> >                 struct kstat parent_stat;
> >                 u64 ino = stat.ino;
> > diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h
> > index 498e5a489826..5ef056ce7591 100644
> > --- a/fs/nfsd/nfsd.h
> > +++ b/fs/nfsd/nfsd.h
> > @@ -364,7 +364,7 @@ void                nfsd_lockd_shutdown(void);
> >   | FATTR4_WORD1_OWNER          | FATTR4_WORD1_OWNER_GROUP  | FATTR4_WORD1_RAWDEV           \
> >   | FATTR4_WORD1_SPACE_AVAIL     | FATTR4_WORD1_SPACE_FREE   | FATTR4_WORD1_SPACE_TOTAL      \
> >   | FATTR4_WORD1_SPACE_USED      | FATTR4_WORD1_TIME_ACCESS  | FATTR4_WORD1_TIME_ACCESS_SET  \
> > - | FATTR4_WORD1_TIME_DELTA   | FATTR4_WORD1_TIME_METADATA    \
> > + | FATTR4_WORD1_TIME_DELTA   | FATTR4_WORD1_TIME_METADATA   | FATTR4_WORD1_TIME_CREATE      \
> >   | FATTR4_WORD1_TIME_MODIFY     | FATTR4_WORD1_TIME_MODIFY_SET | FATTR4_WORD1_MOUNTED_ON_FILEID)
> > 
> >  #define NFSD4_SUPPORTED_ATTRS_WORD2 0
> > 
> > 
> > ... will see
> > 
> > Legal Disclaimer: This e-mail communication (and any attachment/s) is confidential and contains proprietary information, some or all of which may be legally privileged. It is intended solely for the use of the individual or entity to which it is addressed. Access to this email by anyone else is unauthorized. If you are not the intended recipient, any disclosure, copying, distribution or any action taken or omitted to be taken in reliance on it, is prohibited and may be unlawful.

^ permalink raw reply	[flat|nested] 30+ messages in thread

* RE: [PATCH 0/8] Support btime and other NFSv4 specific attributes
  2022-01-06 14:36                     ` bfields
@ 2022-01-06 14:52                       ` Ondrej Valousek
  2022-01-06 14:55                         ` bfields
  0 siblings, 1 reply; 30+ messages in thread
From: Ondrej Valousek @ 2022-01-06 14:52 UTC (permalink / raw)
  To: bfields, Ondrej Valousek
  Cc: Trond Myklebust, linux-nfs, anna.schumaker, trondmy

You rather mean bmval1 not bmval0, right?

-----Original Message-----
From: bfields@fieldses.org <bfields@fieldses.org>
Sent: čtvrtek 6. ledna 2022 15:36
To: Ondrej Valousek <ondrej.valousek.xm@renesas.com>
Cc: Trond Myklebust <trondmy@hammerspace.com>; linux-nfs@vger.kernel.org; anna.schumaker@netapp.com; trondmy@kernel.org
Subject: Re: [PATCH 0/8] Support btime and other NFSv4 specific attributes

On Thu, Jan 06, 2022 at 09:28:12AM -0500, bfields@fieldses.org wrote:
> On Thu, Jan 06, 2022 at 02:19:22PM +0000, Ondrej Valousek wrote:
> > > You also need to update the value of NFSD4_SUPPORTED_ATTRS_WORD1 to reflect the new support for FATTR4_WORD1_TIME_CREATE.
> >
> > Yes, I realized that one shortly after I sent the mail.
> > Just going to try this patch:
>
> Thanks!
>
> Don't we want to vary support depending on the filesystem, though?  Is
> there a way to query that?

Poking around a bit...  looks like we need to check stat->result_mask & STATX_BTIME.  And use that to adjust the value of bmval0 at the top of encode_fattr, and make the below encoding conditional on it.

?

--b.

>
> --b.
>
> >
> > [ondrejv@skynet19 /opt/kernel/linux-git/fs/nfsd]$ git diff diff
> > --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index
> > 5a93a5db4fb0..be47e1dd6da5 100644
> > --- a/fs/nfsd/nfs4xdr.c
> > +++ b/fs/nfsd/nfs4xdr.c
> > @@ -3265,6 +3265,14 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
> >                 p = xdr_encode_hyper(p, (s64)stat.mtime.tv_sec);
> >                 *p++ = cpu_to_be32(stat.mtime.tv_nsec);
> >         }
> > +       /* support for btime here */
> > +        if (bmval1 & FATTR4_WORD1_TIME_CREATE) {
> > +                p = xdr_reserve_space(xdr, 12);
> > +                if (!p)
> > +                        goto out_resource;
> > +                p = xdr_encode_hyper(p, (s64)stat.btime.tv_sec);
> > +                *p++ = cpu_to_be32(stat.btime.tv_nsec);
> > +        }
> >         if (bmval1 & FATTR4_WORD1_MOUNTED_ON_FILEID) {
> >                 struct kstat parent_stat;
> >                 u64 ino = stat.ino;
> > diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h index
> > 498e5a489826..5ef056ce7591 100644
> > --- a/fs/nfsd/nfsd.h
> > +++ b/fs/nfsd/nfsd.h
> > @@ -364,7 +364,7 @@ void                nfsd_lockd_shutdown(void);
> >   | FATTR4_WORD1_OWNER          | FATTR4_WORD1_OWNER_GROUP  | FATTR4_WORD1_RAWDEV           \
> >   | FATTR4_WORD1_SPACE_AVAIL     | FATTR4_WORD1_SPACE_FREE   | FATTR4_WORD1_SPACE_TOTAL      \
> >   | FATTR4_WORD1_SPACE_USED      | FATTR4_WORD1_TIME_ACCESS  | FATTR4_WORD1_TIME_ACCESS_SET  \
> > - | FATTR4_WORD1_TIME_DELTA   | FATTR4_WORD1_TIME_METADATA    \
> > + | FATTR4_WORD1_TIME_DELTA   | FATTR4_WORD1_TIME_METADATA   | FATTR4_WORD1_TIME_CREATE      \
> >   | FATTR4_WORD1_TIME_MODIFY     | FATTR4_WORD1_TIME_MODIFY_SET | FATTR4_WORD1_MOUNTED_ON_FILEID)
> >
> >  #define NFSD4_SUPPORTED_ATTRS_WORD2 0
> >
> >
> > ... will see
> >
> > Legal Disclaimer: This e-mail communication (and any attachment/s) is confidential and contains proprietary information, some or all of which may be legally privileged. It is intended solely for the use of the individual or entity to which it is addressed. Access to this email by anyone else is unauthorized. If you are not the intended recipient, any disclosure, copying, distribution or any action taken or omitted to be taken in reliance on it, is prohibited and may be unlawful.
Legal Disclaimer: This e-mail communication (and any attachment/s) is confidential and contains proprietary information, some or all of which may be legally privileged. It is intended solely for the use of the individual or entity to which it is addressed. Access to this email by anyone else is unauthorized. If you are not the intended recipient, any disclosure, copying, distribution or any action taken or omitted to be taken in reliance on it, is prohibited and may be unlawful.

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH 0/8] Support btime and other NFSv4 specific attributes
  2022-01-06 14:52                       ` Ondrej Valousek
@ 2022-01-06 14:55                         ` bfields
  2022-01-06 16:13                           ` Ondrej Valousek
  0 siblings, 1 reply; 30+ messages in thread
From: bfields @ 2022-01-06 14:55 UTC (permalink / raw)
  To: Ondrej Valousek; +Cc: Trond Myklebust, linux-nfs, anna.schumaker, trondmy

On Thu, Jan 06, 2022 at 02:52:36PM +0000, Ondrej Valousek wrote:
> You rather mean bmval1 not bmval0, right?

Oops, right.--b.

> 
> -----Original Message-----
> From: bfields@fieldses.org <bfields@fieldses.org>
> Sent: čtvrtek 6. ledna 2022 15:36
> To: Ondrej Valousek <ondrej.valousek.xm@renesas.com>
> Cc: Trond Myklebust <trondmy@hammerspace.com>; linux-nfs@vger.kernel.org; anna.schumaker@netapp.com; trondmy@kernel.org
> Subject: Re: [PATCH 0/8] Support btime and other NFSv4 specific attributes
> 
> On Thu, Jan 06, 2022 at 09:28:12AM -0500, bfields@fieldses.org wrote:
> > On Thu, Jan 06, 2022 at 02:19:22PM +0000, Ondrej Valousek wrote:
> > > > You also need to update the value of NFSD4_SUPPORTED_ATTRS_WORD1 to reflect the new support for FATTR4_WORD1_TIME_CREATE.
> > >
> > > Yes, I realized that one shortly after I sent the mail.
> > > Just going to try this patch:
> >
> > Thanks!
> >
> > Don't we want to vary support depending on the filesystem, though?  Is
> > there a way to query that?
> 
> Poking around a bit...  looks like we need to check stat->result_mask & STATX_BTIME.  And use that to adjust the value of bmval0 at the top of encode_fattr, and make the below encoding conditional on it.
> 
> ?
> 
> --b.
> 
> >
> > --b.
> >
> > >
> > > [ondrejv@skynet19 /opt/kernel/linux-git/fs/nfsd]$ git diff diff
> > > --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index
> > > 5a93a5db4fb0..be47e1dd6da5 100644
> > > --- a/fs/nfsd/nfs4xdr.c
> > > +++ b/fs/nfsd/nfs4xdr.c
> > > @@ -3265,6 +3265,14 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
> > >                 p = xdr_encode_hyper(p, (s64)stat.mtime.tv_sec);
> > >                 *p++ = cpu_to_be32(stat.mtime.tv_nsec);
> > >         }
> > > +       /* support for btime here */
> > > +        if (bmval1 & FATTR4_WORD1_TIME_CREATE) {
> > > +                p = xdr_reserve_space(xdr, 12);
> > > +                if (!p)
> > > +                        goto out_resource;
> > > +                p = xdr_encode_hyper(p, (s64)stat.btime.tv_sec);
> > > +                *p++ = cpu_to_be32(stat.btime.tv_nsec);
> > > +        }
> > >         if (bmval1 & FATTR4_WORD1_MOUNTED_ON_FILEID) {
> > >                 struct kstat parent_stat;
> > >                 u64 ino = stat.ino;
> > > diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h index
> > > 498e5a489826..5ef056ce7591 100644
> > > --- a/fs/nfsd/nfsd.h
> > > +++ b/fs/nfsd/nfsd.h
> > > @@ -364,7 +364,7 @@ void                nfsd_lockd_shutdown(void);
> > >   | FATTR4_WORD1_OWNER          | FATTR4_WORD1_OWNER_GROUP  | FATTR4_WORD1_RAWDEV           \
> > >   | FATTR4_WORD1_SPACE_AVAIL     | FATTR4_WORD1_SPACE_FREE   | FATTR4_WORD1_SPACE_TOTAL      \
> > >   | FATTR4_WORD1_SPACE_USED      | FATTR4_WORD1_TIME_ACCESS  | FATTR4_WORD1_TIME_ACCESS_SET  \
> > > - | FATTR4_WORD1_TIME_DELTA   | FATTR4_WORD1_TIME_METADATA    \
> > > + | FATTR4_WORD1_TIME_DELTA   | FATTR4_WORD1_TIME_METADATA   | FATTR4_WORD1_TIME_CREATE      \
> > >   | FATTR4_WORD1_TIME_MODIFY     | FATTR4_WORD1_TIME_MODIFY_SET | FATTR4_WORD1_MOUNTED_ON_FILEID)
> > >
> > >  #define NFSD4_SUPPORTED_ATTRS_WORD2 0
> > >
> > >
> > > ... will see
> > >
> > > Legal Disclaimer: This e-mail communication (and any attachment/s) is confidential and contains proprietary information, some or all of which may be legally privileged. It is intended solely for the use of the individual or entity to which it is addressed. Access to this email by anyone else is unauthorized. If you are not the intended recipient, any disclosure, copying, distribution or any action taken or omitted to be taken in reliance on it, is prohibited and may be unlawful.
> Legal Disclaimer: This e-mail communication (and any attachment/s) is confidential and contains proprietary information, some or all of which may be legally privileged. It is intended solely for the use of the individual or entity to which it is addressed. Access to this email by anyone else is unauthorized. If you are not the intended recipient, any disclosure, copying, distribution or any action taken or omitted to be taken in reliance on it, is prohibited and may be unlawful.

^ permalink raw reply	[flat|nested] 30+ messages in thread

* RE: [PATCH 0/8] Support btime and other NFSv4 specific attributes
  2022-01-06 14:55                         ` bfields
@ 2022-01-06 16:13                           ` Ondrej Valousek
  2022-01-06 16:59                             ` Chuck Lever III
  0 siblings, 1 reply; 30+ messages in thread
From: Ondrej Valousek @ 2022-01-06 16:13 UTC (permalink / raw)
  To: bfields, Ondrej Valousek
  Cc: Trond Myklebust, linux-nfs, anna.schumaker, trondmy

So I think this is what we eventually need (thanks for the pointers you gave me!):

diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 5a93a5db4fb0..e88ae4ce5263 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -2865,6 +2865,9 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
        err = vfs_getattr(&path, &stat, STATX_BASIC_STATS, AT_STATX_SYNC_AS_STAT);
        if (err)
                goto out_nfserr;
+       if (! stat.result_mask & STATX_BTIME)
+               /* underlying FS does not offer btime so we can't share it */
+               bmval1 &= ~FATTR4_WORD1_TIME_CREATE;
        if ((bmval0 & (FATTR4_WORD0_FILES_AVAIL | FATTR4_WORD0_FILES_FREE |
                        FATTR4_WORD0_FILES_TOTAL | FATTR4_WORD0_MAXNAME)) ||
            (bmval1 & (FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE |
@@ -3265,6 +3268,14 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
                p = xdr_encode_hyper(p, (s64)stat.mtime.tv_sec);
                *p++ = cpu_to_be32(stat.mtime.tv_nsec);
        }
+       /* support for btime here */
+        if (bmval1 & FATTR4_WORD1_TIME_CREATE) {
+                p = xdr_reserve_space(xdr, 12);
+                if (!p)
+                        goto out_resource;
+                p = xdr_encode_hyper(p, (s64)stat.btime.tv_sec);
+                *p++ = cpu_to_be32(stat.btime.tv_nsec);
+        }
        if (bmval1 & FATTR4_WORD1_MOUNTED_ON_FILEID) {
                struct kstat parent_stat;
                u64 ino = stat.ino;

diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h
index 498e5a489826..5ef056ce7591 100644
--- a/fs/nfsd/nfsd.h
+++ b/fs/nfsd/nfsd.h
@@ -364,7 +364,7 @@ void                nfsd_lockd_shutdown(void);
  | FATTR4_WORD1_OWNER          | FATTR4_WORD1_OWNER_GROUP  | FATTR4_WORD1_RAWDEV           \
  | FATTR4_WORD1_SPACE_AVAIL     | FATTR4_WORD1_SPACE_FREE   | FATTR4_WORD1_SPACE_TOTAL      \
  | FATTR4_WORD1_SPACE_USED      | FATTR4_WORD1_TIME_ACCESS  | FATTR4_WORD1_TIME_ACCESS_SET  \
- | FATTR4_WORD1_TIME_DELTA   | FATTR4_WORD1_TIME_METADATA    \
+ | FATTR4_WORD1_TIME_DELTA   | FATTR4_WORD1_TIME_METADATA   | FATTR4_WORD1_TIME_CREATE      \
  | FATTR4_WORD1_TIME_MODIFY     | FATTR4_WORD1_TIME_MODIFY_SET | FATTR4_WORD1_MOUNTED_ON_FILEID)

 #define NFSD4_SUPPORTED_ATTRS_WORD2 0

Legal Disclaimer: This e-mail communication (and any attachment/s) is confidential and contains proprietary information, some or all of which may be legally privileged. It is intended solely for the use of the individual or entity to which it is addressed. Access to this email by anyone else is unauthorized. If you are not the intended recipient, any disclosure, copying, distribution or any action taken or omitted to be taken in reliance on it, is prohibited and may be unlawful.

^ permalink raw reply related	[flat|nested] 30+ messages in thread

* Re: [PATCH 0/8] Support btime and other NFSv4 specific attributes
  2022-01-06 16:13                           ` Ondrej Valousek
@ 2022-01-06 16:59                             ` Chuck Lever III
  0 siblings, 0 replies; 30+ messages in thread
From: Chuck Lever III @ 2022-01-06 16:59 UTC (permalink / raw)
  To: Ondrej Valousek
  Cc: Bruce Fields, Trond Myklebust, Linux NFS Mailing List,
	Anna Schumaker, trondmy



> On Jan 6, 2022, at 11:13 AM, Ondrej Valousek <ondrej.valousek.xm@renesas.com> wrote:
> 
> So I think this is what we eventually need (thanks for the pointers you gave me!):
> 
> diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
> index 5a93a5db4fb0..e88ae4ce5263 100644
> --- a/fs/nfsd/nfs4xdr.c
> +++ b/fs/nfsd/nfs4xdr.c
> @@ -2865,6 +2865,9 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
>        err = vfs_getattr(&path, &stat, STATX_BASIC_STATS, AT_STATX_SYNC_AS_STAT);
>        if (err)
>                goto out_nfserr;
> +       if (! stat.result_mask & STATX_BTIME)
> +               /* underlying FS does not offer btime so we can't share it */
> +               bmval1 &= ~FATTR4_WORD1_TIME_CREATE;
>        if ((bmval0 & (FATTR4_WORD0_FILES_AVAIL | FATTR4_WORD0_FILES_FREE |
>                        FATTR4_WORD0_FILES_TOTAL | FATTR4_WORD0_MAXNAME)) ||
>            (bmval1 & (FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE |
> @@ -3265,6 +3268,14 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
>                p = xdr_encode_hyper(p, (s64)stat.mtime.tv_sec);
>                *p++ = cpu_to_be32(stat.mtime.tv_nsec);
>        }
> +       /* support for btime here */
> +        if (bmval1 & FATTR4_WORD1_TIME_CREATE) {
> +                p = xdr_reserve_space(xdr, 12);
> +                if (!p)
> +                        goto out_resource;
> +                p = xdr_encode_hyper(p, (s64)stat.btime.tv_sec);
> +                *p++ = cpu_to_be32(stat.btime.tv_nsec);
> +        }
>        if (bmval1 & FATTR4_WORD1_MOUNTED_ON_FILEID) {
>                struct kstat parent_stat;
>                u64 ino = stat.ino;
> 
> diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h
> index 498e5a489826..5ef056ce7591 100644
> --- a/fs/nfsd/nfsd.h
> +++ b/fs/nfsd/nfsd.h
> @@ -364,7 +364,7 @@ void                nfsd_lockd_shutdown(void);
>  | FATTR4_WORD1_OWNER          | FATTR4_WORD1_OWNER_GROUP  | FATTR4_WORD1_RAWDEV           \
>  | FATTR4_WORD1_SPACE_AVAIL     | FATTR4_WORD1_SPACE_FREE   | FATTR4_WORD1_SPACE_TOTAL      \
>  | FATTR4_WORD1_SPACE_USED      | FATTR4_WORD1_TIME_ACCESS  | FATTR4_WORD1_TIME_ACCESS_SET  \
> - | FATTR4_WORD1_TIME_DELTA   | FATTR4_WORD1_TIME_METADATA    \
> + | FATTR4_WORD1_TIME_DELTA   | FATTR4_WORD1_TIME_METADATA   | FATTR4_WORD1_TIME_CREATE      \
>  | FATTR4_WORD1_TIME_MODIFY     | FATTR4_WORD1_TIME_MODIFY_SET | FATTR4_WORD1_MOUNTED_ON_FILEID)
> 
> #define NFSD4_SUPPORTED_ATTRS_WORD2 0
> 
> Legal Disclaimer: This e-mail communication (and any attachment/s) is confidential and contains proprietary information, some or all of which may be legally privileged. It is intended solely for the use of the individual or entity to which it is addressed. Access to this email by anyone else is unauthorized. If you are not the intended recipient, any disclosure, copying, distribution or any action taken or omitted to be taken in reliance on it, is prohibited and may be unlawful.

Thanks, Ondrej.

This looks like an interesting new feature, however it's a little
late for the v5.17 merge window, IMO. (On the plus side, that gives
us a month or two for further review and deeper testing).

Also, the change needs to be submitted as a real patch. See the

   Documentation/process/submitting-patches.rst 

file in the Linux kernel tree for information on the p's and q's.
In particular:

 -- you need a short and full patch description. The "Describe
    your changes" section explains the details.

 -- you need to ensure your employer permits you to contribute
    to the Linux kernel under GPLv2. The "Sign your work - the
    Developer's Certificate of Origin" section explains this.

I'm a little concerned about your Legal Disclaimer which suggests
that anything you have sent in e-mail is already owned by Renesas
and is therefore constrained for submission to the kernel. Before
submitting again, can you ask your legal team for clarification?

--
Chuck Lever




^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH 0/8] Support btime and other NFSv4 specific attributes
  2022-01-03 20:51 ` [PATCH 0/8] Support btime and other NFSv4 specific attributes J. Bruce Fields
  2022-01-03 20:51   ` Trond Myklebust
@ 2022-01-06 18:47   ` Olga Kornievskaia
  1 sibling, 0 replies; 30+ messages in thread
From: Olga Kornievskaia @ 2022-01-06 18:47 UTC (permalink / raw)
  To: J. Bruce Fields; +Cc: trondmy, Anna Schumaker, linux-nfs

On Mon, Jan 3, 2022 at 4:34 PM J. Bruce Fields <bfields@fieldses.org> wrote:
>
> On Fri, Dec 17, 2021 at 03:48:46PM -0500, trondmy@kernel.org wrote:
> > From: Trond Myklebust <trond.myklebust@hammerspace.com>
> >
> > NFSv4 has support for a number of extra attributes that are of interest
> > to Samba when it is used to re-export a filesystem to Windows clients.
> > Aside from the btime, which is of interest in statx(), Windows clients
> > have an interest in determining the status of the 'hidden', and 'system'
> > flags.
> > Backup programs want to read the 'archive' flags and the 'time backup'
> > attribute.
> > Finally, the 'offline' flag can tell whether or not a file needs to be
> > staged by an HSM system before it can be read or written to.
> >
> > The patch series also adds an ioctl() to allow userspace retrieval and
> > setting of these attributes where appropriate. It also adds an ioctl()
> > to allow retrieval of the raw NFSv4 ACCESS information, to allow more
> > fine grained determination of the user's access rights to a file or
> > directory. All of this information is of use for Samba.
>
> Same question, what filesystem and server are you testing against?

I have tested the patch set against the Netapp filer. generic/528
tests the btime attribute. I'm not sure if xfstests has any other
tests for archive, offline, hidden attributes.

>
> --b.
>
> >
> > Anne Marie Merritt (3):
> >   nfs: Add timecreate to nfs inode
> >   nfs: Add 'archive', 'hidden' and 'system' fields to nfs inode
> >   nfs: Add 'time backup' to nfs inode
> >
> > Richard Sharpe (1):
> >   NFS: Support statx_get and statx_set ioctls
> >
> > Trond Myklebust (4):
> >   NFS: Expand the type of nfs_fattr->valid
> >   NFS: Return the file btime in the statx results when appropriate
> >   NFSv4: Support the offline bit
> >   NFSv4: Add an ioctl to allow retrieval of the NFS raw ACCESS mask
> >
> >  fs/nfs/dir.c              |  71 ++---
> >  fs/nfs/getroot.c          |   3 +-
> >  fs/nfs/inode.c            | 147 +++++++++-
> >  fs/nfs/internal.h         |  10 +
> >  fs/nfs/nfs3proc.c         |   1 +
> >  fs/nfs/nfs4_fs.h          |  31 +++
> >  fs/nfs/nfs4file.c         | 550 ++++++++++++++++++++++++++++++++++++++
> >  fs/nfs/nfs4proc.c         | 175 +++++++++++-
> >  fs/nfs/nfs4trace.h        |   8 +-
> >  fs/nfs/nfs4xdr.c          | 240 +++++++++++++++--
> >  fs/nfs/nfstrace.c         |   5 +
> >  fs/nfs/nfstrace.h         |   9 +-
> >  fs/nfs/proc.c             |   1 +
> >  include/linux/nfs4.h      |   1 +
> >  include/linux/nfs_fs.h    |  15 ++
> >  include/linux/nfs_fs_sb.h |   2 +-
> >  include/linux/nfs_xdr.h   |  80 ++++--
> >  include/uapi/linux/nfs.h  | 101 +++++++
> >  18 files changed, 1356 insertions(+), 94 deletions(-)
> >
> > --
> > 2.33.1

^ permalink raw reply	[flat|nested] 30+ messages in thread

end of thread, other threads:[~2022-01-06 18:47 UTC | newest]

Thread overview: 30+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-12-17 20:48 [PATCH 0/8] Support btime and other NFSv4 specific attributes trondmy
2021-12-17 20:48 ` [PATCH 1/8] NFS: Expand the type of nfs_fattr->valid trondmy
2021-12-17 20:48   ` [PATCH 2/8] nfs: Add timecreate to nfs inode trondmy
2021-12-17 20:48     ` [PATCH 3/8] NFS: Return the file btime in the statx results when appropriate trondmy
2021-12-17 20:48       ` [PATCH 4/8] nfs: Add 'archive', 'hidden' and 'system' fields to nfs inode trondmy
2021-12-17 20:48         ` [PATCH 5/8] nfs: Add 'time backup' " trondmy
2021-12-17 20:48           ` [PATCH 6/8] NFSv4: Support the offline bit trondmy
2021-12-17 20:48             ` [PATCH 7/8] NFS: Support statx_get and statx_set ioctls trondmy
2021-12-17 20:48               ` [PATCH 8/8] NFSv4: Add an ioctl to allow retrieval of the NFS raw ACCESS mask trondmy
2021-12-21 22:20                 ` Anna Schumaker
2022-01-03 20:52               ` [PATCH 7/8] NFS: Support statx_get and statx_set ioctls J. Bruce Fields
2022-01-03 20:56                 ` Trond Myklebust
2022-01-03 21:47                   ` bfields
2022-01-03 20:51 ` [PATCH 0/8] Support btime and other NFSv4 specific attributes J. Bruce Fields
2022-01-03 20:51   ` Trond Myklebust
2022-01-05 15:05     ` Ondrej Valousek
2022-01-05 15:10       ` Trond Myklebust
2022-01-05 15:10       ` bfields
2022-01-05 15:40         ` Ondrej Valousek
2022-01-05 15:54           ` bfields
2022-01-06  9:31             ` Ondrej Valousek
2022-01-06 14:15               ` Trond Myklebust
2022-01-06 14:19                 ` Ondrej Valousek
2022-01-06 14:28                   ` bfields
2022-01-06 14:36                     ` bfields
2022-01-06 14:52                       ` Ondrej Valousek
2022-01-06 14:55                         ` bfields
2022-01-06 16:13                           ` Ondrej Valousek
2022-01-06 16:59                             ` Chuck Lever III
2022-01-06 18:47   ` Olga Kornievskaia

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.