diff mbox series

[RFC,3/4] fs: Implement support for fsx_xflags_mask, fsx_xflags2 and fsx_xflags2_mask into vfs

Message ID 20250216164029.20673-4-pali@kernel.org (mailing list archive)
State New
Headers show
Series fs: Add support for Windows file attributes | expand

Commit Message

Pali Rohár Feb. 16, 2025, 4:40 p.m. UTC
This change adds support for new struct fileattr fields fsx_xflags_mask,
fsx_xflags2 and fsx_xflags2_mask into FS_IOC_FSGETXATTR and
FS_IOC_FSSETXATTR ioctls.

All filesystem will start reporting values in new *_mask fields.
This change does not contain support for any new flag yet. This will be in
some followup changes.

Signed-off-by: Pali Rohár <pali@kernel.org>
---
 fs/btrfs/ioctl.c         |  9 +++++-
 fs/efivarfs/inode.c      |  3 +-
 fs/ext2/ioctl.c          |  2 +-
 fs/ext4/ioctl.c          |  2 +-
 fs/f2fs/file.c           |  2 +-
 fs/fuse/ioctl.c          | 13 ++++++--
 fs/gfs2/file.c           | 14 ++++++++-
 fs/hfsplus/inode.c       |  3 +-
 fs/ioctl.c               | 65 +++++++++++++++++++++++++++++++++++-----
 fs/jfs/ioctl.c           | 14 ++++++++-
 fs/nilfs2/ioctl.c        |  2 +-
 fs/ntfs3/file.c          |  3 +-
 fs/ocfs2/ioctl.c         |  2 +-
 fs/orangefs/inode.c      |  2 +-
 fs/ubifs/ioctl.c         |  3 +-
 fs/xfs/xfs_ioctl.c       |  5 +++-
 include/linux/fileattr.h | 11 +++++--
 mm/shmem.c               |  2 +-
 18 files changed, 130 insertions(+), 27 deletions(-)
diff mbox series

Patch

diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 6c18bad53cd3..600f502ffb46 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -164,6 +164,13 @@  static unsigned int btrfs_inode_flags_to_fsflags(struct btrfs_inode *binode)
 	return iflags;
 }
 
+static inline unsigned int btrfs_supported_fsflags(void)
+{
+	return FS_SYNC_FL | FS_IMMUTABLE_FL | FS_APPEND_FL | FS_NODUMP_FL |
+	       FS_NOATIME_FL | FS_DIRSYNC_FL | FS_NOCOW_FL | FS_VERITY_FL |
+	       FS_NOCOMP_FL | FS_COMPR_FL;
+}
+
 /*
  * Update inode->i_flags based on the btrfs internal flags.
  */
@@ -250,7 +257,7 @@  int btrfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
 {
 	struct btrfs_inode *binode = BTRFS_I(d_inode(dentry));
 
-	fileattr_fill_flags(fa, btrfs_inode_flags_to_fsflags(binode));
+	fileattr_fill_flags(fa, btrfs_inode_flags_to_fsflags(binode), btrfs_supported_fsflags());
 	return 0;
 }
 
diff --git a/fs/efivarfs/inode.c b/fs/efivarfs/inode.c
index 98a7299a9ee9..a8a3962b7751 100644
--- a/fs/efivarfs/inode.c
+++ b/fs/efivarfs/inode.c
@@ -142,12 +142,13 @@  efivarfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
 {
 	unsigned int i_flags;
 	unsigned int flags = 0;
+	unsigned int mask = FS_IMMUTABLE_FL;
 
 	i_flags = d_inode(dentry)->i_flags;
 	if (i_flags & S_IMMUTABLE)
 		flags |= FS_IMMUTABLE_FL;
 
-	fileattr_fill_flags(fa, flags);
+	fileattr_fill_flags(fa, flags, mask);
 
 	return 0;
 }
diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c
index 44e04484e570..7a70f16c2824 100644
--- a/fs/ext2/ioctl.c
+++ b/fs/ext2/ioctl.c
@@ -22,7 +22,7 @@  int ext2_fileattr_get(struct dentry *dentry, struct fileattr *fa)
 {
 	struct ext2_inode_info *ei = EXT2_I(d_inode(dentry));
 
-	fileattr_fill_flags(fa, ei->i_flags & EXT2_FL_USER_VISIBLE);
+	fileattr_fill_flags(fa, ei->i_flags & EXT2_FL_USER_VISIBLE, EXT2_FL_USER_VISIBLE);
 
 	return 0;
 }
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index 7b9ce71c1c81..a199d0b74449 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -989,7 +989,7 @@  int ext4_fileattr_get(struct dentry *dentry, struct fileattr *fa)
 	if (S_ISREG(inode->i_mode))
 		flags &= ~FS_PROJINHERIT_FL;
 
-	fileattr_fill_flags(fa, flags);
+	fileattr_fill_flags(fa, flags, EXT4_FL_USER_VISIBLE);
 	if (ext4_has_feature_project(inode->i_sb))
 		fa->fsx_projid = from_kprojid(&init_user_ns, ei->i_projid);
 
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index f92a9fba9991..03e1b31d1cbb 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -3297,7 +3297,7 @@  int f2fs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
 	if (is_inode_flag_set(inode, FI_PIN_FILE))
 		fsflags |= FS_NOCOW_FL;
 
-	fileattr_fill_flags(fa, fsflags & F2FS_GETTABLE_FS_FL);
+	fileattr_fill_flags(fa, fsflags & F2FS_GETTABLE_FS_FL, F2FS_GETTABLE_FS_FL);
 
 	if (f2fs_sb_has_project_quota(F2FS_I_SB(inode)))
 		fa->fsx_projid = from_kprojid(&init_user_ns, fi->i_projid);
diff --git a/fs/fuse/ioctl.c b/fs/fuse/ioctl.c
index 2d9abf48828f..be0901d7c61e 100644
--- a/fs/fuse/ioctl.c
+++ b/fs/fuse/ioctl.c
@@ -520,14 +520,20 @@  int fuse_fileattr_get(struct dentry *dentry, struct fileattr *fa)
 		if (err)
 			goto cleanup;
 
