mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v7.3 0/9] Various changes
@ 2026-10-26 22:25 Pali Rohár
  2026-10-26 22:25 ` [PATCH v7.3 1/9] cifs: Remove code for querying FILE_INFO_STANDARD via CIFSSMBQPathInfo() Pali Rohár
                   ` (9 more replies)
  0 siblings, 10 replies; 13+ messages in thread
From: Pali Rohár @ 2026-10-26 22:25 UTC (permalink / raw)
  To: Paulo Alcantara, Namjae Jeon; +Cc: linux-cifs, linux-kernel

Rebased changes on top of the v7.3.
No functional change, just fixed 3 oneliners from Sashiko.
Link to previous: https://lore.kernel.org/linux-cifs/20260825230622.24617-1-pali@kernel.org/

Pali Rohár (9):
  cifs: Remove code for querying FILE_INFO_STANDARD via
    CIFSSMBQPathInfo()
  cifs: Remove cifs_backup_query_path_info() and replace it by
    cifs_query_path_info()
  cifs: Fix validation of EAs for WSL reparse points
  cifs: Validate presence of EA $LXMOD for WSL reparse points
  cifs: Check if server supports EAs before trying to set it for WSL
  cifs: Extend ->set_EA() callback to allow operate on reparse point
  cifs: Create native Window socket file compatible also with WSL
    subsystem
  cifs: Add support for parsing WSL symlinks in version 1 format
  cifs: Add support for creating WSL symlinks in version 1 format

 fs/smb/client/cifsglob.h   |  10 +-
 fs/smb/client/cifsproto.h  |   1 +
 fs/smb/client/cifssmb.c    |  43 ++++---
 fs/smb/client/fs_context.c |  11 +-
 fs/smb/client/fs_context.h |   3 +-
 fs/smb/client/inode.c      |  98 +-------------
 fs/smb/client/link.c       |   3 +-
 fs/smb/client/reparse.c    | 257 ++++++++++++++++++++++++++++++++-----
 fs/smb/client/smb1ops.c    |  11 +-
 fs/smb/client/smb1proto.h  |   4 +-
 fs/smb/client/smb2ops.c    |   6 +-
 fs/smb/client/xattr.c      |   3 +-
 fs/smb/common/fscc.h       |   5 +-
 13 files changed, 287 insertions(+), 168 deletions(-)

-- 
2.20.1


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v7.3 1/9] cifs: Remove code for querying FILE_INFO_STANDARD via CIFSSMBQPathInfo()
  2026-10-26 22:25 [PATCH v7.3 0/9] Various changes Pali Rohár
@ 2026-10-26 22:25 ` Pali Rohár
  2026-10-26 22:25 ` [PATCH v7.3 2/9] cifs: Remove cifs_backup_query_path_info() and replace it by cifs_query_path_info() Pali Rohár
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Pali Rohár @ 2026-10-26 22:25 UTC (permalink / raw)
  To: Paulo Alcantara, Namjae Jeon; +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 6dddbd84b93b..8bdd81af6d21 100644
