[PATCH 06/11] fs/ntfs3: add 'ugm' mount option to control storing uid/gid/mode in xattrs

From: Konstantin Komarov

Date: Fri Oct 09 2026 - 13:18:48 EST


On file creation ntfs3 stores the owner, group, mode and device
number in the WSL extended attributes $LXUID, $LXGID, $LXMOD and
$LXDEV, and may also write a POSIX ACL. These attributes are
meaningless to Windows, and the packed size of the EAs is recorded in
the directory entry as well, so every created file carries them.

Add a 'ugm' mount option controlling this. It is enabled by default,
preserving the current behaviour; mount with 'ugm=0' to create files
without the WSL attributes.

Signed-off-by: Konstantin Komarov <almaz.alexandrovich@xxxxxxxxxxxxxxxxxxxx>
---
fs/ntfs3/inode.c | 2 +-
fs/ntfs3/ntfs_fs.h | 1 +
fs/ntfs3/super.c | 13 ++++++++++++-
fs/ntfs3/xattr.c | 7 +++++++
4 files changed, 21 insertions(+), 2 deletions(-)

diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c
index a3b2daaecb4d..7593a93466d9 100644
--- a/fs/ntfs3/inode.c
+++ b/fs/ntfs3/inode.c
@@ -1836,7 +1836,7 @@ int ntfs_create_inode(struct mnt_idmap *idmap, struct inode *dir,
inode->i_flags |= S_NOSEC;
}

- if (!S_ISLNK(mode)) {
+ if (!S_ISLNK(mode) && sbi->options->ugm) {
/*
* ntfs_init_acl and ntfs_save_wsl_perm update extended attribute.
* The packed size of extended attribute is stored in direntry too.
diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h
index 9321b2608e84..90ccfde951dc 100644
--- a/fs/ntfs3/ntfs_fs.h
+++ b/fs/ntfs3/ntfs_fs.h
@@ -111,6 +111,7 @@ struct ntfs_mount_options {
unsigned nocase : 1; /* case insensitive. */
unsigned delalloc : 1; /* delay allocation. */
unsigned ads : 1; /* ads support. */
+ unsigned ugm : 1; /* store user/group/mode in xattr. */
};

/* Special value to unpack and deallocate. */
diff --git a/fs/ntfs3/super.c b/fs/ntfs3/super.c
index 7fd83027007b..cf67d34568d8 100644
--- a/fs/ntfs3/super.c
+++ b/fs/ntfs3/super.c
@@ -275,6 +275,8 @@ enum Opt {
Opt_delalloc_bool,
Opt_ads,
Opt_ads_bool,
+ Opt_ugm,
+ Opt_ugm_bool,
Opt_err,
};

@@ -303,6 +305,8 @@ static const struct fs_parameter_spec ntfs_fs_parameters[] = {
fsparam_bool("delalloc", Opt_delalloc_bool),
fsparam_flag("ads", Opt_ads),
fsparam_bool("ads", Opt_ads_bool),
+ fsparam_flag("ugm", Opt_ugm),
+ fsparam_bool("ugm", Opt_ugm_bool),
{}
};
// clang-format on
@@ -345,7 +349,7 @@ static int ntfs_fs_parse_param(struct fs_context *fc,
pr_err("ntfs3: unknown option \"%s\". Use uid,gid,umask,"
"dmask,fmask,sys_immutable,discard,force,sparse,"
"nohidden,hide_dot_files,windows_names,showmeta,acl,"
- "iocharset,prealloc,nocase,delalloc,ads",
+ "iocharset,prealloc,nocase,delalloc,ads,ugm",
param->key);
return -ENOPARAM;
}
@@ -443,6 +447,12 @@ static int ntfs_fs_parse_param(struct fs_context *fc,
case Opt_ads_bool:
opts->ads = result.boolean;
break;
+ case Opt_ugm:
+ opts->ugm = 1;
+ break;
+ case Opt_ugm_bool:
+ opts->ugm = result.boolean;
+ break;
default:
/* Should not be here unless we forget add case. */
return -EINVAL;
@@ -1905,6 +1915,7 @@ static int ntfs_init_fs_context(struct fs_context *fc)
opts->fs_fmask_inv = opts->fs_dmask_inv = ~current_umask();
opts->prealloc = 1;
opts->ads = 1;
+ opts->ugm = 1;

#ifdef CONFIG_NTFS3_FS_POSIX_ACL
/* Set the default value 'acl' */
diff --git a/fs/ntfs3/xattr.c b/fs/ntfs3/xattr.c
index 2cf45bdd93d5..d6ad06415318 100644
--- a/fs/ntfs3/xattr.c
+++ b/fs/ntfs3/xattr.c
@@ -1015,6 +1015,13 @@ int ntfs_save_wsl_perm(struct inode *inode, __le32 *ea_size)
__le32 value;
struct ntfs_inode *ni = ntfs_i(inode);

+ if (!ni->mi.sbi->options->ugm) {
+ /* Do not save uid/gid/mode/dev in xattr. */
+ if (ea_size)
+ *ea_size = 0;
+ return 0;
+ }
+
ni_lock(ni);
value = cpu_to_le32(i_uid_read(inode));
err = ntfs_set_ea(inode, "$LXUID", sizeof("$LXUID") - 1, &value,
--
2.43.0