Skip to content

Commit 06a7adf

Browse files
paliSteve French
authored andcommitted
cifs: Add support for parsing WSL-style symlinks
Linux CIFS client currently does not implement readlink() for WSL-style symlinks. It is only able to detect that file is of WSL-style symlink, but is not able to read target symlink location. Add this missing functionality and implement support for parsing content of WSL-style symlink. The important note is that symlink target location stored for WSL symlink reparse point (IO_REPARSE_TAG_LX_SYMLINK) is in UTF-8 encoding instead of UTF-16 (which is used in whole SMB protocol and also in all other symlink styles). So for proper locale/cp support it is needed to do conversion from UTF-8 to local_nls. Signed-off-by: Pali Rohár <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent dd26bc0 commit 06a7adf

File tree

2 files changed

+58
-0
lines changed

2 files changed

+58
-0
lines changed

fs/smb/client/reparse.c

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,52 @@ static int parse_reparse_symlink(struct reparse_symlink_data_buffer *sym,
647647
cifs_sb);
648648
}
649649

650+
static int parse_reparse_wsl_symlink(struct reparse_wsl_symlink_data_buffer *buf,
651+
struct cifs_sb_info *cifs_sb,
652+
struct cifs_open_info_data *data)
653+
{
654+
int len = le16_to_cpu(buf->ReparseDataLength);
655+
int symname_utf8_len;
656+
__le16 *symname_utf16;
657+
int symname_utf16_len;
658+
659+
if (len <= sizeof(buf->Flags)) {
660+
cifs_dbg(VFS, "srv returned malformed wsl symlink buffer\n");
661+
return -EIO;
662+
}
663+
664+
/* PathBuffer is in UTF-8 but without trailing null-term byte */
665+
symname_utf8_len = len - sizeof(buf->Flags);
666+
/*
667+
* Check that buffer does not contain null byte
668+
* because Linux cannot process symlink with null byte.
669+
*/
670+
if (strnlen(buf->PathBuffer, symname_utf8_len) != symname_utf8_len) {
671+
cifs_dbg(VFS, "srv returned null byte in wsl symlink target location\n");
672+
return -EIO;
673+
}
674+
symname_utf16 = kzalloc(symname_utf8_len * 2, GFP_KERNEL);
675+
if (!symname_utf16)
676+
return -ENOMEM;
677+
symname_utf16_len = utf8s_to_utf16s(buf->PathBuffer, symname_utf8_len,
678+
UTF16_LITTLE_ENDIAN,
679+
symname_utf16, symname_utf8_len * 2);
680+
if (symname_utf16_len < 0) {
681+
kfree(symname_utf16);
682+
return symname_utf16_len;
683+
}
684+
symname_utf16_len *= 2; /* utf8s_to_utf16s() returns number of u16 items, not byte length */
685+
686+
data->symlink_target = cifs_strndup_from_utf16((u8 *)symname_utf16,
687+
symname_utf16_len, true,
688+
cifs_sb->local_nls);
689+
kfree(symname_utf16);
690+
if (!data->symlink_target)
691+
return -ENOMEM;
692+
693+
return 0;
694+
}
695+
650696
int parse_reparse_point(struct reparse_data_buffer *buf,
651697
u32 plen, struct cifs_sb_info *cifs_sb,
652698
const char *full_path,
@@ -666,6 +712,9 @@ int parse_reparse_point(struct reparse_data_buffer *buf,
666712
(struct reparse_symlink_data_buffer *)buf,
667713
plen, unicode, cifs_sb, full_path, data);
668714
case IO_REPARSE_TAG_LX_SYMLINK:
715+
return parse_reparse_wsl_symlink(
716+
(struct reparse_wsl_symlink_data_buffer *)buf,
717+
cifs_sb, data);
669718
case IO_REPARSE_TAG_AF_UNIX:
670719
case IO_REPARSE_TAG_LX_FIFO:
671720
case IO_REPARSE_TAG_LX_CHR:

fs/smb/common/smb2pdu.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1552,6 +1552,15 @@ struct reparse_symlink_data_buffer {
15521552

15531553
/* See MS-FSCC 2.1.2.6 and cifspdu.h for struct reparse_posix_data */
15541554

1555+
/* For IO_REPARSE_TAG_LX_SYMLINK */
1556+
struct reparse_wsl_symlink_data_buffer {
1557+
__le32 ReparseTag;
1558+
__le16 ReparseDataLength;
1559+
__u16 Reserved;
1560+
__le32 Flags;
1561+
__u8 PathBuffer[]; /* Variable Length UTF-8 string without nul-term */
1562+
} __packed;
1563+
15551564
struct validate_negotiate_info_req {
15561565
__le32 Capabilities;
15571566
__u8 Guid[SMB2_CLIENT_GUID_SIZE];

0 commit comments

Comments
 (0)