Skip to content

Commit 4e2043b

Browse files
paliSteve French
authored andcommitted
cifs: Add support for creating WSL-style symlinks
This change implements support for creating new symlink in WSL-style by Linux cifs client when -o reparse=wsl mount option is specified. WSL-style symlink uses reparse point with tag IO_REPARSE_TAG_LX_SYMLINK and symlink target location is stored in reparse buffer in UTF-8 encoding prefixed by 32-bit flags. Flags bits are unknown, but it was observed that WSL always sets flags to value 0x02000000. Do same in Linux cifs client. New symlinks would be created in WSL-style only in case the mount option -o reparse=wsl is specified, which is not by default. So default CIFS mounts are not affected by this change. Signed-off-by: Pali Rohár <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent eea5119 commit 4e2043b

File tree

1 file changed

+53
-12
lines changed

1 file changed

+53
-12
lines changed

fs/smb/client/reparse.c

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -505,27 +505,63 @@ static int mknod_nfs(unsigned int xid, struct inode *inode,
505505
return rc;
506506
}
507507

508-
static int wsl_set_reparse_buf(struct reparse_data_buffer *buf,
509-
mode_t mode, struct kvec *iov)
508+
static int wsl_set_reparse_buf(struct reparse_data_buffer **buf,
509+
mode_t mode, const char *symname,
510+
struct cifs_sb_info *cifs_sb,
511+
struct kvec *iov)
510512
{
513+
struct reparse_wsl_symlink_data_buffer *symlink_buf;
514+
__le16 *symname_utf16;
515+
int symname_utf16_len;
516+
int symname_utf8_maxlen;
517+
int symname_utf8_len;
518+
size_t buf_len;
511519
u32 tag;
512520

513521
switch ((tag = reparse_mode_wsl_tag(mode))) {
514522
case IO_REPARSE_TAG_LX_BLK:
515523
case IO_REPARSE_TAG_LX_CHR:
516524
case IO_REPARSE_TAG_LX_FIFO:
517525
case IO_REPARSE_TAG_AF_UNIX:
526+
buf_len = sizeof(struct reparse_data_buffer);
527+
*buf = kzalloc(buf_len, GFP_KERNEL);
528+
if (!*buf)
529+
return -ENOMEM;
530+
break;
531+
case IO_REPARSE_TAG_LX_SYMLINK:
532+
symname_utf16 = cifs_strndup_to_utf16(symname, strlen(symname),
533+
&symname_utf16_len,
534+
cifs_sb->local_nls,
535+
NO_MAP_UNI_RSVD);
536+
if (!symname_utf16)
537+
return -ENOMEM;
538+
symname_utf8_maxlen = symname_utf16_len/2*3;
539+
symlink_buf = kzalloc(sizeof(struct reparse_wsl_symlink_data_buffer) +
540+
symname_utf8_maxlen, GFP_KERNEL);
541+
if (!symlink_buf) {
542+
kfree(symname_utf16);
543+
return -ENOMEM;
544+
}
545+
/* Flag 0x02000000 is unknown, but all wsl symlinks have this value */
546+
symlink_buf->Flags = cpu_to_le32(0x02000000);
547+
/* PathBuffer is in UTF-8 but without trailing null-term byte */
548+
symname_utf8_len = utf16s_to_utf8s((wchar_t *)symname_utf16, symname_utf16_len/2,
549+
UTF16_LITTLE_ENDIAN,
550+
symlink_buf->PathBuffer,
551+
symname_utf8_maxlen);
552+
*buf = (struct reparse_data_buffer *)symlink_buf;
553+
buf_len = sizeof(struct reparse_wsl_symlink_data_buffer) + symname_utf8_len;
554+
kfree(symname_utf16);
518555
break;
519-
case IO_REPARSE_TAG_LX_SYMLINK: /* TODO: add support for WSL symlinks */
520556
default:
521557
return -EOPNOTSUPP;
522558
}
523559

524-
buf->ReparseTag = cpu_to_le32(tag);
525-
buf->Reserved = 0;
526-
buf->ReparseDataLength = 0;
527-
iov->iov_base = buf;
528-
iov->iov_len = sizeof(*buf);
560+
(*buf)->ReparseTag = cpu_to_le32(tag);
561+
(*buf)->Reserved = 0;
562+
(*buf)->ReparseDataLength = cpu_to_le16(buf_len - sizeof(struct reparse_data_buffer));
563+
iov->iov_base = *buf;
564+
iov->iov_len = buf_len;
529565
return 0;
530566
}
531567

@@ -617,25 +653,29 @@ static int mknod_wsl(unsigned int xid, struct inode *inode,
617653
const char *full_path, umode_t mode, dev_t dev,
618654
const char *symname)
619655
{
656+
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
620657
struct cifs_open_info_data data;
621-
struct reparse_data_buffer buf;
658+
struct reparse_data_buffer *buf;
622659
struct smb2_create_ea_ctx *cc;
623660
struct inode *new;
624661
unsigned int len;
625662
struct kvec reparse_iov, xattr_iov;
626663
int rc;
627664

628-
rc = wsl_set_reparse_buf(&buf, mode, &reparse_iov);
665+
rc = wsl_set_reparse_buf(&buf, mode, symname, cifs_sb, &reparse_iov);
629666
if (rc)
630667
return rc;
631668

632669
rc = wsl_set_xattrs(inode, mode, dev, &xattr_iov);
633-
if (rc)
670+
if (rc) {
671+
kfree(buf);
634672
return rc;
673+
}
635674

636675
data = (struct cifs_open_info_data) {
637676
.reparse_point = true,
638-
.reparse = { .tag = le32_to_cpu(buf.ReparseTag), .buf = &buf, },
677+
.reparse = { .tag = le32_to_cpu(buf->ReparseTag), .buf = buf, },
678+
.symlink_target = kstrdup(symname, GFP_KERNEL),
639679
};
640680

641681
cc = xattr_iov.iov_base;
@@ -652,6 +692,7 @@ static int mknod_wsl(unsigned int xid, struct inode *inode,
652692
rc = PTR_ERR(new);
653693
cifs_free_open_info(&data);
654694
kfree(xattr_iov.iov_base);
695+
kfree(buf);
655696
return rc;
656697
}
657698

0 commit comments

Comments
 (0)