* [PATCH RESEND 01/11] cifs: Fix and improve cifs_is_path_accessible() function
2026-07-06 18:48 [PATCH RESEND 00/11] cifs: Various changes Pali Rohár
@ 2026-07-06 18:48 ` Pali Rohár
2026-07-06 18:48 ` [PATCH RESEND 02/11] cifs: Remove code for querying FILE_INFO_STANDARD via CIFSSMBQPathInfo() Pali Rohár
` (9 subsequent siblings)
10 siblings, 0 replies; 21+ messages in thread
From: Pali Rohár @ 2026-07-06 18:48 UTC (permalink / raw)
To: Steve French, Paulo Alcantara, Ronnie Sahlberg; +Cc: linux-cifs, linux-kernel
Do not call SMBQueryInformation() command for path with SMB wildcard
characters on non-UNICODE connection because server expands wildcards.
Function cifs_is_path_accessible() needs to check if the real path exists
and must not expand wildcard characters.
Do not dynamically allocate memory for small FILE_ALL_INFO structure and
instead allocate it on the stack. This structure is allocated on stack by
all other functions.
When CAP_NT_SMBS was not negotiated then do not issue CIFSSMBQPathInfo()
command. This command returns failure by non-NT Win9x SMB servers, so there
is no need try it. The purpose of cifs_is_path_accessible() function is
just to check if the path is accessible, so SMBQueryInformation() for old
servers is enough.
Signed-off-by: Pali Rohár <pali@kernel.org>
---
fs/smb/client/smb1ops.c | 26 ++++++++++++++++----------
1 file changed, 16 insertions(+), 10 deletions(-)
diff --git a/fs/smb/client/smb1ops.c b/fs/smb/client/smb1ops.c
index d34b3d99f6ed..437ee5542a8e 100644
--- a/fs/smb/client/smb1ops.c
+++ b/fs/smb/client/smb1ops.c
@@ -505,21 +505,27 @@ static int
cifs_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const char *full_path)
{
- int rc;
- FILE_ALL_INFO *file_info;
+ int rc = -EOPNOTSUPP;
+ FILE_ALL_INFO file_info;
- file_info = kmalloc_obj(FILE_ALL_INFO);
- if (file_info == NULL)
- return -ENOMEM;
+ if (tcon->ses->capabilities & CAP_NT_SMBS)
+ rc = CIFSSMBQPathInfo(xid, tcon, full_path, &file_info,
+ 0 /* not legacy */, cifs_sb->local_nls,
+ cifs_remap(cifs_sb));
- rc = CIFSSMBQPathInfo(xid, tcon, full_path, file_info,
- 0 /* not legacy */, cifs_sb->local_nls,
- cifs_remap(cifs_sb));
+ /*
+ * Non-UNICODE variant of fallback functions below expands wildcards,
+ * so they cannot be used for querying paths with wildcard characters.
+ * Therefore for such paths returns -ENOENT as they cannot exist.
+ */
+ if ((rc == -EOPNOTSUPP || rc == -EINVAL) &&
+ !(tcon->ses->capabilities & CAP_UNICODE) &&
+ strpbrk(full_path, "*?\"><"))
+ rc = -ENOENT;
if (rc == -EOPNOTSUPP || rc == -EINVAL)
- rc = SMBQueryInformation(xid, tcon, full_path, file_info,
+ rc = SMBQueryInformation(xid, tcon, full_path, &file_info,
cifs_sb->local_nls, cifs_remap(cifs_sb));
- kfree(file_info);
return rc;
}
--
2.20.1
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH RESEND 02/11] cifs: Remove code for querying FILE_INFO_STANDARD via CIFSSMBQPathInfo()
2026-07-06 18:48 [PATCH RESEND 00/11] cifs: Various changes Pali Rohár
2026-07-06 18:48 ` [PATCH RESEND 01/11] cifs: Fix and improve cifs_is_path_accessible() function Pali Rohár
@ 2026-07-06 18:48 ` Pali Rohár
2026-07-06 18:48 ` [PATCH RESEND 03/11] cifs: Remove CIFSSMBSetPathInfoFB() fallback function Pali Rohár
` (8 subsequent siblings)
10 siblings, 0 replies; 21+ messages in thread
From: Pali Rohár @ 2026-07-06 18:48 UTC (permalink / raw)
To: Steve French, Paulo Alcantara, Ronnie Sahlberg; +Cc: linux-cifs, linux-kernel
Querying FILE_INFO_STANDARD structure via SMB_INFO_STANDARD level over
TRANS2_QUERY_PATH_INFORMATION or TRANS2_QUERY_FILE_INFORMATION command
(implemented in CIFSSMBQPathInfo() when called with argument legacy=true)
is mostly unusable.
Win9x SMB server returns over those commands the FILE_INFO_STANDARD
structure with swapped TIME and DATE fields, compared with [MS-CIFS] spec
and Samba server implementation. Therefore this command cannot be used
unless we know against which server implementation we are connected.
There are already two fallback mechanisms for querying information about
path which are working correctly against Samba, NT and Win9x servers:
CIFSFindFirst() and SMBQueryInformation() commands.
So remove TRANS2_QUERY_PATH_INFORMATION/SMB_INFO_STANDARD code from
CIFSSMBQPathInfo() function, when the function is called with legacy=true.
Note that there is no use of CIFSSMBQPathInfo(legacy=true) anymore.
Signed-off-by: Pali Rohár <pali@kernel.org>
---
fs/smb/client/cifssmb.c | 23 +++--------------------
fs/smb/client/smb1ops.c | 4 ++--
fs/smb/client/smb1proto.h | 1 -
3 files changed, 5 insertions(+), 23 deletions(-)
diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c
index d39175cdf1b1..d7a6955d904e 100644
--- a/fs/smb/client/cifssmb.c
+++ b/fs/smb/client/cifssmb.c
@@ -4107,7 +4107,6 @@ CIFSSMBQFileInfo(const unsigned int xid, struct cifs_tcon *tcon,
int
CIFSSMBQPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
const char *search_name, FILE_ALL_INFO *data,
- int legacy /* old style infolevel */,
const struct nls_table *nls_codepage, int remap)
{
/* level 263 SMB_QUERY_FILE_ALL_INFO */
@@ -4157,10 +4156,7 @@ CIFSSMBQPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
byte_count = params + 1 /* pad */ ;
pSMB->TotalParameterCount = cpu_to_le16(params);
pSMB->ParameterCount = pSMB->TotalParameterCount;
- if (legacy)
- pSMB->InformationLevel = cpu_to_le16(SMB_INFO_STANDARD);
- else
- pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO);
+ pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO);
pSMB->Reserved4 = 0;
in_len += byte_count;
pSMB->ByteCount = cpu_to_le16(byte_count);
@@ -4175,27 +4171,14 @@ CIFSSMBQPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
if (rc) /* BB add auto retry on EOPNOTSUPP? */
rc = smb_EIO2(smb_eio_trace_qpathinfo_invalid,
get_bcc(&pSMBr->hdr), 40);
- else if (!legacy && get_bcc(&pSMBr->hdr) < 40)
+ else if (get_bcc(&pSMBr->hdr) < 40)
rc = smb_EIO2(smb_eio_trace_qpathinfo_bcc_too_small,
get_bcc(&pSMBr->hdr), 40);
- else if (legacy && get_bcc(&pSMBr->hdr) < 24)
- /* 24 or 26 expected but we do not read last field */
- rc = smb_EIO2(smb_eio_trace_qpathinfo_bcc_too_small,
- get_bcc(&pSMBr->hdr), 24);
else if (data) {
int size;
__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
- /*
- * On legacy responses we do not read the last field,
- * EAsize, fortunately since it varies by subdialect and
- * also note it differs on Set vs Get, ie two bytes or 4
- * bytes depending but we don't care here.
- */
- if (legacy)
- size = sizeof(FILE_INFO_STANDARD);
- else
- size = sizeof(FILE_ALL_INFO);
+ size = sizeof(FILE_ALL_INFO);
memcpy((char *) data, (char *) &pSMBr->hdr.Protocol +
data_offset, size);
} else
diff --git a/fs/smb/client/smb1ops.c b/fs/smb/client/smb1ops.c
index 437ee5542a8e..e14ab5449943 100644
--- a/fs/smb/client/smb1ops.c
+++ b/fs/smb/client/smb1ops.c
@@ -510,7 +510,7 @@ cifs_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
if (tcon->ses->capabilities & CAP_NT_SMBS)
rc = CIFSSMBQPathInfo(xid, tcon, full_path, &file_info,
- 0 /* not legacy */, cifs_sb->local_nls,
+ cifs_sb->local_nls,
cifs_remap(cifs_sb));
/*
@@ -555,7 +555,7 @@ static int cifs_query_path_info(const unsigned int xid,
* do not even use CIFSSMBQPathInfo() or CIFSSMBQFileInfo() function.
*/
if (tcon->ses->capabilities & CAP_NT_SMBS)
- rc = CIFSSMBQPathInfo(xid, tcon, full_path, &fi, 0 /* not legacy */,
+ rc = CIFSSMBQPathInfo(xid, tcon, full_path, &fi,
cifs_sb->local_nls, cifs_remap(cifs_sb));
/*
diff --git a/fs/smb/client/smb1proto.h b/fs/smb/client/smb1proto.h
index 80eaeb3dd2ec..f0350fa3f173 100644
--- a/fs/smb/client/smb1proto.h
+++ b/fs/smb/client/smb1proto.h
@@ -141,7 +141,6 @@ int CIFSSMBQFileInfo(const unsigned int xid, struct cifs_tcon *tcon,
u16 netfid, FILE_ALL_INFO *pFindData);
int CIFSSMBQPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
const char *search_name, FILE_ALL_INFO *data,
- int legacy /* old style infolevel */,
const struct nls_table *nls_codepage, int remap);
int CIFSSMBUnixQFileInfo(const unsigned int xid, struct cifs_tcon *tcon,
u16 netfid, FILE_UNIX_BASIC_INFO *pFindData);
--
2.20.1
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH RESEND 03/11] cifs: Remove CIFSSMBSetPathInfoFB() fallback function
2026-07-06 18:48 [PATCH RESEND 00/11] cifs: Various changes Pali Rohár
2026-07-06 18:48 ` [PATCH RESEND 01/11] cifs: Fix and improve cifs_is_path_accessible() function Pali Rohár
2026-07-06 18:48 ` [PATCH RESEND 02/11] cifs: Remove code for querying FILE_INFO_STANDARD via CIFSSMBQPathInfo() Pali Rohár
@ 2026-07-06 18:48 ` Pali Rohár
2026-07-06 18:48 ` [PATCH RESEND 04/11] cifs: Remove cifs_backup_query_path_info() and replace it by cifs_query_path_info() Pali Rohár
` (7 subsequent siblings)
10 siblings, 0 replies; 21+ messages in thread
From: Pali Rohár @ 2026-07-06 18:48 UTC (permalink / raw)
To: Steve French, Paulo Alcantara, Ronnie Sahlberg; +Cc: linux-cifs, linux-kernel
This fallback function CIFSSMBSetPathInfoFB() is called only from
CIFSSMBSetPathInfo() function. CIFSSMBSetPathInfo() is used in
smb_set_file_info() which contains all required fallback code, including
fallback via filehandle, since commit f122121796f9 ("cifs: Fix changing
times and read-only attr over SMB1 smb_set_file_info() function") and
commit 92210ccd877b ("cifs: Add fallback code path for cifs_mkdir_setinfo()").
So the CIFSSMBSetPathInfoFB() is just code duplication, which is not needed
anymore. Therefore remove it.
Signed-off-by: Pali Rohár <pali@kernel.org>
---
fs/smb/client/cifssmb.c | 36 ------------------------------------
1 file changed, 36 deletions(-)
diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c
index d7a6955d904e..a2f3a1582cee 100644
--- a/fs/smb/client/cifssmb.c
+++ b/fs/smb/client/cifssmb.c
@@ -5812,38 +5812,6 @@ CIFSSMBSetFileDisposition(const unsigned int xid, struct cifs_tcon *tcon,
return rc;
}
-static int
-CIFSSMBSetPathInfoFB(const unsigned int xid, struct cifs_tcon *tcon,
- const char *fileName, const FILE_BASIC_INFO *data,
- const struct nls_table *nls_codepage,
- struct cifs_sb_info *cifs_sb)
-{
- int oplock = 0;
- struct cifs_open_parms oparms;
- struct cifs_fid fid;
- int rc;
-
- oparms = (struct cifs_open_parms) {
- .tcon = tcon,
- .cifs_sb = cifs_sb,
- .desired_access = GENERIC_WRITE,
- .create_options = cifs_create_options(cifs_sb, 0),
- .disposition = FILE_OPEN,
- .path = fileName,
- .fid = &fid,
- };
-
- rc = CIFS_open(xid, &oparms, &oplock, NULL);
- if (rc)
- goto out;
-
- rc = CIFSSMBSetFileInfo(xid, tcon, data, fid.netfid, current->tgid);
- CIFSSMBClose(xid, tcon, fid.netfid);
-out:
-
- return rc;
-}
-
int
CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
const char *fileName, const FILE_BASIC_INFO *data,
@@ -5922,10 +5890,6 @@ CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
if (rc == -EAGAIN)
goto SetTimesRetry;
- if (rc == -EOPNOTSUPP)
- return CIFSSMBSetPathInfoFB(xid, tcon, fileName, data,
- nls_codepage, cifs_sb);
-
return rc;
}
--
2.20.1
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH RESEND 04/11] cifs: Remove cifs_backup_query_path_info() and replace it by cifs_query_path_info()
2026-07-06 18:48 [PATCH RESEND 00/11] cifs: Various changes Pali Rohár
` (2 preceding siblings ...)
2026-07-06 18:48 ` [PATCH RESEND 03/11] cifs: Remove CIFSSMBSetPathInfoFB() fallback function Pali Rohár
@ 2026-07-06 18:48 ` Pali Rohár
2026-07-06 18:48 ` [PATCH RESEND 05/11] cifs: Fix validation of EAs for WSL reparse points Pali Rohár
` (6 subsequent siblings)
10 siblings, 0 replies; 21+ messages in thread
From: Pali Rohár @ 2026-07-06 18:48 UTC (permalink / raw)
To: Steve French, Paulo Alcantara, Ronnie Sahlberg; +Cc: linux-cifs, linux-kernel
Response handling of cifs_backup_query_path_info() function in
cifs_get_fattr() is broken and can cause buffer overflows because
cifs_backup_query_path_info() prepares request with different info levels
but the response parser in cifs_get_fattr() always expects response
structure FILE_DIRECTORY_INFO.
Code which queries file/dir attributes via CIFSFindFirst() is already
implemented in cifs_query_path_info() function, so extend it for
backup_cred(), which is the only missing functionality compared to
cifs_backup_query_path_info().
With this change the cifs_query_path_info() would do everything which is
open-coded in cifs_set_fattr_ino() and cifs_backup_query_path_info()
functions for SMB1. So remove that SMB1 code from cifs_set_fattr_ino() and
also remove whole cifs_backup_query_path_info() function.
Signed-off-by: Pali Rohár <pali@kernel.org>
---
fs/smb/client/inode.c | 97 -----------------------------------------
fs/smb/client/smb1ops.c | 7 ++-
2 files changed, 5 insertions(+), 99 deletions(-)
diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
index 1dbcfd163ff0..6f82aa9bd033 100644
--- a/fs/smb/client/inode.c
+++ b/fs/smb/client/inode.c
@@ -1059,61 +1059,6 @@ static __u64 simple_hashstr(const char *str)
return hash;
}
-#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
-/**
- * cifs_backup_query_path_info - SMB1 fallback code to get ino
- *
- * Fallback code to get file metadata when we don't have access to
- * full_path (EACCES) and have backup creds.
- *
- * @xid: transaction id used to identify original request in logs
- * @tcon: information about the server share we have mounted
- * @sb: the superblock stores info such as disk space available
- * @full_path: name of the file we are getting the metadata for
- * @resp_buf: will be set to cifs resp buf and needs to be freed with
- * cifs_buf_release() when done with @data
- * @data: will be set to search info result buffer
- */
-static int
-cifs_backup_query_path_info(int xid,
- struct cifs_tcon *tcon,
- struct super_block *sb,
- const char *full_path,
- void **resp_buf,
- FILE_ALL_INFO **data)
-{
- struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
- struct cifs_search_info info = {0};
- u16 flags;
- int rc;
-
- *resp_buf = NULL;
- info.endOfSearch = false;
- if (tcon->unix_ext)
- info.info_level = SMB_FIND_FILE_UNIX;
- else if ((tcon->ses->capabilities &
- tcon->ses->server->vals->cap_nt_find) == 0)
- info.info_level = SMB_FIND_FILE_INFO_STANDARD;
- else if (cifs_sb_flags(cifs_sb) & CIFS_MOUNT_SERVER_INUM)
- info.info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO;
- else /* no srvino useful for fallback to some netapp */
- info.info_level = SMB_FIND_FILE_DIRECTORY_INFO;
-
- flags = CIFS_SEARCH_CLOSE_ALWAYS |
- CIFS_SEARCH_CLOSE_AT_END |
- CIFS_SEARCH_BACKUP_SEARCH;
-
- rc = CIFSFindFirst(xid, tcon, full_path,
- cifs_sb, NULL, flags, &info, false);
- if (rc)
- return rc;
-
- *resp_buf = (void *)info.ntwrk_buf_start;
- *data = (FILE_ALL_INFO *)info.srch_entries_start;
- return 0;
-}
-#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
-
static void cifs_set_fattr_ino(int xid, struct cifs_tcon *tcon, struct super_block *sb,
struct inode **inode, const char *full_path,
struct cifs_open_info_data *data, struct cifs_fattr *fattr)
@@ -1328,45 +1273,6 @@ static int cifs_get_fattr(struct cifs_open_info_data *data,
cifs_create_junction_fattr(fattr, sb);
rc = 0;
break;
- case -EACCES:
-#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
- /*
- * perm errors, try again with backup flags if possible
- *
- * For SMB2 and later the backup intent flag
- * is already sent if needed on open and there
- * is no path based FindFirst operation to use
- * to retry with
- */
- if (backup_cred(cifs_sb) && is_smb1_server(server)) {
- /* for easier reading */
- FILE_ALL_INFO *fi;
- FILE_DIRECTORY_INFO *fdi;
- FILE_ID_FULL_DIR_INFO *si;
-
- rc = cifs_backup_query_path_info(xid, tcon, sb,
- full_path,
- &smb1_backup_rsp_buf,
- &fi);
- if (rc)
- goto out;
-
- move_cifs_info_to_smb2(&data->fi, fi);
- fdi = (FILE_DIRECTORY_INFO *)fi;
- si = (FILE_ID_FULL_DIR_INFO *)fi;
-
- cifs_dir_info_to_fattr(fattr, fdi, cifs_sb);
- fattr->cf_uniqueid = le64_to_cpu(si->UniqueId);
- /* uniqueid set, skip get inum step */
- goto handle_mnt_opt;
- } else {
- /* nothing we can do, bail out */
- goto out;
- }
-#else
- goto out;
-#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
- break;
default:
cifs_dbg(FYI, "%s: unhandled err rc %d\n", __func__, rc);
goto out;
@@ -1381,9 +1287,6 @@ static int cifs_get_fattr(struct cifs_open_info_data *data,
/*
* 4. Tweak fattr based on mount options
*/
-#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
-handle_mnt_opt:
-#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
sbflags = cifs_sb_flags(cifs_sb);
/* query for SFU type info if supported and needed */
if ((fattr->cf_cifsattrs & ATTR_SYSTEM) &&
diff --git a/fs/smb/client/smb1ops.c b/fs/smb/client/smb1ops.c
index e14ab5449943..8bacbcad8858 100644
--- a/fs/smb/client/smb1ops.c
+++ b/fs/smb/client/smb1ops.c
@@ -568,15 +568,18 @@ static int cifs_query_path_info(const unsigned int xid,
/*
* Then fallback to CIFSFindFirst() which works also with non-NT servers
* but does not does not provide NumberOfLinks.
+ * Can be used with backup intent flag to overcome -EACCES error.
*/
- if ((rc == -EOPNOTSUPP || rc == -EINVAL) &&
+ if ((rc == -EOPNOTSUPP || rc == -EINVAL ||
+ (backup_cred(cifs_sb) && rc == -EACCES)) &&
!non_unicode_wildcard) {
if (!(tcon->ses->capabilities & tcon->ses->server->vals->cap_nt_find))
search_info.info_level = SMB_FIND_FILE_INFO_STANDARD;
else
search_info.info_level = SMB_FIND_FILE_FULL_DIRECTORY_INFO;
rc = CIFSFindFirst(xid, tcon, full_path, cifs_sb, NULL,
- CIFS_SEARCH_CLOSE_ALWAYS | CIFS_SEARCH_CLOSE_AT_END,
+ CIFS_SEARCH_CLOSE_ALWAYS | CIFS_SEARCH_CLOSE_AT_END |
+ (backup_cred(cifs_sb) ? CIFS_SEARCH_BACKUP_SEARCH : 0),
&search_info, false);
if (rc == 0) {
if (!(tcon->ses->capabilities & tcon->ses->server->vals->cap_nt_find)) {
--
2.20.1
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH RESEND 05/11] cifs: Fix validation of EAs for WSL reparse points
2026-07-06 18:48 [PATCH RESEND 00/11] cifs: Various changes Pali Rohár
` (3 preceding siblings ...)
2026-07-06 18:48 ` [PATCH RESEND 04/11] cifs: Remove cifs_backup_query_path_info() and replace it by cifs_query_path_info() Pali Rohár
@ 2026-07-06 18:48 ` Pali Rohár
2026-07-06 18:48 ` [PATCH RESEND 06/11] cifs: Validate presence of EA $LXMOD " Pali Rohár
` (5 subsequent siblings)
10 siblings, 0 replies; 21+ messages in thread
From: Pali Rohár @ 2026-07-06 18:48 UTC (permalink / raw)
To: Steve French, Paulo Alcantara, Ronnie Sahlberg; +Cc: linux-cifs, linux-kernel
When wsl_to_fattr() is called from readdir() then we should skip validation
of WSL EAs because readdir FIND_FIRST/NEXT results do not provide EA values
at all. For readdir() reply in this case return DT_UNKNOWN type instead of
DT_REG (which is the result of failed WSL EAs validation).
When validation is skipped and some required EAs are missing then returns
true from wsl_to_fattr() function but do not set fattr->cf_mode. This makes
readdir() to return DT_UNKNOWN type in the same way as it is doing NFS
reparse point function.
This change fixes readdir() result that for some WSL reparse points returns
DT_REG due to missing EAs. After this change it returns DT_UNKNOWN, which
is better than returning wrong type.
Fixes: ef201e8759d2 ("cifs: Validate EAs for WSL reparse points")
Signed-off-by: Pali Rohár <pali@kernel.org>
---
fs/smb/client/reparse.c | 27 ++++++++++++++++++++-------
1 file changed, 20 insertions(+), 7 deletions(-)
diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c
index cd1e1eaee67a..49a6a0cc2b61 100644
--- a/fs/smb/client/reparse.c
+++ b/fs/smb/client/reparse.c
@@ -1123,27 +1123,39 @@ static bool wsl_to_fattr(struct cifs_open_info_data *data,
u32 tag, struct cifs_fattr *fattr)
{
struct smb2_file_full_ea_info *ea;
+ bool ignore_missing_eas = false;
bool have_xattr_dev = false;
+ umode_t reparse_mode_type = 0;
u32 next = 0;
switch (tag) {
case IO_REPARSE_TAG_LX_SYMLINK:
- fattr->cf_mode |= S_IFLNK;
+ reparse_mode_type = S_IFLNK;
break;
case IO_REPARSE_TAG_LX_FIFO:
- fattr->cf_mode |= S_IFIFO;
+ reparse_mode_type = S_IFIFO;
break;
case IO_REPARSE_TAG_AF_UNIX:
- fattr->cf_mode |= S_IFSOCK;
+ reparse_mode_type = S_IFSOCK;
break;
case IO_REPARSE_TAG_LX_CHR:
- fattr->cf_mode |= S_IFCHR;
+ reparse_mode_type = S_IFCHR;
break;
case IO_REPARSE_TAG_LX_BLK:
- fattr->cf_mode |= S_IFBLK;
+ reparse_mode_type = S_IFBLK;
break;
+ default:
+ return false;
}
+ /*
+ * When reparse buffer is not available then this is from readdir() call
+ * which does not provide EAs. readdir() can return DT_UNKNOWN type,
+ * which is signaled by no filling the fattr->cf_mode and returning true.
+ */
+ if (!data->reparse.buf && !data->wsl.eas_len)
+ ignore_missing_eas = true;
+
if (!data->wsl.eas_len)
goto out;
@@ -1168,7 +1180,7 @@ static bool wsl_to_fattr(struct cifs_open_info_data *data,
fattr->cf_gid = wsl_make_kgid(cifs_sb, v);
else if (!strncmp(name, SMB2_WSL_XATTR_MODE, nlen)) {
/* File type in reparse point tag and in xattr mode must match. */
- if (S_DT(fattr->cf_mode) != S_DT(le32_to_cpu(*(__le32 *)v)))
+ if (S_DT(reparse_mode_type) != S_DT(le32_to_cpu(*(__le32 *)v)))
return false;
fattr->cf_mode = (umode_t)le32_to_cpu(*(__le32 *)v);
} else if (!strncmp(name, SMB2_WSL_XATTR_DEV, nlen)) {
@@ -1180,8 +1192,9 @@ static bool wsl_to_fattr(struct cifs_open_info_data *data,
/* Major and minor numbers for char and block devices are mandatory. */
if (!have_xattr_dev && (tag == IO_REPARSE_TAG_LX_CHR || tag == IO_REPARSE_TAG_LX_BLK))
- return false;
+ return ignore_missing_eas;
+ fattr->cf_mode |= reparse_mode_type;
return true;
}
--
2.20.1
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH RESEND 06/11] cifs: Validate presence of EA $LXMOD for WSL reparse points
2026-07-06 18:48 [PATCH RESEND 00/11] cifs: Various changes Pali Rohár
` (4 preceding siblings ...)
2026-07-06 18:48 ` [PATCH RESEND 05/11] cifs: Fix validation of EAs for WSL reparse points Pali Rohár
@ 2026-07-06 18:48 ` Pali Rohár
2026-07-06 22:30 ` Steve French
2026-07-06 18:48 ` [PATCH RESEND 07/11] cifs: Check if server supports EAs before trying to set it for WSL Pali Rohár
` (4 subsequent siblings)
10 siblings, 1 reply; 21+ messages in thread
From: Pali Rohár @ 2026-07-06 18:48 UTC (permalink / raw)
To: Steve French, Paulo Alcantara, Ronnie Sahlberg; +Cc: linux-cifs, linux-kernel
S_DT part of xattr $LXMOD is mandatory for all WSL reparse points except the
WSL symlink and Win32 socket. Microsoft WSL subsystem does not recognize
them without xattr $LXMOD too, and treat such inodes as regular files.
Fixes: ef201e8759d2 ("cifs: Validate EAs for WSL reparse points")
Fixes: 78e26bec4d6d ("smb: client: parse uid, gid, mode and dev from WSL reparse points")
Signed-off-by: Pali Rohár <pali@kernel.org>
---
fs/smb/client/reparse.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c
index 49a6a0cc2b61..99e93b56374f 100644
--- a/fs/smb/client/reparse.c
+++ b/fs/smb/client/reparse.c
@@ -1124,6 +1124,7 @@ static bool wsl_to_fattr(struct cifs_open_info_data *data,
{
struct smb2_file_full_ea_info *ea;
bool ignore_missing_eas = false;
+ bool have_xattr_mode = false;
bool have_xattr_dev = false;
umode_t reparse_mode_type = 0;
u32 next = 0;
@@ -1183,6 +1184,7 @@ static bool wsl_to_fattr(struct cifs_open_info_data *data,
if (S_DT(reparse_mode_type) != S_DT(le32_to_cpu(*(__le32 *)v)))
return false;
fattr->cf_mode = (umode_t)le32_to_cpu(*(__le32 *)v);
+ have_xattr_mode = true;
} else if (!strncmp(name, SMB2_WSL_XATTR_DEV, nlen)) {
fattr->cf_rdev = reparse_mkdev(v);
have_xattr_dev = true;
@@ -1194,6 +1196,16 @@ static bool wsl_to_fattr(struct cifs_open_info_data *data,
if (!have_xattr_dev && (tag == IO_REPARSE_TAG_LX_CHR || tag == IO_REPARSE_TAG_LX_BLK))
return ignore_missing_eas;
+ /*
+ * S_DT part of xattr MODE is mandatory for all WSL reparse points except the WSL symlink.
+ * Microsoft WSL does not recognize them without xattr MODE too (except the WSL symlink).
+ * IO_REPARSE_TAG_AF_UNIX is here an exception because this reparse point is used by both
+ * WSL subsystem and native NT/WinAPI subsystems. And NT/WinAPI creates AF UNIX socket
+ * without the xattr MODE and recognize it also without the xattr MODE.
+ */
+ if (!have_xattr_mode && (tag != IO_REPARSE_TAG_AF_UNIX && tag != IO_REPARSE_TAG_LX_SYMLINK))
+ return ignore_missing_eas;
+
fattr->cf_mode |= reparse_mode_type;
return true;
}
--
2.20.1
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH RESEND 06/11] cifs: Validate presence of EA $LXMOD for WSL reparse points
2026-07-06 18:48 ` [PATCH RESEND 06/11] cifs: Validate presence of EA $LXMOD " Pali Rohár
@ 2026-07-06 22:30 ` Steve French
2026-07-06 22:39 ` Pali Rohár
0 siblings, 1 reply; 21+ messages in thread
From: Steve French @ 2026-07-06 22:30 UTC (permalink / raw)
To: Pali Rohár; +Cc: CIFS
If this EA is missing, does Linux client still recognize it (not
talking about WSL just what Linux client would see)?
On Mon, Jul 6, 2026 at 1:51 PM Pali Rohár <pali@kernel.org> wrote:
>
> S_DT part of xattr $LXMOD is mandatory for all WSL reparse points except the
> WSL symlink and Win32 socket. Microsoft WSL subsystem does not recognize
> them without xattr $LXMOD too, and treat such inodes as regular files.
>
> Fixes: ef201e8759d2 ("cifs: Validate EAs for WSL reparse points")
> Fixes: 78e26bec4d6d ("smb: client: parse uid, gid, mode and dev from WSL reparse points")
> Signed-off-by: Pali Rohár <pali@kernel.org>
> ---
> fs/smb/client/reparse.c | 12 ++++++++++++
> 1 file changed, 12 insertions(+)
>
> diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c
> index 49a6a0cc2b61..99e93b56374f 100644
> --- a/fs/smb/client/reparse.c
> +++ b/fs/smb/client/reparse.c
> @@ -1124,6 +1124,7 @@ static bool wsl_to_fattr(struct cifs_open_info_data *data,
> {
> struct smb2_file_full_ea_info *ea;
> bool ignore_missing_eas = false;
> + bool have_xattr_mode = false;
> bool have_xattr_dev = false;
> umode_t reparse_mode_type = 0;
> u32 next = 0;
> @@ -1183,6 +1184,7 @@ static bool wsl_to_fattr(struct cifs_open_info_data *data,
> if (S_DT(reparse_mode_type) != S_DT(le32_to_cpu(*(__le32 *)v)))
> return false;
> fattr->cf_mode = (umode_t)le32_to_cpu(*(__le32 *)v);
> + have_xattr_mode = true;
> } else if (!strncmp(name, SMB2_WSL_XATTR_DEV, nlen)) {
> fattr->cf_rdev = reparse_mkdev(v);
> have_xattr_dev = true;
> @@ -1194,6 +1196,16 @@ static bool wsl_to_fattr(struct cifs_open_info_data *data,
> if (!have_xattr_dev && (tag == IO_REPARSE_TAG_LX_CHR || tag == IO_REPARSE_TAG_LX_BLK))
> return ignore_missing_eas;
>
> + /*
> + * S_DT part of xattr MODE is mandatory for all WSL reparse points except the WSL symlink.
> + * Microsoft WSL does not recognize them without xattr MODE too (except the WSL symlink).
> + * IO_REPARSE_TAG_AF_UNIX is here an exception because this reparse point is used by both
> + * WSL subsystem and native NT/WinAPI subsystems. And NT/WinAPI creates AF UNIX socket
> + * without the xattr MODE and recognize it also without the xattr MODE.
> + */
> + if (!have_xattr_mode && (tag != IO_REPARSE_TAG_AF_UNIX && tag != IO_REPARSE_TAG_LX_SYMLINK))
> + return ignore_missing_eas;
> +
> fattr->cf_mode |= reparse_mode_type;
> return true;
> }
> --
> 2.20.1
>
>
--
Thanks,
Steve
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH RESEND 06/11] cifs: Validate presence of EA $LXMOD for WSL reparse points
2026-07-06 22:30 ` Steve French
@ 2026-07-06 22:39 ` Pali Rohár
2026-07-06 22:55 ` Steve French
0 siblings, 1 reply; 21+ messages in thread
From: Pali Rohár @ 2026-07-06 22:39 UTC (permalink / raw)
To: Steve French; +Cc: CIFS
With this change applied, such nodes are recognized as "regular files" same as in WSL.
On Monday 06 July 2026 17:30:35 Steve French wrote:
> If this EA is missing, does Linux client still recognize it (not
> talking about WSL just what Linux client would see)?
>
> On Mon, Jul 6, 2026 at 1:51 PM Pali Rohár <pali@kernel.org> wrote:
> >
> > S_DT part of xattr $LXMOD is mandatory for all WSL reparse points except the
> > WSL symlink and Win32 socket. Microsoft WSL subsystem does not recognize
> > them without xattr $LXMOD too, and treat such inodes as regular files.
> >
> > Fixes: ef201e8759d2 ("cifs: Validate EAs for WSL reparse points")
> > Fixes: 78e26bec4d6d ("smb: client: parse uid, gid, mode and dev from WSL reparse points")
> > Signed-off-by: Pali Rohár <pali@kernel.org>
> > ---
> > fs/smb/client/reparse.c | 12 ++++++++++++
> > 1 file changed, 12 insertions(+)
> >
> > diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c
> > index 49a6a0cc2b61..99e93b56374f 100644
> > --- a/fs/smb/client/reparse.c
> > +++ b/fs/smb/client/reparse.c
> > @@ -1124,6 +1124,7 @@ static bool wsl_to_fattr(struct cifs_open_info_data *data,
> > {
> > struct smb2_file_full_ea_info *ea;
> > bool ignore_missing_eas = false;
> > + bool have_xattr_mode = false;
> > bool have_xattr_dev = false;
> > umode_t reparse_mode_type = 0;
> > u32 next = 0;
> > @@ -1183,6 +1184,7 @@ static bool wsl_to_fattr(struct cifs_open_info_data *data,
> > if (S_DT(reparse_mode_type) != S_DT(le32_to_cpu(*(__le32 *)v)))
> > return false;
> > fattr->cf_mode = (umode_t)le32_to_cpu(*(__le32 *)v);
> > + have_xattr_mode = true;
> > } else if (!strncmp(name, SMB2_WSL_XATTR_DEV, nlen)) {
> > fattr->cf_rdev = reparse_mkdev(v);
> > have_xattr_dev = true;
> > @@ -1194,6 +1196,16 @@ static bool wsl_to_fattr(struct cifs_open_info_data *data,
> > if (!have_xattr_dev && (tag == IO_REPARSE_TAG_LX_CHR || tag == IO_REPARSE_TAG_LX_BLK))
> > return ignore_missing_eas;
> >
> > + /*
> > + * S_DT part of xattr MODE is mandatory for all WSL reparse points except the WSL symlink.
> > + * Microsoft WSL does not recognize them without xattr MODE too (except the WSL symlink).
> > + * IO_REPARSE_TAG_AF_UNIX is here an exception because this reparse point is used by both
> > + * WSL subsystem and native NT/WinAPI subsystems. And NT/WinAPI creates AF UNIX socket
> > + * without the xattr MODE and recognize it also without the xattr MODE.
> > + */
> > + if (!have_xattr_mode && (tag != IO_REPARSE_TAG_AF_UNIX && tag != IO_REPARSE_TAG_LX_SYMLINK))
> > + return ignore_missing_eas;
> > +
> > fattr->cf_mode |= reparse_mode_type;
> > return true;
> > }
> > --
> > 2.20.1
> >
> >
>
>
> --
> Thanks,
>
> Steve
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH RESEND 06/11] cifs: Validate presence of EA $LXMOD for WSL reparse points
2026-07-06 22:39 ` Pali Rohár
@ 2026-07-06 22:55 ` Steve French
2026-07-06 23:02 ` Pali Rohár
0 siblings, 1 reply; 21+ messages in thread
From: Steve French @ 2026-07-06 22:55 UTC (permalink / raw)
To: Pali Rohár; +Cc: CIFS
Without this change would Linux client recognize it more accurately?
On Mon, Jul 6, 2026 at 5:39 PM Pali Rohár <pali@kernel.org> wrote:
>
> With this change applied, such nodes are recognized as "regular files" same as in WSL.
>
> On Monday 06 July 2026 17:30:35 Steve French wrote:
> > If this EA is missing, does Linux client still recognize it (not
> > talking about WSL just what Linux client would see)?
> >
> > On Mon, Jul 6, 2026 at 1:51 PM Pali Rohár <pali@kernel.org> wrote:
> > >
> > > S_DT part of xattr $LXMOD is mandatory for all WSL reparse points except the
> > > WSL symlink and Win32 socket. Microsoft WSL subsystem does not recognize
> > > them without xattr $LXMOD too, and treat such inodes as regular files.
> > >
> > > Fixes: ef201e8759d2 ("cifs: Validate EAs for WSL reparse points")
> > > Fixes: 78e26bec4d6d ("smb: client: parse uid, gid, mode and dev from WSL reparse points")
> > > Signed-off-by: Pali Rohár <pali@kernel.org>
> > > ---
> > > fs/smb/client/reparse.c | 12 ++++++++++++
> > > 1 file changed, 12 insertions(+)
> > >
> > > diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c
> > > index 49a6a0cc2b61..99e93b56374f 100644
> > > --- a/fs/smb/client/reparse.c
> > > +++ b/fs/smb/client/reparse.c
> > > @@ -1124,6 +1124,7 @@ static bool wsl_to_fattr(struct cifs_open_info_data *data,
> > > {
> > > struct smb2_file_full_ea_info *ea;
> > > bool ignore_missing_eas = false;
> > > + bool have_xattr_mode = false;
> > > bool have_xattr_dev = false;
> > > umode_t reparse_mode_type = 0;
> > > u32 next = 0;
> > > @@ -1183,6 +1184,7 @@ static bool wsl_to_fattr(struct cifs_open_info_data *data,
> > > if (S_DT(reparse_mode_type) != S_DT(le32_to_cpu(*(__le32 *)v)))
> > > return false;
> > > fattr->cf_mode = (umode_t)le32_to_cpu(*(__le32 *)v);
> > > + have_xattr_mode = true;
> > > } else if (!strncmp(name, SMB2_WSL_XATTR_DEV, nlen)) {
> > > fattr->cf_rdev = reparse_mkdev(v);
> > > have_xattr_dev = true;
> > > @@ -1194,6 +1196,16 @@ static bool wsl_to_fattr(struct cifs_open_info_data *data,
> > > if (!have_xattr_dev && (tag == IO_REPARSE_TAG_LX_CHR || tag == IO_REPARSE_TAG_LX_BLK))
> > > return ignore_missing_eas;
> > >
> > > + /*
> > > + * S_DT part of xattr MODE is mandatory for all WSL reparse points except the WSL symlink.
> > > + * Microsoft WSL does not recognize them without xattr MODE too (except the WSL symlink).
> > > + * IO_REPARSE_TAG_AF_UNIX is here an exception because this reparse point is used by both
> > > + * WSL subsystem and native NT/WinAPI subsystems. And NT/WinAPI creates AF UNIX socket
> > > + * without the xattr MODE and recognize it also without the xattr MODE.
> > > + */
> > > + if (!have_xattr_mode && (tag != IO_REPARSE_TAG_AF_UNIX && tag != IO_REPARSE_TAG_LX_SYMLINK))
> > > + return ignore_missing_eas;
> > > +
> > > fattr->cf_mode |= reparse_mode_type;
> > > return true;
> > > }
> > > --
> > > 2.20.1
> > >
> > >
> >
> >
> > --
> > Thanks,
> >
> > Steve
--
Thanks,
Steve
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH RESEND 06/11] cifs: Validate presence of EA $LXMOD for WSL reparse points
2026-07-06 22:55 ` Steve French
@ 2026-07-06 23:02 ` Pali Rohár
0 siblings, 0 replies; 21+ messages in thread
From: Pali Rohár @ 2026-07-06 23:02 UTC (permalink / raw)
To: Steve French; +Cc: CIFS
Without this change, Linux recognize some nodes as special files which
are not special at all (for WSL). With this change, the recognition is
aligned with the WSL. Those nodes which are special for WSL are
recognized as special also by Linux client. And those nodes which
regular files for WSL are also regular for Linux client.
So this is mostly alignment between MS WSL client and Linux client, to
ensure interoperability.
On Monday 06 July 2026 17:55:50 Steve French wrote:
> Without this change would Linux client recognize it more accurately?
>
> On Mon, Jul 6, 2026 at 5:39 PM Pali Rohár <pali@kernel.org> wrote:
> >
> > With this change applied, such nodes are recognized as "regular files" same as in WSL.
> >
> > On Monday 06 July 2026 17:30:35 Steve French wrote:
> > > If this EA is missing, does Linux client still recognize it (not
> > > talking about WSL just what Linux client would see)?
> > >
> > > On Mon, Jul 6, 2026 at 1:51 PM Pali Rohár <pali@kernel.org> wrote:
> > > >
> > > > S_DT part of xattr $LXMOD is mandatory for all WSL reparse points except the
> > > > WSL symlink and Win32 socket. Microsoft WSL subsystem does not recognize
> > > > them without xattr $LXMOD too, and treat such inodes as regular files.
> > > >
> > > > Fixes: ef201e8759d2 ("cifs: Validate EAs for WSL reparse points")
> > > > Fixes: 78e26bec4d6d ("smb: client: parse uid, gid, mode and dev from WSL reparse points")
> > > > Signed-off-by: Pali Rohár <pali@kernel.org>
> > > > ---
> > > > fs/smb/client/reparse.c | 12 ++++++++++++
> > > > 1 file changed, 12 insertions(+)
> > > >
> > > > diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c
> > > > index 49a6a0cc2b61..99e93b56374f 100644
> > > > --- a/fs/smb/client/reparse.c
> > > > +++ b/fs/smb/client/reparse.c
> > > > @@ -1124,6 +1124,7 @@ static bool wsl_to_fattr(struct cifs_open_info_data *data,
> > > > {
> > > > struct smb2_file_full_ea_info *ea;
> > > > bool ignore_missing_eas = false;
> > > > + bool have_xattr_mode = false;
> > > > bool have_xattr_dev = false;
> > > > umode_t reparse_mode_type = 0;
> > > > u32 next = 0;
> > > > @@ -1183,6 +1184,7 @@ static bool wsl_to_fattr(struct cifs_open_info_data *data,
> > > > if (S_DT(reparse_mode_type) != S_DT(le32_to_cpu(*(__le32 *)v)))
> > > > return false;
> > > > fattr->cf_mode = (umode_t)le32_to_cpu(*(__le32 *)v);
> > > > + have_xattr_mode = true;
> > > > } else if (!strncmp(name, SMB2_WSL_XATTR_DEV, nlen)) {
> > > > fattr->cf_rdev = reparse_mkdev(v);
> > > > have_xattr_dev = true;
> > > > @@ -1194,6 +1196,16 @@ static bool wsl_to_fattr(struct cifs_open_info_data *data,
> > > > if (!have_xattr_dev && (tag == IO_REPARSE_TAG_LX_CHR || tag == IO_REPARSE_TAG_LX_BLK))
> > > > return ignore_missing_eas;
> > > >
> > > > + /*
> > > > + * S_DT part of xattr MODE is mandatory for all WSL reparse points except the WSL symlink.
> > > > + * Microsoft WSL does not recognize them without xattr MODE too (except the WSL symlink).
> > > > + * IO_REPARSE_TAG_AF_UNIX is here an exception because this reparse point is used by both
> > > > + * WSL subsystem and native NT/WinAPI subsystems. And NT/WinAPI creates AF UNIX socket
> > > > + * without the xattr MODE and recognize it also without the xattr MODE.
> > > > + */
> > > > + if (!have_xattr_mode && (tag != IO_REPARSE_TAG_AF_UNIX && tag != IO_REPARSE_TAG_LX_SYMLINK))
> > > > + return ignore_missing_eas;
> > > > +
> > > > fattr->cf_mode |= reparse_mode_type;
> > > > return true;
> > > > }
> > > > --
> > > > 2.20.1
> > > >
> > > >
> > >
> > >
> > > --
> > > Thanks,
> > >
> > > Steve
>
>
>
> --
> Thanks,
>
> Steve
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH RESEND 07/11] cifs: Check if server supports EAs before trying to set it for WSL
2026-07-06 18:48 [PATCH RESEND 00/11] cifs: Various changes Pali Rohár
` (5 preceding siblings ...)
2026-07-06 18:48 ` [PATCH RESEND 06/11] cifs: Validate presence of EA $LXMOD " Pali Rohár
@ 2026-07-06 18:48 ` Pali Rohár
2026-07-06 22:33 ` Steve French
2026-07-06 18:48 ` [PATCH RESEND 08/11] cifs: Extend ->set_EA() callback to allow operate on reparse point Pali Rohár
` (3 subsequent siblings)
10 siblings, 1 reply; 21+ messages in thread
From: Pali Rohár @ 2026-07-06 18:48 UTC (permalink / raw)
To: Steve French, Paulo Alcantara, Ronnie Sahlberg; +Cc: linux-cifs, linux-kernel
WSL special files store information into EAs. When EAs are not supported
on the server filesystem then fast fail in mknod_wsl() function.
Signed-off-by: Pali Rohár <pali@kernel.org>
---
fs/smb/client/reparse.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c
index 99e93b56374f..e2ea59a17c9c 100644
--- a/fs/smb/client/reparse.c
+++ b/fs/smb/client/reparse.c
@@ -668,6 +668,13 @@ static int mknod_wsl(unsigned int xid, struct inode *inode,
struct kvec reparse_iov, xattr_iov;
int rc;
+ /*
+ * WSL special files store information into EAs. When EAs are not
+ * supported on the server filesystem then fast fail.
+ */
+ if (!(le32_to_cpu(tcon->fsAttrInfo.Attributes) & FILE_SUPPORTS_EXTENDED_ATTRIBUTES))
+ return -EOPNOTSUPP;
+
rc = wsl_set_reparse_buf(&buf, mode, symname, cifs_sb, &reparse_iov);
if (rc)
return rc;
--
2.20.1
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH RESEND 07/11] cifs: Check if server supports EAs before trying to set it for WSL
2026-07-06 18:48 ` [PATCH RESEND 07/11] cifs: Check if server supports EAs before trying to set it for WSL Pali Rohár
@ 2026-07-06 22:33 ` Steve French
2026-07-06 22:42 ` Pali Rohár
0 siblings, 1 reply; 21+ messages in thread
From: Steve French @ 2026-07-06 22:33 UTC (permalink / raw)
To: Pali Rohár
Cc: Steve French, Paulo Alcantara, Ronnie Sahlberg, linux-cifs,
linux-kernel
If this were purely Linux, without this patch what would happen with
creating special files if the server didn't support EAs. Would some
special files still work?
On Mon, Jul 6, 2026 at 1:52 PM Pali Rohár <pali@kernel.org> wrote:
>
> WSL special files store information into EAs. When EAs are not supported
> on the server filesystem then fast fail in mknod_wsl() function.
>
> Signed-off-by: Pali Rohár <pali@kernel.org>
> ---
> fs/smb/client/reparse.c | 7 +++++++
> 1 file changed, 7 insertions(+)
>
> diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c
> index 99e93b56374f..e2ea59a17c9c 100644
> --- a/fs/smb/client/reparse.c
> +++ b/fs/smb/client/reparse.c
> @@ -668,6 +668,13 @@ static int mknod_wsl(unsigned int xid, struct inode *inode,
> struct kvec reparse_iov, xattr_iov;
> int rc;
>
> + /*
> + * WSL special files store information into EAs. When EAs are not
> + * supported on the server filesystem then fast fail.
> + */
> + if (!(le32_to_cpu(tcon->fsAttrInfo.Attributes) & FILE_SUPPORTS_EXTENDED_ATTRIBUTES))
> + return -EOPNOTSUPP;
> +
> rc = wsl_set_reparse_buf(&buf, mode, symname, cifs_sb, &reparse_iov);
> if (rc)
> return rc;
> --
> 2.20.1
>
>
--
Thanks,
Steve
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH RESEND 07/11] cifs: Check if server supports EAs before trying to set it for WSL
2026-07-06 22:33 ` Steve French
@ 2026-07-06 22:42 ` Pali Rohár
[not found] ` <CAH2r5mvDGM7i8EsdPkW681a_AnXOAsHkg38eJqegO7g2gV8qAw@mail.gmail.com>
0 siblings, 1 reply; 21+ messages in thread
From: Pali Rohár @ 2026-07-06 22:42 UTC (permalink / raw)
To: Steve French
Cc: Steve French, Paulo Alcantara, Ronnie Sahlberg, linux-cifs,
linux-kernel
If the server does not support EAs then setting the EAs (which is part
of the creating WSL special file) would fail and so WSL special file
would not be created. Major and minor numbers of char and block devices
is stored in the EAs and therefore without EAs support, it is not
possible to create and store char and block device nodes.
On Monday 06 July 2026 17:33:42 Steve French wrote:
> If this were purely Linux, without this patch what would happen with
> creating special files if the server didn't support EAs. Would some
> special files still work?
>
> On Mon, Jul 6, 2026 at 1:52 PM Pali Rohár <pali@kernel.org> wrote:
> >
> > WSL special files store information into EAs. When EAs are not supported
> > on the server filesystem then fast fail in mknod_wsl() function.
> >
> > Signed-off-by: Pali Rohár <pali@kernel.org>
> > ---
> > fs/smb/client/reparse.c | 7 +++++++
> > 1 file changed, 7 insertions(+)
> >
> > diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c
> > index 99e93b56374f..e2ea59a17c9c 100644
> > --- a/fs/smb/client/reparse.c
> > +++ b/fs/smb/client/reparse.c
> > @@ -668,6 +668,13 @@ static int mknod_wsl(unsigned int xid, struct inode *inode,
> > struct kvec reparse_iov, xattr_iov;
> > int rc;
> >
> > + /*
> > + * WSL special files store information into EAs. When EAs are not
> > + * supported on the server filesystem then fast fail.
> > + */
> > + if (!(le32_to_cpu(tcon->fsAttrInfo.Attributes) & FILE_SUPPORTS_EXTENDED_ATTRIBUTES))
> > + return -EOPNOTSUPP;
> > +
> > rc = wsl_set_reparse_buf(&buf, mode, symname, cifs_sb, &reparse_iov);
> > if (rc)
> > return rc;
> > --
> > 2.20.1
> >
> >
>
>
> --
> Thanks,
>
> Steve
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH RESEND 08/11] cifs: Extend ->set_EA() callback to allow operate on reparse point
2026-07-06 18:48 [PATCH RESEND 00/11] cifs: Various changes Pali Rohár
` (6 preceding siblings ...)
2026-07-06 18:48 ` [PATCH RESEND 07/11] cifs: Check if server supports EAs before trying to set it for WSL Pali Rohár
@ 2026-07-06 18:48 ` Pali Rohár
2026-07-06 18:48 ` [PATCH RESEND 09/11] cifs: Create native Window socket file compatible also with WSL subsystem Pali Rohár
` (2 subsequent siblings)
10 siblings, 0 replies; 21+ messages in thread
From: Pali Rohár @ 2026-07-06 18:48 UTC (permalink / raw)
To: Steve French, Paulo Alcantara, Ronnie Sahlberg; +Cc: linux-cifs, linux-kernel
Upcoming changes for setting EAs needs to operate on the reparse point
iself. This change extend >set_EA() callback with a new boolean parameter
to allow it.
Signed-off-by: Pali Rohár <pali@kernel.org>
---
fs/smb/client/cifsglob.h | 1 +
fs/smb/client/cifssmb.c | 20 +++++++++++++++++++-
fs/smb/client/smb1proto.h | 3 ++-
fs/smb/client/smb2ops.c | 6 ++++--
fs/smb/client/xattr.c | 3 ++-
5 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
index 99f9e6dca62b..36a2e807f1c9 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -569,6 +569,7 @@ struct smb_version_operations {
const unsigned char *, const unsigned char *, char *,
size_t, struct cifs_sb_info *);
int (*set_EA)(const unsigned int, struct cifs_tcon *, const char *,
+ bool open_reparse_point,
const char *, const void *, const __u16,
const struct nls_table *, struct cifs_sb_info *);
struct smb_ntsd * (*get_acl)(struct cifs_sb_info *cifssb, struct inode *ino,
diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c
index a2f3a1582cee..e2c3b76cc0e8 100644
--- a/fs/smb/client/cifssmb.c
+++ b/fs/smb/client/cifssmb.c
@@ -3138,6 +3138,7 @@ struct inode *cifs_create_reparse_inode(struct cifs_open_info_data *data,
rc = CIFSSMBSetEA(xid,
tcon,
full_path,
+ true /* open reparse point */,
&ea->ea_data[0],
&ea->ea_data[ea->ea_name_length+1],
le16_to_cpu(ea->ea_value_length),
@@ -6285,7 +6286,8 @@ CIFSSMBQAllEAs(const unsigned int xid, struct cifs_tcon *tcon,
int
CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon,
- const char *fileName, const char *ea_name, const void *ea_value,
+ const char *fileName, bool open_reparse_point,
+ const char *ea_name, const void *ea_value,
const __u16 ea_value_len, const struct nls_table *nls_codepage,
struct cifs_sb_info *cifs_sb)
{
@@ -6299,6 +6301,22 @@ CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon,
__u16 params, param_offset, byte_count, offset, count;
int remap = cifs_remap(cifs_sb);
+ /*
+ * On NT systems which supports reparse points, the TRANS2_SET_PATH_INFORMATION
+ * operates on the reparse point itself and not the path location where reparse
+ * point redirects. So the behavior of TRANS2_SET_PATH_INFORMATION is as if the
+ * path was opened with OPEN_REPARSE_POINT flag. Hence this SMB1 SetEA function
+ * implements only the behavior of "open_reparse_point=true" parameter.
+ *
+ * TODO: Implement "open_reparse_point=false" support for SMB1 SetEA. For this
+ * is needed to call NT OPEN without OPEN_REPARSE_POINT flag and then call
+ * TRANS2_SET_FILE_INFORMATION.
+ *
+ * On systems which do not support reparse points, the behavior of both
+ * "open_reparse_point=true" and "open_reparse_point=false" is same.
+ */
+ (void)open_reparse_point;
+
cifs_dbg(FYI, "In SetEA\n");
SetEARetry:
rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
diff --git a/fs/smb/client/smb1proto.h b/fs/smb/client/smb1proto.h
index f0350fa3f173..8a17350fee7c 100644
--- a/fs/smb/client/smb1proto.h
+++ b/fs/smb/client/smb1proto.h
@@ -209,7 +209,8 @@ ssize_t CIFSSMBQAllEAs(const unsigned int xid, struct cifs_tcon *tcon,
const unsigned char *ea_name, char *EAData,
size_t buf_size, struct cifs_sb_info *cifs_sb);
int CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon,
- const char *fileName, const char *ea_name,
+ const char *fileName, bool open_reparse_point,
+ const char *ea_name,
const void *ea_value, const __u16 ea_value_len,
const struct nls_table *nls_codepage,
struct cifs_sb_info *cifs_sb);
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 06e9322a762a..4f7eefa7a683 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -1179,7 +1179,8 @@ smb2_query_eas(const unsigned int xid, struct cifs_tcon *tcon,
static int
smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
- const char *path, const char *ea_name, const void *ea_value,
+ const char *path, bool open_reparse_point,
+ const char *ea_name, const void *ea_value,
const __u16 ea_value_len, const struct nls_table *nls_codepage,
struct cifs_sb_info *cifs_sb)
{
@@ -1281,7 +1282,8 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
.path = path,
.desired_access = FILE_WRITE_EA,
.disposition = FILE_OPEN,
- .create_options = cifs_create_options(cifs_sb, 0),
+ .create_options = cifs_create_options(cifs_sb,
+ open_reparse_point ? OPEN_REPARSE_POINT : 0),
.fid = &fid,
.replay = !!(retries),
};
diff --git a/fs/smb/client/xattr.c b/fs/smb/client/xattr.c
index 5091f6c0d7fe..93e4cc114d75 100644
--- a/fs/smb/client/xattr.c
+++ b/fs/smb/client/xattr.c
@@ -154,7 +154,8 @@ static int cifs_xattr_set(const struct xattr_handler *handler,
if (pTcon->ses->server->ops->set_EA) {
rc = pTcon->ses->server->ops->set_EA(xid, pTcon,
- full_path, name, value, (__u16)size,
+ full_path, false /* open reparse point */,
+ name, value, (__u16)size,
cifs_sb->local_nls, cifs_sb);
if (rc == 0)
inode_set_ctime_current(inode);
--
2.20.1
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH RESEND 09/11] cifs: Create native Window socket file compatible also with WSL subsystem
2026-07-06 18:48 [PATCH RESEND 00/11] cifs: Various changes Pali Rohár
` (7 preceding siblings ...)
2026-07-06 18:48 ` [PATCH RESEND 08/11] cifs: Extend ->set_EA() callback to allow operate on reparse point Pali Rohár
@ 2026-07-06 18:48 ` Pali Rohár
2026-07-06 18:48 ` [PATCH RESEND 10/11] cifs: Add support for parsing WSL symlinks in version 1 format Pali Rohár
2026-07-06 18:48 ` [PATCH RESEND 11/11] cifs: Add support for creating " Pali Rohár
10 siblings, 0 replies; 21+ messages in thread
From: Pali Rohár @ 2026-07-06 18:48 UTC (permalink / raw)
To: Steve French, Paulo Alcantara, Ronnie Sahlberg; +Cc: linux-cifs, linux-kernel
After creating a native socket via the IO_REPARSE_TAG_AF_UNIX reparse
point, try to set also the WSL EA $LXMOD.
This makes the native Win32 / NT socket file recognized also under
WSL subsystem. WSL subsystem uses same reparse point tag but requires also
to be EA $LXMOD set.
Fixes: 45a99d5d1173 ("cifs: Add support for creating native Windows sockets")
Signed-off-by: Pali Rohár <pali@kernel.org>
---
fs/smb/client/reparse.c | 30 ++++++++++++++++++++++++++++--
1 file changed, 28 insertions(+), 2 deletions(-)
diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c
index e2ea59a17c9c..50eeea127a42 100644
--- a/fs/smb/client/reparse.c
+++ b/fs/smb/client/reparse.c
@@ -384,7 +384,7 @@ static int detect_directory_symlink_target(struct cifs_sb_info *cifs_sb,
static int create_native_socket(const unsigned int xid, struct inode *inode,
struct dentry *dentry, struct cifs_tcon *tcon,
- const char *full_path)
+ const char *full_path, umode_t mode)
{
struct reparse_data_buffer buf = {
.ReparseTag = cpu_to_le32(IO_REPARSE_TAG_AF_UNIX),
@@ -398,6 +398,10 @@ static int create_native_socket(const unsigned int xid, struct inode *inode,
.iov_base = &buf,
.iov_len = sizeof(buf),
};
+#ifdef CONFIG_CIFS_XATTR
+ const __le64 xattr_mode_val = cpu_to_le64(mode);
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+#endif
struct inode *new;
int rc = 0;
@@ -408,6 +412,28 @@ static int create_native_socket(const unsigned int xid, struct inode *inode,
d_instantiate(dentry, new);
else
rc = PTR_ERR(new);
+
+ /*
+ * Try to set also optional WSL EA $LXMOD but ignore errors.
+ * WSL socket and native Win32/NT sockets uses same reparse point
+ * tag IO_REPARSE_TAG_AF_UNIX. WSL subsystem additionally requires
+ * EA $LXMOD to be set with the S_IFSOCK value. So setting this EA
+ * allows native socket to be recognized also by WSL subsystem.
+ * Note that setting of both EAs and reparse points is not supported
+ * by NTFS filesystem on Windows 8 / Windows Server 2012 and always
+ * fails. So ignore failures from this set_EA call.
+ */
+#ifdef CONFIG_CIFS_XATTR
+ if (!rc && tcon->ses->server->ops->set_EA &&
+ (le32_to_cpu(tcon->fsAttrInfo.Attributes) & FILE_SUPPORTS_EXTENDED_ATTRIBUTES))
+ tcon->ses->server->ops->set_EA(xid, tcon, full_path,
+ true /* open reparse point */,
+ SMB2_WSL_XATTR_MODE,
+ &xattr_mode_val,
+ SMB2_WSL_XATTR_MODE_SIZE,
+ cifs_sb->local_nls, cifs_sb);
+#endif
+
cifs_free_open_info(&data);
return rc;
}
@@ -717,7 +743,7 @@ int mknod_reparse(unsigned int xid, struct inode *inode,
struct smb3_fs_context *ctx = CIFS_SB(inode->i_sb)->ctx;
if (S_ISSOCK(mode) && !ctx->nonativesocket && ctx->reparse_type != CIFS_REPARSE_TYPE_NONE)
- return create_native_socket(xid, inode, dentry, tcon, full_path);
+ return create_native_socket(xid, inode, dentry, tcon, full_path, mode);
switch (ctx->reparse_type) {
case CIFS_REPARSE_TYPE_NFS:
--
2.20.1
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH RESEND 10/11] cifs: Add support for parsing WSL symlinks in version 1 format
2026-07-06 18:48 [PATCH RESEND 00/11] cifs: Various changes Pali Rohár
` (8 preceding siblings ...)
2026-07-06 18:48 ` [PATCH RESEND 09/11] cifs: Create native Window socket file compatible also with WSL subsystem Pali Rohár
@ 2026-07-06 18:48 ` Pali Rohár
2026-07-06 20:50 ` Pali Rohár
2026-07-06 21:02 ` Pali Rohár
2026-07-06 18:48 ` [PATCH RESEND 11/11] cifs: Add support for creating " Pali Rohár
10 siblings, 2 replies; 21+ messages in thread
From: Pali Rohár @ 2026-07-06 18:48 UTC (permalink / raw)
To: Steve French, Paulo Alcantara, Ronnie Sahlberg; +Cc: linux-cifs, linux-kernel
MS-FSCC 2.1.2.7 for IO_REPARSE_TAG_LX_SYMLINK reparse points currently
documents only layout version 2 format.
IO_REPARSE_TAG_LX_SYMLINK reparse point buffer of layout version 1 format
is documented in the newly released Microsoft WSL source code at github:
https://github.com/microsoft/WSL/blob/2.5.8/test/windows/DrvFsTests.cpp#L775-L815
Difference between version 1 and version 2 is that version 1 stores the
symlink target location into data section of the file, but version 2 stores
it directly into the reparse point buffer.
This change implements support for parsing WSL symlinks in this layout
version 1 format by Linux SMB client and so allow to recognize these type
of symlinks like Windows WSL.
Signed-off-by: Pali Rohár <pali@kernel.org>
---
fs/smb/client/cifsproto.h | 1 +
fs/smb/client/inode.c | 1 +
fs/smb/client/reparse.c | 118 ++++++++++++++++++++++++++++++++------
fs/smb/common/fscc.h | 5 +-
4 files changed, 105 insertions(+), 20 deletions(-)
diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h
index c4ababcb51a3..1ed97957038f 100644
--- a/fs/smb/client/cifsproto.h
+++ b/fs/smb/client/cifsproto.h
@@ -390,6 +390,7 @@ int cifs_update_super_prepath(struct cifs_sb_info *cifs_sb, char *prefix);
char *extract_hostname(const char *unc);
char *extract_sharename(const char *unc);
int parse_reparse_point(struct reparse_data_buffer *buf, u32 plen,
+ unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const char *full_path,
struct cifs_open_info_data *data);
int __cifs_sfu_make_node(unsigned int xid, struct inode *inode,
diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
index 6f82aa9bd033..002a2ec85db5 100644
--- a/fs/smb/client/inode.c
+++ b/fs/smb/client/inode.c
@@ -1171,6 +1171,7 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
reparse_buf = server->ops->get_reparse_point_buffer(iov, &reparse_len);
rc = parse_reparse_point(reparse_buf, reparse_len,
+ xid, tcon,
cifs_sb, full_path, data);
/*
* If the reparse point was not handled but it is the
diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c
index 50eeea127a42..0402a30d0d53 100644
--- a/fs/smb/client/reparse.c
+++ b/fs/smb/client/reparse.c
@@ -1045,51 +1045,124 @@ static int parse_reparse_native_symlink(struct reparse_symlink_data_buffer *sym,
}
static int parse_reparse_wsl_symlink(struct reparse_wsl_symlink_data_buffer *buf,
+ unsigned int xid,
+ struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb,
+ const char *full_path,
struct cifs_open_info_data *data)
{
int len = le16_to_cpu(buf->ReparseDataLength);
int data_offset = offsetof(typeof(*buf), Target) - offsetof(typeof(*buf), Version);
- int symname_utf8_len;
+ bool free_symname_utf8 = false;
+ struct cifs_open_parms oparms;
+ struct cifs_io_parms io_parms;
+ unsigned int symname_utf8_len;
+ char *symname_utf8 = NULL;
__le16 *symname_utf16;
int symname_utf16_len;
+ struct cifs_fid fid;
+ __u32 oplock;
+ int buf_type;
+ int rc = 0;
- if (len <= data_offset) {
+ if (len < data_offset) {
cifs_dbg(VFS, "srv returned malformed wsl symlink buffer\n");
- return smb_EIO2(smb_eio_trace_reparse_wsl_symbuf,
+ rc = smb_EIO2(smb_eio_trace_reparse_wsl_symbuf,
len, data_offset);
+ goto out;
}
- /* MS-FSCC 2.1.2.7 defines layout of the Target field only for Version 2. */
u32 version = le32_to_cpu(buf->Version);
+ switch (version) {
+ case 1:
+ /*
+ * Layout version 1 stores the symlink target in the data section of
+ * the file encoded in UTF-8 without trailing null-term byte.
+ */
- if (version != 2) {
+ oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, FILE_READ_DATA,
+ FILE_OPEN, CREATE_NOT_DIR | OPEN_REPARSE_POINT,
+ ACL_NO_MODE);
+ oparms.fid = &fid;
+ oplock = tcon->ses->server->oplocks ? REQ_OPLOCK : 0;
+ rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, NULL);
+ if (rc)
+ goto out;
+
+ free_symname_utf8 = true;
+ symname_utf8_len = le64_to_cpu(data->fi.EndOfFile);
+ symname_utf8 = kmalloc(symname_utf8_len, GFP_KERNEL);
+ if (!symname_utf8) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ buf_type = CIFS_NO_BUFFER;
+ io_parms = (struct cifs_io_parms) {
+ .netfid = fid.netfid,
+ .pid = current->tgid,
+ .tcon = tcon,
+ .offset = 0,
+ .length = symname_utf8_len,
+ };
+ rc = tcon->ses->server->ops->sync_read(xid, &fid, &io_parms,
+ &symname_utf8_len,
+ &symname_utf8,
+ &buf_type);
+ if (!rc && symname_utf8_len != le64_to_cpu(data->fi.EndOfFile))
+ rc = -EIO;
+
+ tcon->ses->server->ops->close(xid, tcon, &fid);
+
+ if (rc) {
+ cifs_dbg(VFS, "cannot read wsl symlink target location: %d\n", rc);
+ goto out;
+ }
+
+ break;
+ case 2:
+ /*
+ * Layout version 2 stores the symlink target in the reparse buffer
+ * field Target encoded in UTF-8 without trailing null-term byte.
+ */
+ symname_utf8_len = len - data_offset;
+ symname_utf8 = buf->Target;
+ break;
+ default:
cifs_dbg(VFS, "srv returned unsupported wsl symlink version %u\n", version);
return smb_EIO1(smb_eio_trace_reparse_wsl_ver, version);
}
- /* Target for Version 2 is in UTF-8 but without trailing null-term byte */
- symname_utf8_len = len - data_offset;
+ if (symname_utf8_len == 0) {
+ cifs_dbg(VFS, "srv returned empty wsl symlink target location\n");
+ rc = -EIO;
+ goto out;
+ }
+
/*
* Check that buffer does not contain null byte
* because Linux cannot process symlink with null byte.
*/
- size_t ulen = strnlen(buf->Target, symname_utf8_len);
+ size_t ulen = strnlen(symname_utf8, symname_utf8_len);
if (ulen != symname_utf8_len) {
cifs_dbg(VFS, "srv returned null byte in wsl symlink target location\n");
- return smb_EIO2(smb_eio_trace_reparse_wsl_ver,
+ rc = smb_EIO2(smb_eio_trace_reparse_wsl_ver,
ulen, symname_utf8_len);
+ goto out;
}
symname_utf16 = kzalloc(symname_utf8_len * 2, GFP_KERNEL);
- if (!symname_utf16)
- return -ENOMEM;
- symname_utf16_len = utf8s_to_utf16s(buf->Target, symname_utf8_len,
+ if (!symname_utf16) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ symname_utf16_len = utf8s_to_utf16s(symname_utf8, symname_utf8_len,
UTF16_LITTLE_ENDIAN,
(wchar_t *) symname_utf16, symname_utf8_len * 2);
if (symname_utf16_len < 0) {
kfree(symname_utf16);
- return symname_utf16_len;
+ rc = symname_utf16_len;
+ goto out;
}
symname_utf16_len *= 2; /* utf8s_to_utf16s() returns number of u16 items, not byte length */
@@ -1097,14 +1170,23 @@ static int parse_reparse_wsl_symlink(struct reparse_wsl_symlink_data_buffer *buf
symname_utf16_len, true,
cifs_sb->local_nls);
kfree(symname_utf16);
- if (!data->symlink_target)
- return -ENOMEM;
+ if (!data->symlink_target) {
+ rc = -ENOMEM;
+ goto out;
+ }
- return 0;
+out:
+ if (free_symname_utf8)
+ kfree(symname_utf8);
+
+ return rc;
}
int parse_reparse_point(struct reparse_data_buffer *buf,
- u32 plen, struct cifs_sb_info *cifs_sb,
+ u32 plen,
+ unsigned int xid,
+ struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb,
const char *full_path,
struct cifs_open_info_data *data)
{
@@ -1122,7 +1204,7 @@ int parse_reparse_point(struct reparse_data_buffer *buf,
case IO_REPARSE_TAG_LX_SYMLINK:
return parse_reparse_wsl_symlink(
(struct reparse_wsl_symlink_data_buffer *)buf,
- cifs_sb, data);
+ xid, tcon, cifs_sb, full_path, data);
case IO_REPARSE_TAG_AF_UNIX:
case IO_REPARSE_TAG_LX_FIFO:
case IO_REPARSE_TAG_LX_CHR:
diff --git a/fs/smb/common/fscc.h b/fs/smb/common/fscc.h
index 859849a42fec..f5ec6b203c42 100644
--- a/fs/smb/common/fscc.h
+++ b/fs/smb/common/fscc.h
@@ -69,12 +69,13 @@ struct reparse_nfs_data_buffer {
__u8 DataBuffer[];
} __packed;
-/* For IO_REPARSE_TAG_LX_SYMLINK - see MS-FSCC 2.1.2.7 */
+/* For IO_REPARSE_TAG_LX_SYMLINK - see MS-FSCC 2.1.2.7 and
+ * https://github.com/microsoft/WSL/blob/2.5.8/test/windows/DrvFsTests.cpp#L775-L815 */
struct reparse_wsl_symlink_data_buffer {
__le32 ReparseTag;
__le16 ReparseDataLength;
__u16 Reserved;
- __le32 Version; /* Always 2 */
+ __le32 Version; /* 1 - stores symlink path in file data section; 2 - stores symlink path in Target[] field */
__u8 Target[]; /* Variable Length UTF-8 string without nul-term */
} __packed;
--
2.20.1
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH RESEND 10/11] cifs: Add support for parsing WSL symlinks in version 1 format
2026-07-06 18:48 ` [PATCH RESEND 10/11] cifs: Add support for parsing WSL symlinks in version 1 format Pali Rohár
@ 2026-07-06 20:50 ` Pali Rohár
2026-07-06 21:02 ` Pali Rohár
1 sibling, 0 replies; 21+ messages in thread
From: Pali Rohár @ 2026-07-06 20:50 UTC (permalink / raw)
To: Steve French, Paulo Alcantara, Ronnie Sahlberg; +Cc: linux-cifs, linux-kernel
On Monday 06 July 2026 20:48:18 Pali Rohár wrote:
> - /* MS-FSCC 2.1.2.7 defines layout of the Target field only for Version 2. */
> u32 version = le32_to_cpu(buf->Version);
> + switch (version) {
> + case 1:
> + /*
> + * Layout version 1 stores the symlink target in the data section of
> + * the file encoded in UTF-8 without trailing null-term byte.
> + */
>
> - if (version != 2) {
> + oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, FILE_READ_DATA,
> + FILE_OPEN, CREATE_NOT_DIR | OPEN_REPARSE_POINT,
> + ACL_NO_MODE);
> + oparms.fid = &fid;
> + oplock = tcon->ses->server->oplocks ? REQ_OPLOCK : 0;
> + rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, NULL);
> + if (rc)
> + goto out;
> +
> + free_symname_utf8 = true;
> + symname_utf8_len = le64_to_cpu(data->fi.EndOfFile);
> + symname_utf8 = kmalloc(symname_utf8_len, GFP_KERNEL);
> + if (!symname_utf8) {
> + rc = -ENOMEM;
> + goto out;
> + }
Sashiko AI wrote:
https://sashiko.dev/#/patchset/20260706184819.22124-1-pali%40kernel.org
Does this leak the server-side file handle and oplock?
If this kmalloc() fails, the code jumps to the out label without calling
tcon->ses->server->ops->close(xid, tcon, &fid).
This is really a problem. I would suggest to move the kmalloc() block
above the tcon->ses->server->ops->open(...) block which should address
this issue. E.g.:
diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c
index 3418e4b66022..3fe57d166776 100644
--- a/fs/smb/client/reparse.c
+++ b/fs/smb/client/reparse.c
@@ -1133,6 +1133,14 @@ static int parse_reparse_wsl_symlink(struct reparse_wsl_symlink_data_buffer *buf
* the file encoded in UTF-8 without trailing null-term byte.
*/
+ free_symname_utf8 = true;
+ symname_utf8_len = le64_to_cpu(data->fi.EndOfFile);
+ symname_utf8 = kmalloc(symname_utf8_len, GFP_KERNEL);
+ if (!symname_utf8) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, FILE_READ_DATA,
FILE_OPEN, CREATE_NOT_DIR | OPEN_REPARSE_POINT,
ACL_NO_MODE);
@@ -1142,14 +1150,6 @@ static int parse_reparse_wsl_symlink(struct reparse_wsl_symlink_data_buffer *buf
if (rc)
goto out;
- free_symname_utf8 = true;
- symname_utf8_len = le64_to_cpu(data->fi.EndOfFile);
- symname_utf8 = kmalloc(symname_utf8_len, GFP_KERNEL);
- if (!symname_utf8) {
- rc = -ENOMEM;
- goto out;
- }
-
buf_type = CIFS_NO_BUFFER;
io_parms = (struct cifs_io_parms) {
.netfid = fid.netfid,
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH RESEND 10/11] cifs: Add support for parsing WSL symlinks in version 1 format
2026-07-06 18:48 ` [PATCH RESEND 10/11] cifs: Add support for parsing WSL symlinks in version 1 format Pali Rohár
2026-07-06 20:50 ` Pali Rohár
@ 2026-07-06 21:02 ` Pali Rohár
1 sibling, 0 replies; 21+ messages in thread
From: Pali Rohár @ 2026-07-06 21:02 UTC (permalink / raw)
To: Steve French, Paulo Alcantara, Ronnie Sahlberg; +Cc: linux-cifs, linux-kernel
On Monday 06 July 2026 20:48:18 Pali Rohár wrote:
> + switch (version) {
> + case 1:
> + /*
> + * Layout version 1 stores the symlink target in the data section of
> + * the file encoded in UTF-8 without trailing null-term byte.
> + */
>
> - if (version != 2) {
> + oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, FILE_READ_DATA,
> + FILE_OPEN, CREATE_NOT_DIR | OPEN_REPARSE_POINT,
> + ACL_NO_MODE);
> + oparms.fid = &fid;
> + oplock = tcon->ses->server->oplocks ? REQ_OPLOCK : 0;
> + rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, NULL);
> + if (rc)
> + goto out;
> +
> + free_symname_utf8 = true;
> + symname_utf8_len = le64_to_cpu(data->fi.EndOfFile);
And Sashiko found another issue
https://sashiko.dev/#/patchset/20260706184819.22124-1-pali%40kernel.org
Does this unconditionally read EndOfFile from the smb2_file_all_info
struct, even when data->contains_posix_file_info is true?
If the mount uses POSIX extensions, smb311_posix_get_fattr() populates the
posix_fi union member instead of fi, so could this read garbage data
overlaying DosAttributes and Inode? Also, should there be a bounds check
against something like PATH_MAX before passing this length to kmalloc()?
I did not though about combining POSIX extensions and WSL together.
And I must admit that it is tricky that sometimes it is needed to read
data from data->posix_fi and sometimes from data->fi. Following change
should address this issue. About bounds checks, do you have any
suggestion which one to use?
diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c
index 3fe57d166776..d7cb8eca7838 100644
--- a/fs/smb/client/reparse.c
+++ b/fs/smb/client/reparse.c
@@ -1114,6 +1114,7 @@ static int parse_reparse_wsl_symlink(struct reparse_wsl_symlink_data_buffer *buf
__le16 *symname_utf16;
int symname_utf16_len;
struct cifs_fid fid;
+ u64 file_size;
__u32 oplock;
int buf_type;
int rc = 0;
@@ -1133,8 +1134,12 @@ static int parse_reparse_wsl_symlink(struct reparse_wsl_symlink_data_buffer *buf
* the file encoded in UTF-8 without trailing null-term byte.
*/
+ file_size = data->contains_posix_file_info ?
+ le64_to_cpu(data->posix_fi.EndOfFile) :
+ le64_to_cpu(data->fi.EndOfFile);
+
free_symname_utf8 = true;
- symname_utf8_len = le64_to_cpu(data->fi.EndOfFile);
+ symname_utf8_len = file_size;
symname_utf8 = kmalloc(symname_utf8_len, GFP_KERNEL);
if (!symname_utf8) {
rc = -ENOMEM;
@@ -1162,7 +1167,7 @@ static int parse_reparse_wsl_symlink(struct reparse_wsl_symlink_data_buffer *buf
&symname_utf8_len,
&symname_utf8,
&buf_type);
- if (!rc && symname_utf8_len != le64_to_cpu(data->fi.EndOfFile))
+ if (!rc && symname_utf8_len != file_size)
rc = -EIO;
tcon->ses->server->ops->close(xid, tcon, &fid);
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH RESEND 11/11] cifs: Add support for creating WSL symlinks in version 1 format
2026-07-06 18:48 [PATCH RESEND 00/11] cifs: Various changes Pali Rohár
` (9 preceding siblings ...)
2026-07-06 18:48 ` [PATCH RESEND 10/11] cifs: Add support for parsing WSL symlinks in version 1 format Pali Rohár
@ 2026-07-06 18:48 ` Pali Rohár
10 siblings, 0 replies; 21+ messages in thread
From: Pali Rohár @ 2026-07-06 18:48 UTC (permalink / raw)
To: Steve French, Paulo Alcantara, Ronnie Sahlberg; +Cc: linux-cifs, linux-kernel
Add a new mount option -o symlink=wsl1 which cause that all newly created
symlinks would be of WSL style in layout version 1 format. This type of
symlinks is supported by all WSL versions.
Existing mount option -o symlink=wsl will be now an alias to -o
symlink=wsl2 which creates symlinks in layour version 2 format.
Signed-off-by: Pali Rohár <pali@kernel.org>
---
fs/smb/client/cifsglob.h | 9 +++--
fs/smb/client/fs_context.c | 11 ++++--
fs/smb/client/fs_context.h | 3 +-
fs/smb/client/link.c | 3 +-
fs/smb/client/reparse.c | 73 ++++++++++++++++++++++++++++++++------
5 files changed, 81 insertions(+), 18 deletions(-)
diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
index 36a2e807f1c9..ca6f1be8e7d4 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -190,7 +190,8 @@ enum cifs_symlink_type {
CIFS_SYMLINK_TYPE_MFSYMLINKS,
CIFS_SYMLINK_TYPE_SFU,
CIFS_SYMLINK_TYPE_NFS,
- CIFS_SYMLINK_TYPE_WSL,
+ CIFS_SYMLINK_TYPE_WSL1,
+ CIFS_SYMLINK_TYPE_WSL2,
};
static inline const char *cifs_symlink_type_str(enum cifs_symlink_type type)
@@ -208,8 +209,10 @@ static inline const char *cifs_symlink_type_str(enum cifs_symlink_type type)
return "sfu";
case CIFS_SYMLINK_TYPE_NFS:
return "nfs";
- case CIFS_SYMLINK_TYPE_WSL:
- return "wsl";
+ case CIFS_SYMLINK_TYPE_WSL1:
+ return "wsl1";
+ case CIFS_SYMLINK_TYPE_WSL2:
+ return "wsl2";
default:
return "unknown";
}
diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c
index abc29500a664..3e8e4b43690a 100644
--- a/fs/smb/client/fs_context.c
+++ b/fs/smb/client/fs_context.c
@@ -376,7 +376,9 @@ static const match_table_t symlink_flavor_tokens = {
{ Opt_symlink_mfsymlinks, "mfsymlinks" },
{ Opt_symlink_sfu, "sfu" },
{ Opt_symlink_nfs, "nfs" },
- { Opt_symlink_wsl, "wsl" },
+ { Opt_symlink_wsl1, "wsl1" },
+ { Opt_symlink_wsl2, "wsl2" },
+ { Opt_symlink_wsl2, "wsl" }, /* wsl - alias for wsl2 */
{ Opt_symlink_err, NULL },
};
@@ -407,8 +409,11 @@ static int parse_symlink_flavor(struct fs_context *fc, char *value,
case Opt_symlink_nfs:
ctx->symlink_type = CIFS_SYMLINK_TYPE_NFS;
break;
- case Opt_symlink_wsl:
- ctx->symlink_type = CIFS_SYMLINK_TYPE_WSL;
+ case Opt_symlink_wsl1:
+ ctx->symlink_type = CIFS_SYMLINK_TYPE_WSL1;
+ break;
+ case Opt_symlink_wsl2:
+ ctx->symlink_type = CIFS_SYMLINK_TYPE_WSL2;
break;
default:
cifs_errorf(fc, "bad symlink= option: %s\n", value);
diff --git a/fs/smb/client/fs_context.h b/fs/smb/client/fs_context.h
index a80a5caff23c..56bbb20a30a3 100644
--- a/fs/smb/client/fs_context.h
+++ b/fs/smb/client/fs_context.h
@@ -72,7 +72,8 @@ enum cifs_symlink_parm {
Opt_symlink_mfsymlinks,
Opt_symlink_sfu,
Opt_symlink_nfs,
- Opt_symlink_wsl,
+ Opt_symlink_wsl1,
+ Opt_symlink_wsl2,
Opt_symlink_err
};
diff --git a/fs/smb/client/link.c b/fs/smb/client/link.c
index dd127917a340..4c61d6b64cae 100644
--- a/fs/smb/client/link.c
+++ b/fs/smb/client/link.c
@@ -613,7 +613,8 @@ cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,
case CIFS_SYMLINK_TYPE_NATIVE:
case CIFS_SYMLINK_TYPE_NFS:
- case CIFS_SYMLINK_TYPE_WSL:
+ case CIFS_SYMLINK_TYPE_WSL1:
+ case CIFS_SYMLINK_TYPE_WSL2:
if (CIFS_REPARSE_SUPPORT(pTcon)) {
rc = create_reparse_symlink(xid, inode, direntry, pTcon,
full_path, symname);
diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c
index 0402a30d0d53..3418e4b66022 100644
--- a/fs/smb/client/reparse.c
+++ b/fs/smb/client/reparse.c
@@ -22,7 +22,7 @@ static int mknod_nfs(unsigned int xid, struct inode *inode,
static int mknod_wsl(unsigned int xid, struct inode *inode,
struct dentry *dentry, struct cifs_tcon *tcon,
const char *full_path, umode_t mode, dev_t dev,
- const char *symname);
+ const char *symname, int symver);
static int create_native_symlink(const unsigned int xid, struct inode *inode,
struct dentry *dentry, struct cifs_tcon *tcon,
@@ -43,8 +43,10 @@ int create_reparse_symlink(const unsigned int xid, struct inode *inode,
return create_native_symlink(xid, inode, dentry, tcon, full_path, symname);
case CIFS_SYMLINK_TYPE_NFS:
return mknod_nfs(xid, inode, dentry, tcon, full_path, S_IFLNK, 0, symname);
- case CIFS_SYMLINK_TYPE_WSL:
- return mknod_wsl(xid, inode, dentry, tcon, full_path, S_IFLNK, 0, symname);
+ case CIFS_SYMLINK_TYPE_WSL1:
+ return mknod_wsl(xid, inode, dentry, tcon, full_path, S_IFLNK, 0, symname, 1);
+ case CIFS_SYMLINK_TYPE_WSL2:
+ return mknod_wsl(xid, inode, dentry, tcon, full_path, S_IFLNK, 0, symname, 2);
default:
return -EOPNOTSUPP;
}
@@ -539,6 +541,7 @@ static int mknod_nfs(unsigned int xid, struct inode *inode,
static int wsl_set_reparse_buf(struct reparse_data_buffer **buf,
mode_t mode, const char *symname,
+ int symver,
struct cifs_sb_info *cifs_sb,
struct kvec *iov)
{
@@ -574,15 +577,20 @@ static int wsl_set_reparse_buf(struct reparse_data_buffer **buf,
kfree(symname_utf16);
return -ENOMEM;
}
- /* Version field must be set to 2 (MS-FSCC 2.1.2.7) */
- symlink_buf->Version = cpu_to_le32(2);
- /* Target for Version 2 is in UTF-8 but without trailing null-term byte */
+ symlink_buf->Version = cpu_to_le32(symver);
+ /* Target is in UTF-8 but without trailing null-term byte */
symname_utf8_len = utf16s_to_utf8s((wchar_t *)symname_utf16, symname_utf16_len/2,
UTF16_LITTLE_ENDIAN,
symlink_buf->Target,
symname_utf8_maxlen);
*buf = (struct reparse_data_buffer *)symlink_buf;
- buf_len = sizeof(struct reparse_wsl_symlink_data_buffer) + symname_utf8_len;
+ buf_len = sizeof(struct reparse_wsl_symlink_data_buffer);
+ /*
+ * Layout version 2 stores the symlink target in the reparse point buffer.
+ * Layout version 1 stores the symlink target in the data section of the file.
+ */
+ if (symver == 2)
+ buf_len += symname_utf8_len;
kfree(symname_utf16);
break;
default:
@@ -683,7 +691,7 @@ static int wsl_set_xattrs(struct inode *inode, umode_t _mode,
static int mknod_wsl(unsigned int xid, struct inode *inode,
struct dentry *dentry, struct cifs_tcon *tcon,
const char *full_path, umode_t mode, dev_t dev,
- const char *symname)
+ const char *symname, int symver)
{
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifs_open_info_data data;
@@ -692,6 +700,12 @@ static int mknod_wsl(unsigned int xid, struct inode *inode,
struct inode *new;
unsigned int len;
struct kvec reparse_iov, xattr_iov;
+ struct cifs_open_parms oparms;
+ struct cifs_io_parms io_parms;
+ unsigned int bytes_written;
+ struct kvec symv1_iov[2];
+ struct cifs_fid fid;
+ __u32 oplock;
int rc;
/*
@@ -701,7 +715,7 @@ static int mknod_wsl(unsigned int xid, struct inode *inode,
if (!(le32_to_cpu(tcon->fsAttrInfo.Attributes) & FILE_SUPPORTS_EXTENDED_ATTRIBUTES))
return -EOPNOTSUPP;
- rc = wsl_set_reparse_buf(&buf, mode, symname, cifs_sb, &reparse_iov);
+ rc = wsl_set_reparse_buf(&buf, mode, symname, symver, cifs_sb, &reparse_iov);
if (rc)
return rc;
@@ -726,6 +740,45 @@ static int mknod_wsl(unsigned int xid, struct inode *inode,
&data, inode->i_sb,
xid, tcon, full_path, false,
&reparse_iov, &xattr_iov);
+ if (!IS_ERR(new) && mode == S_IFLNK && symver == 1) {
+ /*
+ * WSL symlink layout version 1 stores the symlink target
+ * location into the data section of the file.
+ * Store it now after the reparse point file was created.
+ * The target location was allocated into the buf but iov
+ * size filled in reparse_iov by wsl_set_reparse_buf() was
+ * set to smaller so the created reparse point does not
+ * contain it.
+ */
+ oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, FILE_WRITE_DATA,
+ FILE_OPEN, CREATE_NOT_DIR | OPEN_REPARSE_POINT,
+ ACL_NO_MODE);
+ oparms.fid = &fid;
+ oplock = tcon->ses->server->oplocks ? REQ_OPLOCK : 0;
+ rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, NULL);
+ if (!rc) {
+ symv1_iov[1].iov_base = ((struct reparse_wsl_symlink_data_buffer *)buf)->Target;
+ symv1_iov[1].iov_len = strlen((const char *)symv1_iov[1].iov_base);
+ io_parms = (struct cifs_io_parms) {
+ .netfid = fid.netfid,
+ .pid = current->tgid,
+ .tcon = tcon,
+ .offset = 0,
+ .length = symv1_iov[1].iov_len,
+ };
+ rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms,
+ &bytes_written,
+ symv1_iov,
+ ARRAY_SIZE(symv1_iov)-1);
+ if (bytes_written != symv1_iov[1].iov_len)
+ rc = -EIO;
+ tcon->ses->server->ops->close(xid, tcon, &fid);
+ }
+ if (rc) {
+ tcon->ses->server->ops->unlink(xid, tcon, full_path, cifs_sb, NULL);
+ new = ERR_PTR(rc);
+ }
+ }
if (!IS_ERR(new))
d_instantiate(dentry, new);
else
@@ -749,7 +802,7 @@ int mknod_reparse(unsigned int xid, struct inode *inode,
case CIFS_REPARSE_TYPE_NFS:
return mknod_nfs(xid, inode, dentry, tcon, full_path, mode, dev, NULL);
case CIFS_REPARSE_TYPE_WSL:
- return mknod_wsl(xid, inode, dentry, tcon, full_path, mode, dev, NULL);
+ return mknod_wsl(xid, inode, dentry, tcon, full_path, mode, dev, NULL, 0);
default:
return -EOPNOTSUPP;
}
--
2.20.1
^ permalink raw reply related [flat|nested] 21+ messages in thread