--- a/fs/smb/client/cifssmb.c
+++ b/fs/smb/client/cifssmb.c
@@ -4144,7 +4144,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 */
@@ -4194,10 +4193,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);
@@ -4212,27 +4208,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 3ac4126267f6..9c77752cb7f9 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));
 
 	/*
@@ -556,7 +556,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	[flat|nested] 13+ messages in thread

* [PATCH v7.3 2/9] cifs: Remove cifs_backup_query_path_info() and replace it by cifs_query_path_info()
  2026-10-26 22:25 [PATCH v7.3 0/9] Various changes Pali Rohár
  2026-10-26 22:25 ` [PATCH v7.3 1/9] cifs: Remove code for querying FILE_INFO_STANDARD via CIFSSMBQPathInfo() Pali Rohár
@ 2026-10-26 22:25 ` Pali Rohár
  2026-10-26 22:25 ` [PATCH v7.3 3/9] cifs: Fix validation of EAs for WSL reparse points Pali Rohár
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Pali Rohár @ 2026-10-26 22:25 UTC (permalink / raw)
  To: Paulo Alcantara, Namjae Jeon; +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 f29555f3c7d0..3620eb378deb 100644
--- a/fs/smb/client/inode.c
+++ b/fs/smb/client/inode.c
@@ -1070,61 +1070,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)
@@ -1340,45 +1285,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;
@@ -1393,9 +1299,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 9c77752cb7f9..be73e4b9028c 100644
--- a/fs/smb/client/smb1ops.c
+++ b/fs/smb/client/smb1ops.c
@@ -569,15 +569,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	[flat|nested] 13+ messages in thread

* [PATCH v7.3 3/9] cifs: Fix validation of EAs for WSL reparse points
  2026-10-26 22:25 [PATCH v7.3 0/9] Various changes Pali Rohár
  2026-10-26 22:25 ` [PATCH v7.3 1/9] cifs: Remove code for querying FILE_INFO_STANDARD via CIFSSMBQPathInfo() Pali Rohár
  2026-10-26 22:25 ` [PATCH v7.3 2/9] cifs: Remove cifs_backup_query_path_info() and replace it by cifs_query_path_info() Pali Rohár
@ 2026-10-26 22:25 ` Pali Rohár
  2026-10-26 22:25 ` [PATCH v7.3 4/9] cifs: Validate presence of EA $LXMOD " Pali Rohár
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Pali Rohár @ 2026-10-26 22:25 UTC (permalink / raw)
  To: Paulo Alcantara, Namjae Jeon; +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 | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c
index 3a27773186ae..e547510ca3c5 100644
--- a/fs/smb/client/reparse.c
+++ b/fs/smb/client/reparse.c
@@ -1152,6 +1152,7 @@ static bool wsl_to_fattr(struct cifs_open_info_data *data,
 	kuid_t uid = cifs_sb->ctx->linux_uid;
 	kgid_t gid = cifs_sb->ctx->linux_gid;
 	struct smb2_file_full_ea_info *ea;
+	bool ignore_missing_eas = false;
 	bool have_xattr_dev = false;
 	dev_t rdev = 0;
 	umode_t mode;
@@ -1174,8 +1175,18 @@ static bool wsl_to_fattr(struct cifs_open_info_data *data,
 	case IO_REPARSE_TAG_LX_BLK:
 		mode |= 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;
 
@@ -1213,7 +1224,7 @@ static bool wsl_to_fattr(struct cifs_open_info_data *data,
 out:
 	/* 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_uid = uid;
 	fattr->cf_gid = gid;
-- 
2.20.1


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v7.3 4/9] cifs: Validate presence of EA $LXMOD for WSL reparse points
  2026-10-26 22:25 [PATCH v7.3 0/9] Various changes Pali Rohár
                   ` (2 preceding siblings ...)
  2026-10-26 22:25 ` [PATCH v7.3 3/9] cifs: Fix validation of EAs for WSL reparse points Pali Rohár
@ 2026-10-26 22:25 ` Pali Rohár
  2026-10-26 22:25 ` [PATCH v7.3 5/9] cifs: Check if server supports EAs before trying to set it for WSL Pali Rohár
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Pali Rohár @ 2026-10-26 22:25 UTC (permalink / raw)
  To: Paulo Alcantara, Namjae Jeon; +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 e547510ca3c5..577cdf49c047 100644
--- a/fs/smb/client/reparse.c
+++ b/fs/smb/client/reparse.c
@@ -1153,6 +1153,7 @@ static bool wsl_to_fattr(struct cifs_open_info_data *data,
 	kgid_t gid = cifs_sb->ctx->linux_gid;
 	struct smb2_file_full_ea_info *ea;
 	bool ignore_missing_eas = false;
+	bool have_xattr_mode = false;
 	bool have_xattr_dev = false;
 	dev_t rdev = 0;
 	umode_t mode;
@@ -1216,6 +1217,7 @@ static bool wsl_to_fattr(struct cifs_open_info_data *data,
 			if (S_DT(mode) != S_DT(get_unaligned_le32(v)))
 				return false;
 			mode = get_unaligned_le32(v);
+			have_xattr_mode = true;
 		} else if (!strncmp(name, SMB2_WSL_XATTR_DEV, nlen)) {
 			rdev = reparse_mkdev(v);
 			have_xattr_dev = true;
@@ -1226,6 +1228,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_uid = uid;
 	fattr->cf_gid = gid;
 	fattr->cf_mode = mode;
-- 
2.20.1


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v7.3 5/9] cifs: Check if server supports EAs before trying to set it for WSL
  2026-10-26 22:25 [PATCH v7.3 0/9] Various changes Pali Rohár
                   ` (3 preceding siblings ...)
  2026-10-26 22:25 ` [PATCH v7.3 4/9] cifs: Validate presence of EA $LXMOD " Pali Rohár
@ 2026-10-26 22:25 ` Pali Rohár
  2026-10-26 22:25 ` [PATCH v7.3 6/9] cifs: Extend ->set_EA() callback to allow operate on reparse point Pali Rohár
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Pali Rohár @ 2026-10-26 22:25 UTC (permalink / raw)
  To: Paulo Alcantara, Namjae Jeon; +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 577cdf49c047..86eb56dbc81a 100644
--- a/fs/smb/client/reparse.c
+++ b/fs/smb/client/reparse.c
@@ -693,6 +693,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	[flat|nested] 13+ messages in thread

* [PATCH v7.3 6/9] cifs: Extend ->set_EA() callback to allow operate on reparse point
  2026-10-26 22:25 [PATCH v7.3 0/9] Various changes Pali Rohár
                   ` (4 preceding siblings ...)
  2026-10-26 22:25 ` [PATCH v7.3 5/9] cifs: Check if server supports EAs before trying to set it for WSL Pali Rohár
@ 2026-10-26 22:25 ` Pali Rohár
  2026-10-26 22:25 ` [PATCH v7.3 7/9] cifs: Create native Window socket file compatible also with WSL subsystem Pali Rohár
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Pali Rohár @ 2026-10-26 22:25 UTC (permalink / raw)
  To: Paulo Alcantara, Namjae Jeon; +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 5c5b76a9e9fe..d5faef5f3a73 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -570,6 +570,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 8bdd81af6d21..07c7d6fa97c4 100644
--- a/fs/smb/client/cifssmb.c
+++ b/fs/smb/client/cifssmb.c
@@ -3166,6 +3166,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),
@@ -6322,7 +6323,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)
 {
@@ -6338,6 +6340,22 @@ CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon,
 	int remap = cifs_remap(cifs_sb);
 	unsigned int total_len;
 
+	/*
+	 * 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 583ab4c4d773..6de334e222f0 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -1188,7 +1188,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)
 {
@@ -1290,7 +1291,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	[flat|nested] 13+ messages in thread

* [PATCH v7.3 7/9] cifs: Create native Window socket file compatible also with WSL subsystem
  2026-10-26 22:25 [PATCH v7.3 0/9] Various changes Pali Rohár
                   ` (5 preceding siblings ...)
  2026-10-26 22:25 ` [PATCH v7.3 6/9] cifs: Extend ->set_EA() callback to allow operate on reparse point Pali Rohár
@ 2026-10-26 22:25 ` Pali Rohár
  2026-10-26 22:25 ` [PATCH v7.3 8/9] cifs: Add support for parsing WSL symlinks in version 1 format Pali Rohár
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Pali Rohár @ 2026-10-26 22:25 UTC (permalink / raw)
  To: Paulo Alcantara, Namjae Jeon; +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 86eb56dbc81a..6a7e6d9ad375 100644
--- a/fs/smb/client/reparse.c
+++ b/fs/smb/client/reparse.c
@@ -409,7 +409,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),
@@ -423,6 +423,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;
 
@@ -433,6 +437,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;
 }
@@ -742,7 +768,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	[flat|nested] 13+ messages in thread

* [PATCH v7.3 8/9] cifs: Add support for parsing WSL symlinks in version 1 format
  2026-10-26 22:25 [PATCH v7.3 0/9] Various changes Pali Rohár
                   ` (6 preceding siblings ...)
  2026-10-26 22:25 ` [PATCH v7.3 7/9] cifs: Create native Window socket file compatible also with WSL subsystem Pali Rohár
@ 2026-10-26 22:25 ` Pali Rohár
  2026-10-27  1:37   ` Namjae Jeon
  2026-10-26 22:26 ` [PATCH v7.3 9/9] cifs: Add support for creating " Pali Rohár
  2026-10-27  1:42 ` [PATCH v7.3 0/9] Various changes Namjae Jeon
  9 siblings, 1 reply; 13+ messages in thread
From: Pali Rohár @ 2026-10-26 22:25 UTC (permalink / raw)
  To: Paulo Alcantara, Namjae Jeon; +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   | 122 ++++++++++++++++++++++++++++++++------
 fs/smb/common/fscc.h      |   5 +-
 4 files changed, 109 insertions(+), 20 deletions(-)

diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h
index 00168839c123..1202934d7b94 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 3620eb378deb..399b7f602a84 100644
--- a/fs/smb/client/inode.c
+++ b/fs/smb/client/inode.c
@@ -1182,6 +1182,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 6a7e6d9ad375..93c0c95d2202 100644
--- a/fs/smb/client/reparse.c
+++ b/fs/smb/client/reparse.c
@@ -1071,51 +1071,128 @@ 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;
+	u64 file_size;
+	__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.
+		 */
+
+		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 = file_size;
+		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);
+		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;
+
+		buf_type = CIFS_NO_BUFFER;
+		io_parms = (struct cifs_io_parms) {
+			.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 != file_size)
+			rc = -EIO;
 
