summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNamjae Jeon <linkinjeon@kernel.org>2026-07-27 18:07:15 +0900
committerNamjae Jeon <linkinjeon@kernel.org>2026-08-19 18:16:45 +0900
commit4d730a4dd78caa7bf29dda36fa5340a0237a8689 (patch)
treec993dbb1ce56b6c6fb85e5dab6a5b5e411df7376
parent71feaa7686fb9cc08fc81c8a09d120b6204b51a0 (diff)
downloadlinux-4d730a4dd78caa7bf29dda36fa5340a0237a8689.tar.gz
linux-4d730a4dd78caa7bf29dda36fa5340a0237a8689.zip
ntfs: respect per-file chmod mode over mount masks
fmask and dmask provide the default permissions for files without WSL metadata. Once chmod stores a mode in $LXMOD, however, that per-file mode must take precedence so selected files can retain permissions such as execute across remounts. Record whether $LXMOD was found while loading an inode and apply the mount masks only when it is absent. Do not remask the in-memory mode after setattr persists it. Continue loading $LXMOD even when optional $LXUID or $LXGID metadata is missing, since chmod may create only $LXMOD. Fixes: fc053f05ca28 ("ntfs: add reparse and ea operations") Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
-rw-r--r--fs/ntfs/ea.c20
-rw-r--r--fs/ntfs/ea.h3
-rw-r--r--fs/ntfs/file.c10
-rw-r--r--fs/ntfs/inode.c15
-rw-r--r--fs/ntfs/namei.c4
5 files changed, 24 insertions, 28 deletions
diff --git a/fs/ntfs/ea.c b/fs/ntfs/ea.c
index 4fb10d51211c..4fbea76afe7e 100644
--- a/fs/ntfs/ea.c
+++ b/fs/ntfs/ea.c
@@ -406,37 +406,35 @@ out:
* Check for the presence of an EA "$LXDEV" (used by WSL)
* and return its value as a device address
*/
-int ntfs_ea_get_wsl_inode(struct inode *inode, dev_t *rdevp, unsigned int flags)
+int ntfs_ea_get_wsl_inode(struct inode *inode, dev_t *rdevp, unsigned int flags,
+ bool *has_lxmod)
{
int err;
__le32 v;
+ *has_lxmod = false;
+
if (!(flags & NTFS_VOL_UID)) {
/* Load uid to lxuid EA */
err = ntfs_get_ea(inode, "$LXUID", sizeof("$LXUID") - 1, &v,
sizeof(v));
- if (err < 0)
- return err;
- if (err != sizeof(v))
- return -EIO;
- i_uid_write(inode, le32_to_cpu(v));
+ if (err == sizeof(v))
+ i_uid_write(inode, le32_to_cpu(v));
}
if (!(flags & NTFS_VOL_GID)) {
/* Load gid to lxgid EA */
err = ntfs_get_ea(inode, "$LXGID", sizeof("$LXGID") - 1, &v,
sizeof(v));
- if (err < 0)
- return err;
- if (err != sizeof(v))
- return -EIO;
- i_gid_write(inode, le32_to_cpu(v));
+ if (err == sizeof(v))
+ i_gid_write(inode, le32_to_cpu(v));
}
/* Load mode to lxmod EA */
err = ntfs_get_ea(inode, "$LXMOD", sizeof("$LXMOD") - 1, &v, sizeof(v));
if (err == sizeof(v)) {
inode->i_mode = le32_to_cpu(v);
+ *has_lxmod = true;
} else {
/* Everyone gets all permissions. */
inode->i_mode |= 0777;
diff --git a/fs/ntfs/ea.h b/fs/ntfs/ea.h
index 1f63bd55e057..acb39c2a6fbc 100644
--- a/fs/ntfs/ea.h
+++ b/fs/ntfs/ea.h
@@ -10,7 +10,8 @@
extern const struct xattr_handler *const ntfs_xattr_handlers[];
int ntfs_ea_set_wsl_not_symlink(struct ntfs_inode *ni, mode_t mode, dev_t dev);
-int ntfs_ea_get_wsl_inode(struct inode *inode, dev_t *rdevp, unsigned int flags);
+int ntfs_ea_get_wsl_inode(struct inode *inode, dev_t *rdevp, unsigned int flags,
+ bool *has_lxmod);
int ntfs_ea_set_wsl_inode(struct inode *inode, dev_t rdev, __le16 *ea_size,
unsigned int flags);
ssize_t ntfs_listxattr(struct dentry *dentry, char *buffer, size_t size);
diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c
index c8e49f83fd92..d4282822b3ce 100644
--- a/fs/ntfs/file.c
+++ b/fs/ntfs/file.c
@@ -342,14 +342,12 @@ int ntfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
if (ia_valid & ATTR_MODE)
flags |= NTFS_EA_MODE;
- if (S_ISDIR(vi->i_mode))
- vi->i_mode &= ~vol->dmask;
- else
- vi->i_mode &= ~vol->fmask;
-
mutex_lock(&ni->mrec_lock);
- ntfs_ea_set_wsl_inode(vi, 0, NULL, flags);
+ err = ntfs_ea_set_wsl_inode(vi, 0, NULL, flags);
mutex_unlock(&ni->mrec_lock);
+ if (err)
+ goto out;
+
}
mark_inode_dirty(vi);
diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c
index 50e244aa372e..39c7fd8c1149 100644
--- a/fs/ntfs/inode.c
+++ b/fs/ntfs/inode.c
@@ -682,6 +682,7 @@ static int ntfs_read_locked_inode(struct inode *vi)
unsigned int name_len = 4, flags = 0;
int extend_sys = 0;
dev_t dev = 0;
+ bool has_lxmod = false;
bool vol_err = true;
ntfs_debug("Entering for i_ino 0x%llx.", ni->mft_no);
@@ -862,7 +863,7 @@ skip_attr_list_load:
err = ntfs_attr_lookup(AT_EA_INFORMATION, NULL, 0, 0, 0, NULL, 0, ctx);
if (!err) {
NInoSetHasEA(ni);
- ntfs_ea_get_wsl_inode(vi, &dev, flags);
+ ntfs_ea_get_wsl_inode(vi, &dev, flags, &has_lxmod);
}
if (ni->flags & FILE_ATTR_REPARSE_POINT) {
@@ -886,16 +887,18 @@ skip_attr_list_load:
if (S_ISDIR(vi->i_mode)) {
/*
- * Apply the directory permissions mask set in the mount
- * options.
+ * Apply the directory permissions mask set in the mount options
+ * when no per-file WSL mode is present.
*/
- vi->i_mode &= ~vol->dmask;
+ if (!has_lxmod)
+ vi->i_mode &= ~vol->dmask;
/* Things break without this kludge! */
if (vi->i_nlink > 1)
set_nlink(vi, 1);
} else {
- /* Apply the file permissions mask set in the mount options. */
- vi->i_mode &= ~vol->fmask;
+ /* Apply the file permissions mask when no WSL mode is present. */
+ if (!has_lxmod)
+ vi->i_mode &= ~vol->fmask;
}
/*
diff --git a/fs/ntfs/namei.c b/fs/ntfs/namei.c
index 0de5b63ef407..7091b2496fac 100644
--- a/fs/ntfs/namei.c
+++ b/fs/ntfs/namei.c
@@ -424,8 +424,6 @@ static struct ntfs_inode *__ntfs_create(struct mnt_idmap *idmap, struct inode *d
* directories, also setup the index values to the defaults.
*/
if (S_ISDIR(mode)) {
- mode &= ~vol->dmask;
-
NInoSetMstProtected(ni);
ni->itype.index.block_size = 4096;
ni->itype.index.block_size_bits = ntfs_ffs(4096) - 1;
@@ -439,8 +437,6 @@ static struct ntfs_inode *__ntfs_create(struct mnt_idmap *idmap, struct inode *d
ni->itype.index.vcn_size_bits =
vol->sector_size_bits;
}
- } else {
- mode &= ~vol->fmask;
}
if (IS_RDONLY(vi))