-		fileattr_fill_flags(fa, flags);
+		fileattr_fill_flags(fa, flags, ~0);
 	} else {
 		err = fuse_priv_ioctl(inode, ff, FS_IOC_FSGETXATTR,
 				      &xfa, sizeof(xfa));
 		if (err)
 			goto cleanup;
 
-		fileattr_fill_xflags(fa, xfa.fsx_xflags);
+		if (!(xfa.fsx_xflags & FS_XFLAG_HASEXTFIELDS)) {
+			xfa.fsx_xflags_mask = 0;
+			xfa.fsx_xflags2 = 0;
+			xfa.fsx_xflags2_mask = 0;
+		}
+
+		fileattr_fill_xflags(fa, xfa.fsx_xflags, xfa.fsx_xflags_mask, xfa.fsx_xflags2, xfa.fsx_xflags2_mask);
 		fa->fsx_extsize = xfa.fsx_extsize;
 		fa->fsx_nextents = xfa.fsx_nextents;
 		fa->fsx_projid = xfa.fsx_projid;
@@ -564,6 +570,9 @@  int fuse_fileattr_set(struct mnt_idmap *idmap,
 		xfa.fsx_nextents = fa->fsx_nextents;
 		xfa.fsx_projid = fa->fsx_projid;
 		xfa.fsx_cowextsize = fa->fsx_cowextsize;
+		xfa.fsx_xflags2 = fa->fsx_xflags2;
+		xfa.fsx_xflags2_mask = fa->fsx_xflags2_mask;
+		xfa.fsx_xflags_mask = fa->fsx_xflags_mask;
 
 		err = fuse_priv_ioctl(inode, ff, FS_IOC_FSSETXATTR,
 				      &xfa, sizeof(xfa));
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
index c9bb3be21d2b..29d243ade026 100644
--- a/fs/gfs2/file.c
+++ b/fs/gfs2/file.c
@@ -139,6 +139,16 @@  static struct {
 	{FS_JOURNAL_DATA_FL, GFS2_DIF_JDATA | GFS2_DIF_INHERIT_JDATA},
 };
 
+static inline u32 gfs2_supported_fsflags(void)
+{
+	int i;
+	u32 fsflags = 0;
+
+	for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
+		fsflags |= fsflag_gfs2flag[i].fsflag;
+	return fsflags;
+}
+
 static inline u32 gfs2_gfsflags_to_fsflags(struct inode *inode, u32 gfsflags)
 {
 	int i;
@@ -162,6 +172,7 @@  int gfs2_fileattr_get(struct dentry *dentry, struct fileattr *fa)
 	struct gfs2_holder gh;
 	int error;
 	u32 fsflags;
+	u32 fsmask;
 
 	if (d_is_special(dentry))
 		return -ENOTTY;
@@ -172,8 +183,9 @@  int gfs2_fileattr_get(struct dentry *dentry, struct fileattr *fa)
 		goto out_uninit;
 
 	fsflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
+	fsmask = gfs2_supported_fsflags();
 
-	fileattr_fill_flags(fa, fsflags);
+	fileattr_fill_flags(fa, fsflags, fsmask);
 
 	gfs2_glock_dq(&gh);
 out_uninit:
diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c
index f331e9574217..bb430a920f2b 100644
--- a/fs/hfsplus/inode.c
+++ b/fs/hfsplus/inode.c
@@ -659,6 +659,7 @@  int hfsplus_fileattr_get(struct dentry *dentry, struct fileattr *fa)
 	struct inode *inode = d_inode(dentry);
 	struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
 	unsigned int flags = 0;
+	unsigned int mask = FS_IMMUTABLE_FL | FS_APPEND_FL | FS_NODUMP_FL;
 
 	if (inode->i_flags & S_IMMUTABLE)
 		flags |= FS_IMMUTABLE_FL;
@@ -667,7 +668,7 @@  int hfsplus_fileattr_get(struct dentry *dentry, struct fileattr *fa)
 	if (hip->userflags & HFSPLUS_FLG_NODUMP)
 		flags |= FS_NODUMP_FL;
 
-	fileattr_fill_flags(fa, flags);
+	fileattr_fill_flags(fa, flags, mask);
 
 	return 0;
 }
diff --git a/fs/ioctl.c b/fs/ioctl.c
index 9f3609b50779..ef4d7d3d417b 100644
--- a/fs/ioctl.c
+++ b/fs/ioctl.c
@@ -458,14 +458,19 @@  static int ioctl_file_dedupe_range(struct file *file,
  * @fa:		fileattr pointer
  * @xflags:	FS_XFLAG_* flags
  *
- * Set ->fsx_xflags, ->fsx_valid and ->flags (translated xflags).  All
- * other fields are zeroed.
+ * Set ->fsx_xflags, ->fsx_xflags2, ->fsx->xflags_mask, ->fsx_xflags2_mask,
+ * ->fsx_valid and ->flags (translated xflags).  All other fields are zeroed.
  */
-void fileattr_fill_xflags(struct fileattr *fa, u32 xflags)
+void fileattr_fill_xflags(struct fileattr *fa, u32 xflags, u32 xflags_mask, u16 xflags2, u16 xflags2_mask)
 {
 	memset(fa, 0, sizeof(*fa));
 	fa->fsx_valid = true;
 	fa->fsx_xflags = xflags;
+	fa->fsx_xflags2 = xflags2;
+	fa->fsx_xflags_mask = xflags_mask;
+	fa->fsx_xflags2_mask = xflags2_mask;
+	if (fa->fsx_xflags2 != 0 || fa->fsx_xflags_mask != 0 || fa->fsx_xflags2_mask != 0)
+		fa->fsx_xflags |= FS_XFLAG_HASEXTFIELDS;
 	if (fa->fsx_xflags & FS_XFLAG_IMMUTABLE)
 		fa->flags |= FS_IMMUTABLE_FL;
 	if (fa->fsx_xflags & FS_XFLAG_APPEND)
@@ -491,15 +496,20 @@  EXPORT_SYMBOL(fileattr_fill_xflags);
  * fileattr_fill_flags - initialize fileattr with flags
  * @fa:		fileattr pointer
  * @flags:	FS_*_FL flags
+ * @mask:	FS_*_FL flags mask
  *
- * Set ->flags, ->flags_valid and ->fsx_xflags (translated flags).
+ * Set ->flags, ->flags_valid and ->fsx_xflags (translated flags),
+ * fa->fsx_xflags_mask (translated flags mask).
  * All other fields are zeroed.
  */
-void fileattr_fill_flags(struct fileattr *fa, u32 flags)
+void fileattr_fill_flags(struct fileattr *fa, u32 flags, u32 mask)
 {
 	memset(fa, 0, sizeof(*fa));
 	fa->flags_valid = true;
 	fa->flags = flags;
+
+	fa->fsx_xflags |= FS_XFLAG_HASEXTFIELDS;
+
 	if (fa->flags & FS_COMPR_FL)
 		fa->fsx_xflags |= FS_XFLAG_COMPRESSED;
 	if (fa->flags & FS_SYNC_FL)
@@ -518,6 +528,25 @@  void fileattr_fill_flags(struct fileattr *fa, u32 flags)
 		fa->fsx_xflags |= FS_XFLAG_DAX;
 	if (fa->flags & FS_PROJINHERIT_FL)
 		fa->fsx_xflags |= FS_XFLAG_PROJINHERIT;
+
+	if (mask & FS_COMPR_FL)
+		fa->fsx_xflags_mask |= FS_XFLAG_COMPRESSED;
+	if (mask & FS_SYNC_FL)
+		fa->fsx_xflags_mask |= FS_XFLAG_SYNC;
+	if (mask & FS_IMMUTABLE_FL)
+		fa->fsx_xflags_mask |= FS_XFLAG_IMMUTABLE;
+	if (mask & FS_APPEND_FL)
+		fa->fsx_xflags_mask |= FS_XFLAG_APPEND;
+	if (mask & FS_NODUMP_FL)
+		fa->fsx_xflags_mask |= FS_XFLAG_NODUMP;
+	if (mask & FS_NOATIME_FL)
+		fa->fsx_xflags_mask |= FS_XFLAG_NOATIME;
+	if (mask & FS_ENCRYPT_FL)
+		fa->fsx_xflags_mask |= FS_XFLAG_ENCRYPTED;
+	if (mask & FS_DAX_FL)
+		fa->fsx_xflags_mask |= FS_XFLAG_DAX;
+	if (mask & FS_PROJINHERIT_FL)
+		fa->fsx_xflags_mask |= FS_XFLAG_PROJINHERIT;
 }
 EXPORT_SYMBOL(fileattr_fill_flags);
 
@@ -558,6 +587,11 @@  int copy_fsxattr_to_user(const struct fileattr *fa, struct fsxattr __user *ufa)
 	xfa.fsx_nextents = fa->fsx_nextents;
 	xfa.fsx_projid = fa->fsx_projid;
 	xfa.fsx_cowextsize = fa->fsx_cowextsize;
+	if (xfa.fsx_xflags & FS_XFLAG_HASEXTFIELDS) {
+		xfa.fsx_xflags2 = fa->fsx_xflags2;
+		xfa.fsx_xflags2_mask = fa->fsx_xflags2_mask;
+		xfa.fsx_xflags_mask = fa->fsx_xflags_mask;
+	}
 
 	if (copy_to_user(ufa, &xfa, sizeof(xfa)))
 		return -EFAULT;
@@ -574,7 +608,13 @@  static int copy_fsxattr_from_user(struct fileattr *fa,
 	if (copy_from_user(&xfa, ufa, sizeof(xfa)))
 		return -EFAULT;
 
-	fileattr_fill_xflags(fa, xfa.fsx_xflags);
+	if (!(xfa.fsx_xflags & FS_XFLAG_HASEXTFIELDS)) {
+		xfa.fsx_xflags_mask = 0;
+		xfa.fsx_xflags2 = 0;
+		xfa.fsx_xflags2_mask = 0;
+	}
+
+	fileattr_fill_xflags(fa, xfa.fsx_xflags, xfa.fsx_xflags_mask, xfa.fsx_xflags2, xfa.fsx_xflags2_mask);
 	fa->fsx_extsize = xfa.fsx_extsize;
 	fa->fsx_nextents = xfa.fsx_nextents;
 	fa->fsx_projid = xfa.fsx_projid;
@@ -692,11 +732,22 @@  int vfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry,
 		/* initialize missing bits from old_ma */
 		if (fa->flags_valid) {
 			fa->fsx_xflags |= old_ma.fsx_xflags & ~FS_XFLAG_COMMON;
+			fa->fsx_xflags_mask = fa->fsx_xflags ^ old_ma.fsx_xflags;
 			fa->fsx_extsize = old_ma.fsx_extsize;
 			fa->fsx_nextents = old_ma.fsx_nextents;
 			fa->fsx_projid = old_ma.fsx_projid;
 			fa->fsx_cowextsize = old_ma.fsx_cowextsize;
+			fa->fsx_xflags2 = 0;
+			fa->fsx_xflags2_mask = 0;
 		} else {
+			if (fa->fsx_xflags & FS_XFLAG_HASEXTFIELDS) {
+				fa->fsx_xflags = (fa->fsx_xflags & fa->fsx_xflags_mask) | (old_ma.fsx_xflags & ~fa->fsx_xflags_mask);
+				fa->fsx_xflags2 = (fa->fsx_xflags2 & fa->fsx_xflags2_mask) | (old_ma.fsx_xflags2 & ~fa->fsx_xflags2_mask);
+			} else {
+				fa->fsx_xflags_mask = fa->fsx_xflags ^ old_ma.fsx_xflags;
+				fa->fsx_xflags2 = old_ma.fsx_xflags2;
+				fa->fsx_xflags2_mask = 0;
+			}
 			fa->flags |= old_ma.flags & ~FS_COMMON_FL;
 		}
 		err = fileattr_set_prepare(inode, &old_ma, fa);
@@ -732,7 +783,7 @@  static int ioctl_setflags(struct file *file, unsigned int __user *argp)
 	if (!err) {
 		err = mnt_want_write_file(file);
 		if (!err) {
-			fileattr_fill_flags(&fa, flags);
+			fileattr_fill_flags(&fa, flags, FS_COMMON_FL);
 			err = vfs_fileattr_set(idmap, dentry, &fa);
 			mnt_drop_write_file(file);
 		}
diff --git a/fs/jfs/ioctl.c b/fs/jfs/ioctl.c
index f7bd7e8f5be4..86184e32015c 100644
--- a/fs/jfs/ioctl.c
+++ b/fs/jfs/ioctl.c
@@ -39,6 +39,18 @@  static struct {
 	{0, 0},
 };
 
+static long jfs_supported_ext2_flags(void)
+{
+	int index=0;
+	long mapped=0;
+
+	while (jfs_map[index].jfs_flag) {
+		mapped |= jfs_map[index].ext2_flag;
+		index++;
+	}
+	return mapped;
+}
+
 static long jfs_map_ext2(unsigned long flags, int from)
 {
 	int index=0;
@@ -65,7 +77,7 @@  int jfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
 	if (d_is_special(dentry))
 		return -ENOTTY;
 
-	fileattr_fill_flags(fa, jfs_map_ext2(flags, 0));
+	fileattr_fill_flags(fa, jfs_map_ext2(flags, 0), jfs_supported_ext2_flags());
 
 	return 0;
 }
diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c
index a66d62a51f77..2f1d5d765dcb 100644
--- a/fs/nilfs2/ioctl.c
+++ b/fs/nilfs2/ioctl.c
@@ -122,7 +122,7 @@  int nilfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
 {
 	struct inode *inode = d_inode(dentry);
 
-	fileattr_fill_flags(fa, NILFS_I(inode)->i_flags & FS_FL_USER_VISIBLE);
+	fileattr_fill_flags(fa, NILFS_I(inode)->i_flags & FS_FL_USER_VISIBLE, FS_FL_USER_VISIBLE);
 
 	return 0;
 }
diff --git a/fs/ntfs3/file.c b/fs/ntfs3/file.c
index 3f96a11804c9..a8f4d0b08d83 100644
--- a/fs/ntfs3/file.c
+++ b/fs/ntfs3/file.c
@@ -57,6 +57,7 @@  int ntfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
 	struct inode *inode = d_inode(dentry);
 	struct ntfs_inode *ni = ntfs_i(inode);
 	u32 flags = 0;
+	u32 mask = FS_IMMUTABLE_FL | FS_APPEND_FL | FS_COMPR_FL | FS_ENCRYPT_FL;
 
 	if (inode->i_flags & S_IMMUTABLE)
 		flags |= FS_IMMUTABLE_FL;
@@ -70,7 +71,7 @@  int ntfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
 	if (is_encrypted(ni))
 		flags |= FS_ENCRYPT_FL;
 
-	fileattr_fill_flags(fa, flags);
+	fileattr_fill_flags(fa, flags, mask);
 
 	return 0;
 }
diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c
index 7ae96fb8807a..fa48d1b92aab 100644
--- a/fs/ocfs2/ioctl.c
+++ b/fs/ocfs2/ioctl.c
@@ -77,7 +77,7 @@  int ocfs2_fileattr_get(struct dentry *dentry, struct fileattr *fa)
 	flags = OCFS2_I(inode)->ip_attr;
 	ocfs2_inode_unlock(inode, 0);
 
-	fileattr_fill_flags(fa, flags & OCFS2_FL_VISIBLE);
+	fileattr_fill_flags(fa, flags & OCFS2_FL_VISIBLE, OCFS2_FL_VISIBLE);
 
 	return status;
 }
diff --git a/fs/orangefs/inode.c b/fs/orangefs/inode.c
index aae6d2b8767d..36104a08d654 100644
--- a/fs/orangefs/inode.c
+++ b/fs/orangefs/inode.c
@@ -923,7 +923,7 @@  static int orangefs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
 
 	gossip_debug(GOSSIP_FILE_DEBUG, "%s: flags=%u\n", __func__, (u32) val);
 
-	fileattr_fill_flags(fa, val);
+	fileattr_fill_flags(fa, val, FS_IMMUTABLE_FL | FS_APPEND_FL | FS_NOATIME_FL);
 	return 0;
 }
 
diff --git a/fs/ubifs/ioctl.c b/fs/ubifs/ioctl.c
index 2c99349cf537..7d445c03a877 100644
--- a/fs/ubifs/ioctl.c
+++ b/fs/ubifs/ioctl.c
@@ -134,12 +134,13 @@  int ubifs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
 {
 	struct inode *inode = d_inode(dentry);
 	int flags = ubifs2ioctl(ubifs_inode(inode)->flags);
+	int mask = ubifs2ioctl(~0);
 
 	if (d_is_special(dentry))
 		return -ENOTTY;
 
 	dbg_gen("get flags: %#x, i_flags %#x", flags, inode->i_flags);
-	fileattr_fill_flags(fa, flags);
+	fileattr_fill_flags(fa, flags, mask);
 
 	return 0;
 }
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index ed85322507dd..9c5e75568c94 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -448,8 +448,11 @@  xfs_fill_fsxattr(
 {
 	struct xfs_mount	*mp = ip->i_mount;
 	struct xfs_ifork	*ifp = xfs_ifork_ptr(ip, whichfork);
+	struct xfs_inode	ip_all_xflags = { .i_diflags = XFS_DIFLAG_ANY,
+						  .i_diflags2 = XFS_DIFLAG2_ANY,
+						  .i_forkoff = 1 };
 
-	fileattr_fill_xflags(fa, xfs_ip2xflags(ip));
+	fileattr_fill_xflags(fa, xfs_ip2xflags(ip), xfs_ip2xflags(&ip_all_xflags), 0, 0);
 
 	if (ip->i_diflags & XFS_DIFLAG_EXTSIZE) {
 		fa->fsx_extsize = XFS_FSB_TO_B(mp, ip->i_extsize);
diff --git a/include/linux/fileattr.h b/include/linux/fileattr.h
index c297e6151703..f2107598ed6b 100644
--- a/include/linux/fileattr.h
+++ b/include/linux/fileattr.h
@@ -28,6 +28,9 @@  struct fileattr {
 	u32	fsx_nextents;	/* nextents field value (get)	*/
 	u32	fsx_projid;	/* project identifier (get/set) */
 	u32	fsx_cowextsize;	/* CoW extsize field value (get/set)*/
+	u16	fsx_xflags2;	/* xflags2 field value (get/set)*/
+	u16	fsx_xflags2_mask;/*mask for xflags2 (get/set)*/
+	u32	fsx_xflags_mask;/* mask for xflags (get/set)*/
 	/* selectors: */
 	bool	flags_valid:1;
 	bool	fsx_valid:1;
@@ -35,8 +38,8 @@  struct fileattr {
 
 int copy_fsxattr_to_user(const struct fileattr *fa, struct fsxattr __user *ufa);
 
-void fileattr_fill_xflags(struct fileattr *fa, u32 xflags);
-void fileattr_fill_flags(struct fileattr *fa, u32 flags);
+void fileattr_fill_xflags(struct fileattr *fa, u32 xflags, u32 xflags_mask, u16 xflags2, u16 xflags2_mask);
+void fileattr_fill_flags(struct fileattr *fa, u32 flags, u32 mask);
 
 /**
  * fileattr_has_fsx - check for extended flags/attributes
@@ -49,7 +52,9 @@  static inline bool fileattr_has_fsx(const struct fileattr *fa)
 {
 	return fa->fsx_valid &&
 		((fa->fsx_xflags & ~FS_XFLAG_COMMON) || fa->fsx_extsize != 0 ||
-		 fa->fsx_projid != 0 ||	fa->fsx_cowextsize != 0);
+		 fa->fsx_projid != 0 || fa->fsx_cowextsize != 0 ||
+		 fa->fsx_xflags2 != 0 || fa->fsx_xflags2_mask != 0 ||
+		 (fa->fsx_xflags_mask & ~FS_XFLAG_COMMON));
 }
 
 int vfs_fileattr_get(struct dentry *dentry, struct fileattr *fa);
diff --git a/mm/shmem.c b/mm/shmem.c
index 4ea6109a8043..b991f49ee638 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -4178,7 +4178,7 @@  static int shmem_fileattr_get(struct dentry *dentry, struct fileattr *fa)
 {
 	struct shmem_inode_info *info = SHMEM_I(d_inode(dentry));
 
-	fileattr_fill_flags(fa, info->fsflags & SHMEM_FL_USER_VISIBLE);
+	fileattr_fill_flags(fa, info->fsflags & SHMEM_FL_USER_VISIBLE, SHMEM_FL_USER_VISIBLE);
 
 	return 0;
 }