-	if (version != 2) {
+		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 */
 
@@ -1123,14 +1200,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)
 {
@@ -1148,7 +1234,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 e46d3379b779..dee4fb6af16a 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	[flat|nested] 13+ messages in thread

* [PATCH v7.3 9/9] cifs: Add support for creating WSL symlinks in version 1 format
  2026-10-26 22:25 [PATCH v7.3 0/9] Various changes Pali Rohár
                   ` (7 preceding siblings ...)
  2026-10-26 22:25 ` [PATCH v7.3 8/9] cifs: Add support for parsing WSL symlinks in version 1 format Pali Rohár
@ 2026-10-26 22:26 ` Pali Rohár
  2026-10-27  1:38   ` Namjae Jeon
  2026-10-27  1:42 ` [PATCH v7.3 0/9] Various changes Namjae Jeon
  9 siblings, 1 reply; 13+ messages in thread
From: Pali Rohár @ 2026-10-26 22:26 UTC (permalink / raw)
  To: Paulo Alcantara, Namjae Jeon; +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 layout 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 d5faef5f3a73..d828ad0b466b 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 8d5d6aca742a..fd7203aa0917 100644
--- a/fs/smb/client/link.c
+++ b/fs/smb/client/link.c
@@ -604,7 +604,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 93c0c95d2202..4dcd3fa0d6a2 100644
--- a/fs/smb/client/reparse.c
+++ b/fs/smb/client/reparse.c
@@ -23,7 +23,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,
@@ -44,8 +44,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;
 	}
@@ -564,6 +566,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)
 {
@@ -599,15 +602,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:
@@ -708,7 +716,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;
@@ -717,6 +725,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;
 
 	/*
@@ -726,7 +740,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;
 
@@ -751,6 +765,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) {
+				.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 (!rc && 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);
+			iput(new);
+			new = ERR_PTR(rc);
+		}
+	}
 	if (!IS_ERR(new))
 		d_instantiate(dentry, new);
 	else
@@ -774,7 +827,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	[flat|nested] 13+ messages in thread

* Re: [PATCH v7.3 8/9] cifs: Add support for parsing WSL symlinks in version 1 format
  2026-10-26 22:25 ` [PATCH v7.3 8/9] cifs: Add support for parsing WSL symlinks in version 1 format Pali Rohár
@ 2026-10-27  1:37   ` Namjae Jeon
  0 siblings, 0 replies; 13+ messages in thread
From: Namjae Jeon @ 2026-10-27  1:37 UTC (permalink / raw)
  To: Pali Rohár; +Cc: Paulo Alcantara, linux-cifs, linux-kernel

>  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;
> +       u64 file_size;
> +       __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.
> +                */
> +
> +               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 = file_size;
> +               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);
> +               oparms.fid = &fid;
Should we pass initialized fid to ->open()? When SMB2/3 leasing is
enabled, the open path copies fid->lease_key into the outgoing CREATE
request, potentially disclosing 16 bytes of stale stack data to the
server ?
So, how about updating it like this ?

struct cifs_fid fid = {};
...
if (tcon->ses->server->ops->new_lease_key)
    tcon->ses->server->ops->new_lease_key(&fid);



> +               oplock = tcon->ses->server->oplocks ? REQ_OPLOCK : 0;
> +               rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, NULL);
> +               if (rc)
> +                       goto out;
> +
> +               buf_type = CIFS_NO_BUFFER;
> +               io_parms = (struct cifs_io_parms) {
> +                       .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 != file_size)
> +                       rc = -EIO;
>
> -       if (version != 2) {
> +               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 */
>
> @@ -1123,14 +1200,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)
>  {
> @@ -1148,7 +1234,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 e46d3379b779..dee4fb6af16a 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	[flat|nested] 13+ messages in thread

* Re: [PATCH v7.3 9/9] cifs: Add support for creating WSL symlinks in version 1 format
  2026-10-26 22:26 ` [PATCH v7.3 9/9] cifs: Add support for creating " Pali Rohár
@ 2026-10-27  1:38   ` Namjae Jeon
  0 siblings, 0 replies; 13+ messages in thread
From: Namjae Jeon @ 2026-10-27  1:38 UTC (permalink / raw)
  To: Pali Rohár; +Cc: Paulo Alcantara, linux-cifs, linux-kernel

> @@ -708,7 +716,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;
> @@ -717,6 +725,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;
>
>         /*
> @@ -726,7 +740,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;
>
> @@ -751,6 +765,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;
It is the same here. we need to initialize fid and call new_lease_key().


> +               oplock = tcon->ses->server->oplocks ? REQ_OPLOCK : 0;
> +               rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, NULL);

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v7.3 0/9] Various changes
  2026-10-26 22:25 [PATCH v7.3 0/9] Various changes Pali Rohár
                   ` (8 preceding siblings ...)
  2026-10-26 22:26 ` [PATCH v7.3 9/9] cifs: Add support for creating " Pali Rohár
@ 2026-10-27  1:42 ` Namjae Jeon
  9 siblings, 0 replies; 13+ messages in thread
From: Namjae Jeon @ 2026-10-27  1:42 UTC (permalink / raw)
  To: Pali Rohár; +Cc: Paulo Alcantara, linux-cifs, linux-kernel

On Tue, Oct 27, 2026 at 7:26 AM Pali Rohár <pali@kernel.org> wrote:
>
> Rebased changes on top of the v7.3.
> No functional change, just fixed 3 oneliners from Sashiko.
> Link to previous: https://lore.kernel.org/linux-cifs/20260825230622.24617-1-pali@kernel.org/
>
> Pali Rohár (9):
>   cifs: Remove code for querying FILE_INFO_STANDARD via
>     CIFSSMBQPathInfo()
>   cifs: Remove cifs_backup_query_path_info() and replace it by
>     cifs_query_path_info()
>   cifs: Fix validation of EAs for WSL reparse points
>   cifs: Validate presence of EA $LXMOD for WSL reparse points
>   cifs: Check if server supports EAs before trying to set it for WSL
>   cifs: Extend ->set_EA() callback to allow operate on reparse point
>   cifs: Create native Window socket file compatible also with WSL
>     subsystem
>   cifs: Add support for parsing WSL symlinks in version 1 format
>   cifs: Add support for creating WSL symlinks in version 1 format

I expected there would be 10th Patch to fix the problem below.

---------------------------------8<-----------------------------------------------------------------------
> > PATCH 8/9 comment 1 - unbound memory allocation based on symlink length
> > - this is valid, but unfortunately all the symlink parsing code would do
> > similar thing. And we have already more symlink types. Do we have some
> > limit how long could be symlink? Does it make sense to address only this
> > one issue and let all other untouched? I would rather propose to define
> > one common limit and fix this issue on all places.
> I agree that a common limit is preferable. cifs.ko already has
> REPARSE_SYM_PATH_MAX(4060) for native reparse symlinks.

Lets this for followup change.
------------------------------------------------------------------------------------------------------------

Thanks.
>
>  fs/smb/client/cifsglob.h   |  10 +-
>  fs/smb/client/cifsproto.h  |   1 +
>  fs/smb/client/cifssmb.c    |  43 ++++---
>  fs/smb/client/fs_context.c |  11 +-
>  fs/smb/client/fs_context.h |   3 +-
>  fs/smb/client/inode.c      |  98 +-------------
>  fs/smb/client/link.c       |   3 +-
>  fs/smb/client/reparse.c    | 257 ++++++++++++++++++++++++++++++++-----
>  fs/smb/client/smb1ops.c    |  11 +-
>  fs/smb/client/smb1proto.h  |   4 +-
>  fs/smb/client/smb2ops.c    |   6 +-
>  fs/smb/client/xattr.c      |   3 +-
>  fs/smb/common/fscc.h       |   5 +-
>  13 files changed, 287 insertions(+), 168 deletions(-)
>
> --
> 2.20.1
>

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2026-10-27  1:42 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-26 22:25 [PATCH v7.3 0/9] Various changes Pali Rohár
2026-10-26 22:25 ` [PATCH v7.3 1/9] cifs: Remove code for querying FILE_INFO_STANDARD via CIFSSMBQPathInfo() Pali Rohár
2026-10-26 22:25 ` [PATCH v7.3 2/9] cifs: Remove cifs_backup_query_path_info() and replace it by cifs_query_path_info() Pali Rohár
2026-10-26 22:25 ` [PATCH v7.3 3/9] cifs: Fix validation of EAs for WSL reparse points Pali Rohár
2026-10-26 22:25 ` [PATCH v7.3 4/9] cifs: Validate presence of EA $LXMOD " Pali Rohár
2026-10-26 22:25 ` [PATCH v7.3 5/9] cifs: Check if server supports EAs before trying to set it for WSL Pali Rohár
2026-10-26 22:25 ` [PATCH v7.3 6/9] cifs: Extend ->set_EA() callback to allow operate on reparse point Pali Rohár
2026-10-26 22:25 ` [PATCH v7.3 7/9] cifs: Create native Window socket file compatible also with WSL subsystem Pali Rohár
2026-10-26 22:25 ` [PATCH v7.3 8/9] cifs: Add support for parsing WSL symlinks in version 1 format Pali Rohár
2026-10-27  1:37   ` Namjae Jeon
2026-10-26 22:26 ` [PATCH v7.3 9/9] cifs: Add support for creating " Pali Rohár
2026-10-27  1:38   ` Namjae Jeon
2026-10-27  1:42 ` [PATCH v7.3 0/9] Various changes Namjae Jeon

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®