Re: [PATCH] cifs: Fix support for WSL-style symlinks

From: Steve French
Date: Tue Apr 08 2025 - 19:42:16 EST


Merged into cifs-2.6.git for-next pending additional review

On Tue, Apr 8, 2025 at 5:34 PM Pali Rohár <pali@xxxxxxxxxx> wrote:
>
> MS-FSCC in section 2.1.2.7 LX SYMLINK REPARSE_DATA_BUFFER now contains
> documentation about WSL symlink reparse point buffers.
>
> https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/68337353-9153-4ee1-ac6b-419839c3b7ad
>
> Fix the struct reparse_wsl_symlink_data_buffer to reflect buffer fields
> according to the MS-FSCC documentation.
>
> Fix the Linux SMB client to correctly fill the WSL symlink reparse point
> buffer when creaing new WSL-style symlink. There was a mistake during
> filling the data part of the reparse point buffer. It should starts with
> bytes "\x02\x00\x00\x00" (which represents version 2) but this constant was
> written as number 0x02000000 encoded in little endian, which resulted bytes
> "\x00\x00\x00\x02". This change is fixing this mistake.
>
> Fixes: 4e2043be5c14 ("cifs: Add support for creating WSL-style symlinks")
> Signed-off-by: Pali Rohár <pali@xxxxxxxxxx>
> ---
> fs/smb/client/reparse.c | 25 ++++++++++++++++---------
> fs/smb/common/smb2pdu.h | 6 +++---
> 2 files changed, 19 insertions(+), 12 deletions(-)
>
> diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c
> index 7cefe903edb5..dbd3dd9b678f 100644
> --- a/fs/smb/client/reparse.c
> +++ b/fs/smb/client/reparse.c
> @@ -520,12 +520,12 @@ static int wsl_set_reparse_buf(struct reparse_data_buffer **buf,
> kfree(symname_utf16);
> return -ENOMEM;
> }
> - /* Flag 0x02000000 is unknown, but all wsl symlinks have this value */
> - symlink_buf->Flags = cpu_to_le32(0x02000000);
> - /* PathBuffer is in UTF-8 but without trailing null-term byte */
> + /* 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 */
> symname_utf8_len = utf16s_to_utf8s((wchar_t *)symname_utf16, symname_utf16_len/2,
> UTF16_LITTLE_ENDIAN,
> - symlink_buf->PathBuffer,
> + 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;
> @@ -995,29 +995,36 @@ static int parse_reparse_wsl_symlink(struct reparse_wsl_symlink_data_buffer *buf
> 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;
> __le16 *symname_utf16;
> int symname_utf16_len;
>
> - if (len <= sizeof(buf->Flags)) {
> + if (len <= data_offset) {
> cifs_dbg(VFS, "srv returned malformed wsl symlink buffer\n");
> return -EIO;
> }
>
> - /* PathBuffer is in UTF-8 but without trailing null-term byte */
> - symname_utf8_len = len - sizeof(buf->Flags);
> + /* MS-FSCC 2.1.2.7 defines layout of the Target field only for Version 2. */
> + if (le32_to_cpu(buf->Version) != 2) {
> + cifs_dbg(VFS, "srv returned unsupported wsl symlink version %u\n", le32_to_cpu(buf->Version));
> + return -EIO;
> + }
> +
> + /* Target for Version 2 is in UTF-8 but without trailing null-term byte */
> + symname_utf8_len = len - data_offset;
> /*
> * Check that buffer does not contain null byte
> * because Linux cannot process symlink with null byte.
> */
> - if (strnlen(buf->PathBuffer, symname_utf8_len) != symname_utf8_len) {
> + if (strnlen(buf->Target, symname_utf8_len) != symname_utf8_len) {
> cifs_dbg(VFS, "srv returned null byte in wsl symlink target location\n");
> return -EIO;
> }
> symname_utf16 = kzalloc(symname_utf8_len * 2, GFP_KERNEL);
> if (!symname_utf16)
> return -ENOMEM;
> - symname_utf16_len = utf8s_to_utf16s(buf->PathBuffer, symname_utf8_len,
> + symname_utf16_len = utf8s_to_utf16s(buf->Target, symname_utf8_len,
> UTF16_LITTLE_ENDIAN,
> (wchar_t *) symname_utf16, symname_utf8_len * 2);
> if (symname_utf16_len < 0) {
> diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h
> index 764dca80c15c..f79a5165a7cc 100644
> --- a/fs/smb/common/smb2pdu.h
> +++ b/fs/smb/common/smb2pdu.h
> @@ -1567,13 +1567,13 @@ struct reparse_nfs_data_buffer {
> __u8 DataBuffer[];
> } __packed;
>
> -/* For IO_REPARSE_TAG_LX_SYMLINK */
> +/* For IO_REPARSE_TAG_LX_SYMLINK - see MS-FSCC 2.1.2.7 */
> struct reparse_wsl_symlink_data_buffer {
> __le32 ReparseTag;
> __le16 ReparseDataLength;
> __u16 Reserved;
> - __le32 Flags;
> - __u8 PathBuffer[]; /* Variable Length UTF-8 string without nul-term */
> + __le32 Version; /* Always 2 */
> + __u8 Target[]; /* Variable Length UTF-8 string without nul-term */
> } __packed;
>
> struct validate_negotiate_info_req {
> --
> 2.20.1
>
>


--
Thanks,

Steve