]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
cifs: Fix support for WSL-style symlinks
authorPali Rohár <pali@kernel.org>
Tue, 8 Apr 2025 22:29:49 +0000 (00:29 +0200)
committerSteve French <stfrench@microsoft.com>
Tue, 8 Apr 2025 23:38:03 +0000 (18:38 -0500)
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@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/client/reparse.c
fs/smb/common/smb2pdu.h

index f85dd40f34aff7b58363126526d9b7b83bcbed9b..1416b9ffaca12466508a60a4a5aef84a53b73283 100644 (file)
@@ -542,12 +542,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;
@@ -1016,29 +1016,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) {
index 764dca80c15cb1af1e500845ae36087ebd011bce..f79a5165a7cc6a22b4aecedca75ecf6e86751aaf 100644 (file)
@@ -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 {