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 <pali@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>

authored by Pali Rohár and committed by Steve French 4e2043be eea5119f

+53 -12
+53 -12
fs/smb/client/reparse.c
··· 505 return rc; 506 } 507 508 - static int wsl_set_reparse_buf(struct reparse_data_buffer *buf, 509 - mode_t mode, struct kvec *iov) 510 { 511 u32 tag; 512 513 switch ((tag = reparse_mode_wsl_tag(mode))) { ··· 523 case IO_REPARSE_TAG_LX_CHR: 524 case IO_REPARSE_TAG_LX_FIFO: 525 case IO_REPARSE_TAG_AF_UNIX: 526 break; 527 - case IO_REPARSE_TAG_LX_SYMLINK: /* TODO: add support for WSL symlinks */ 528 default: 529 return -EOPNOTSUPP; 530 } 531 532 - buf->ReparseTag = cpu_to_le32(tag); 533 - buf->Reserved = 0; 534 - buf->ReparseDataLength = 0; 535 - iov->iov_base = buf; 536 - iov->iov_len = sizeof(*buf); 537 return 0; 538 } 539 ··· 653 const char *full_path, umode_t mode, dev_t dev, 654 const char *symname) 655 { 656 struct cifs_open_info_data data; 657 - struct reparse_data_buffer buf; 658 struct smb2_create_ea_ctx *cc; 659 struct inode *new; 660 unsigned int len; 661 struct kvec reparse_iov, xattr_iov; 662 int rc; 663 664 - rc = wsl_set_reparse_buf(&buf, mode, &reparse_iov); 665 if (rc) 666 return rc; 667 668 rc = wsl_set_xattrs(inode, mode, dev, &xattr_iov); 669 - if (rc) 670 return rc; 671 672 data = (struct cifs_open_info_data) { 673 .reparse_point = true, 674 - .reparse = { .tag = le32_to_cpu(buf.ReparseTag), .buf = &buf, }, 675 }; 676 677 cc = xattr_iov.iov_base; ··· 692 rc = PTR_ERR(new); 693 cifs_free_open_info(&data); 694 kfree(xattr_iov.iov_base); 695 return rc; 696 } 697
··· 505 return rc; 506 } 507 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) 512 { 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; 519 u32 tag; 520 521 switch ((tag = reparse_mode_wsl_tag(mode))) { ··· 515 case IO_REPARSE_TAG_LX_CHR: 516 case IO_REPARSE_TAG_LX_FIFO: 517 case IO_REPARSE_TAG_AF_UNIX: 518 + buf_len = sizeof(struct reparse_data_buffer); 519 + *buf = kzalloc(buf_len, GFP_KERNEL); 520 + if (!*buf) 521 + return -ENOMEM; 522 break; 523 + case IO_REPARSE_TAG_LX_SYMLINK: 524 + symname_utf16 = cifs_strndup_to_utf16(symname, strlen(symname), 525 + &symname_utf16_len, 526 + cifs_sb->local_nls, 527 + NO_MAP_UNI_RSVD); 528 + if (!symname_utf16) 529 + return -ENOMEM; 530 + symname_utf8_maxlen = symname_utf16_len/2*3; 531 + symlink_buf = kzalloc(sizeof(struct reparse_wsl_symlink_data_buffer) + 532 + symname_utf8_maxlen, GFP_KERNEL); 533 + if (!symlink_buf) { 534 + kfree(symname_utf16); 535 + return -ENOMEM; 536 + } 537 + /* Flag 0x02000000 is unknown, but all wsl symlinks have this value */ 538 + symlink_buf->Flags = cpu_to_le32(0x02000000); 539 + /* PathBuffer is in UTF-8 but without trailing null-term byte */ 540 + symname_utf8_len = utf16s_to_utf8s((wchar_t *)symname_utf16, symname_utf16_len/2, 541 + UTF16_LITTLE_ENDIAN, 542 + symlink_buf->PathBuffer, 543 + symname_utf8_maxlen); 544 + *buf = (struct reparse_data_buffer *)symlink_buf; 545 + buf_len = sizeof(struct reparse_wsl_symlink_data_buffer) + symname_utf8_len; 546 + kfree(symname_utf16); 547 + break; 548 default: 549 return -EOPNOTSUPP; 550 } 551 552 + (*buf)->ReparseTag = cpu_to_le32(tag); 553 + (*buf)->Reserved = 0; 554 + (*buf)->ReparseDataLength = cpu_to_le16(buf_len - sizeof(struct reparse_data_buffer)); 555 + iov->iov_base = *buf; 556 + iov->iov_len = buf_len; 557 return 0; 558 } 559 ··· 617 const char *full_path, umode_t mode, dev_t dev, 618 const char *symname) 619 { 620 + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); 621 struct cifs_open_info_data data; 622 + struct reparse_data_buffer *buf; 623 struct smb2_create_ea_ctx *cc; 624 struct inode *new; 625 unsigned int len; 626 struct kvec reparse_iov, xattr_iov; 627 int rc; 628 629 + rc = wsl_set_reparse_buf(&buf, mode, symname, cifs_sb, &reparse_iov); 630 if (rc) 631 return rc; 632 633 rc = wsl_set_xattrs(inode, mode, dev, &xattr_iov); 634 + if (rc) { 635 + kfree(buf); 636 return rc; 637 + } 638 639 data = (struct cifs_open_info_data) { 640 .reparse_point = true, 641 + .reparse = { .tag = le32_to_cpu(buf->ReparseTag), .buf = buf, }, 642 + .symlink_target = kstrdup(symname, GFP_KERNEL), 643 }; 644 645 cc = xattr_iov.iov_base; ··· 652 rc = PTR_ERR(new); 653 cifs_free_open_info(&data); 654 kfree(xattr_iov.iov_base); 655 + kfree(buf); 656 return rc; 657 } 658