diff --git a/fs/afs/inode.c b/fs/afs/inode.c
index 8fcffea2daf5..7d732a38c739 100644
--- a/fs/afs/inode.c
+++ b/fs/afs/inode.c
@@ -670,6 +670,9 @@ int afs_validate(struct afs_vnode *vnode, struct key *key)
 	       vnode->fid.vid, vnode->fid.vnode, vnode->flags,
 	       key_serial(key));
 
+	if (!vnode->volume)
+		goto valid; /* Dynroot */
+
 	if (unlikely(test_bit(AFS_VNODE_DELETED, &vnode->flags))) {
 		if (vnode->vfs_inode.i_nlink)
 			clear_nlink(&vnode->vfs_inode);
@@ -728,10 +731,15 @@ int afs_getattr(struct user_namespace *mnt_userns, const struct path *path,
 {
 	struct inode *inode = d_inode(path->dentry);
 	struct afs_vnode *vnode = AFS_FS_I(inode);
-	int seq = 0;
+	struct afs_file *af = stat->file ? stat->file->private_data : NULL;
+	int ret, seq = 0;
 
 	_enter("{ ino=%lu v=%u }", inode->i_ino, inode->i_generation);
 
+	ret = afs_validate(vnode, af ? af->key : NULL);
+	if (ret < 0)
+		return ret;
+
 	do {
 		read_seqbegin_or_lock(&vnode->cb_lock, &seq);
 		generic_fillattr(&init_user_ns, inode, stat);
diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c
index 0106eba46d5a..d3fba1aea432 100644
--- a/fs/exportfs/expfs.c
+++ b/fs/exportfs/expfs.c
@@ -303,7 +303,7 @@ static int get_name(const struct path *path, char *name, struct dentry *child)
 	 * actually call ->getattr, not just read i_ino:
 	 */
 	error = vfs_getattr_nosec(&child_path, &stat,
-				  STATX_INO, AT_STATX_SYNC_AS_STAT);
+				  STATX_INO, AT_STATX_SYNC_AS_STAT, NULL);
 	if (error)
 		return error;
 	buffer.ino = stat.ino;
diff --git a/fs/stat.c b/fs/stat.c
index 1fa38bdec1a6..c3410e809b4d 100644
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -65,6 +65,7 @@ EXPORT_SYMBOL(generic_fillattr);
  * @stat: structure to return attributes in
  * @request_mask: STATX_xxx flags indicating what the caller wants
  * @query_flags: Query mode (AT_STATX_SYNC_TYPE)
+ * @file: File with credential info or NULL
  *
  * Get attributes without calling security_inode_getattr.
  *
@@ -73,12 +74,14 @@ EXPORT_SYMBOL(generic_fillattr);
  * attributes to any user.  Any other code probably wants vfs_getattr.
  */
 int vfs_getattr_nosec(const struct path *path, struct kstat *stat,
-		      u32 request_mask, unsigned int query_flags)
+		      u32 request_mask, unsigned int query_flags,
+		      struct file *file)
 {
 	struct user_namespace *mnt_userns;
 	struct inode *inode = d_backing_inode(path->dentry);
 
 	memset(stat, 0, sizeof(*stat));
+	stat->file = file;
 	stat->result_mask |= STATX_BASIC_STATS;
 	query_flags &= AT_STATX_SYNC_TYPE;
 
@@ -139,7 +142,7 @@ int vfs_getattr(const struct path *path, struct kstat *stat,
 	retval = security_inode_getattr(path);
 	if (retval)
 		return retval;
-	return vfs_getattr_nosec(path, stat, request_mask, query_flags);
+	return vfs_getattr_nosec(path, stat, request_mask, query_flags, NULL);
 }
 EXPORT_SYMBOL(vfs_getattr);
 
@@ -161,7 +164,11 @@ int vfs_fstat(int fd, struct kstat *stat)
 	f = fdget_raw(fd);
 	if (!f.file)
 		return -EBADF;
-	error = vfs_getattr(&f.file->f_path, stat, STATX_BASIC_STATS, 0);
+
+	error = security_inode_getattr(&f.file->f_path);
+	if (!error)
+		error = vfs_getattr_nosec(&f.file->f_path, stat,
+					  STATX_BASIC_STATS, 0, f.file);
 	fdput(f);
 	return error;
 }
@@ -185,7 +192,9 @@ static int vfs_statx(int dfd, const char __user *filename, int flags,
 	      struct kstat *stat, u32 request_mask)
 {
 	struct path path;
+	struct fd f;
 	unsigned lookup_flags = 0;
+	bool put_fd = false;
 	int error;
 
 	if (flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT | AT_EMPTY_PATH |
@@ -200,17 +209,36 @@ static int vfs_statx(int dfd, const char __user *filename, int flags,
 		lookup_flags |= LOOKUP_EMPTY;
 
 retry:
-	error = user_path_at(dfd, filename, lookup_flags, &path);
-	if (error)
-		goto out;
-
-	error = vfs_getattr(&path, stat, request_mask, flags);
-	stat->mnt_id = real_mount(path.mnt)->mnt_id;
-	stat->result_mask |= STATX_MNT_ID;
-	if (path.mnt->mnt_root == path.dentry)
-		stat->attributes |= STATX_ATTR_MOUNT_ROOT;
-	stat->attributes_mask |= STATX_ATTR_MOUNT_ROOT;
-	path_put(&path);
+	if ((lookup_flags & LOOKUP_EMPTY) &&
+	    dfd >= 0 &&
+	    filename &&
+	    strnlen_user(filename, 2) == 0) {
+		/* Should we use ESTALE retry for direct-fd? */
+		f = fdget_raw(dfd);
+		if (!f.file)
+			return -EBADF;
+		path = f.file->f_path;
+		put_fd = true;
+	} else {
+		f.file = NULL;
+		error = user_path_at(dfd, filename, lookup_flags, &path);
+		if (error)
+			goto out;
+	}
+
+	error = security_inode_getattr(&path);
+	if (!error) {
+		error = vfs_getattr_nosec(&path, stat, request_mask, flags, f.file);
+		stat->mnt_id = real_mount(path.mnt)->mnt_id;
+		stat->result_mask |= STATX_MNT_ID;
+		if (path.mnt->mnt_root == path.dentry)
+			stat->attributes |= STATX_ATTR_MOUNT_ROOT;
+		stat->attributes_mask |= STATX_ATTR_MOUNT_ROOT;
+	}
+	if (put_fd)
+		fdput(f);
+	else
+		path_put(&path);
 	if (retry_estale(error, lookup_flags)) {
 		lookup_flags |= LOOKUP_REVAL;
 		goto retry;
diff --git a/include/linux/fs.h b/include/linux/fs.h
index c58c2611a195..3f31f739f9a6 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -3312,7 +3312,8 @@ extern int page_symlink(struct inode *inode, const char *symname, int len);
 extern const struct inode_operations page_symlink_inode_operations;
 extern void kfree_link(void *);
 void generic_fillattr(struct user_namespace *, struct inode *, struct kstat *);
-extern int vfs_getattr_nosec(const struct path *, struct kstat *, u32, unsigned int);
+extern int vfs_getattr_nosec(const struct path *, struct kstat *, u32, unsigned int,
+			     struct file *);
 extern int vfs_getattr(const struct path *, struct kstat *, u32, unsigned int);
 void __inode_add_bytes(struct inode *inode, loff_t bytes);
 void inode_add_bytes(struct inode *inode, loff_t bytes);
diff --git a/include/linux/stat.h b/include/linux/stat.h
index fff27e603814..b9986688cc59 100644
--- a/include/linux/stat.h
+++ b/include/linux/stat.h
@@ -20,6 +20,7 @@
 #include <linux/uidgid.h>
 
 struct kstat {
+	struct file	*file;		/* File if called from fstat() equivalent or NULL */
 	u32		result_mask;	/* What fields the user got */
 	umode_t		mode;
 	unsigned int	nlink;
