Re: [PATCH 6/7] cifs: Add support for creating WSL-style symlinks

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Sunday 06 October 2024 12:00:45 Pali Rohár wrote:
> 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@xxxxxxxxxx>
> ---
>  fs/smb/client/reparse.c | 65 +++++++++++++++++++++++++++++++++--------
>  1 file changed, 53 insertions(+), 12 deletions(-)
> 
> diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c
> index 402eb568f466..6606c40487ae 100644
> --- a/fs/smb/client/reparse.c
> +++ b/fs/smb/client/reparse.c
> @@ -506,9 +506,17 @@ static int mknod_nfs(unsigned int xid, struct inode *inode,
>  	return rc;
>  }
>  
> -static int wsl_set_reparse_buf(struct reparse_data_buffer *buf,
> -			       mode_t mode, struct kvec *iov)
> +static int wsl_set_reparse_buf(struct reparse_data_buffer **buf,
> +			       mode_t mode, const char *symname,
> +			       struct cifs_sb_info *cifs_sb,
> +			       struct kvec *iov)
>  {
> +	struct reparse_wsl_symlink_data_buffer *symlink_buf;
> +	__le16 *symname_utf16;
> +	int symname_utf16_len;
> +	int symname_utf8_maxlen;
> +	int symname_utf8_len;
> +	size_t buf_len;
>  	u32 tag;
>  
>  	switch ((tag = reparse_mode_wsl_tag(mode))) {
> @@ -516,17 +524,45 @@ static int wsl_set_reparse_buf(struct reparse_data_buffer *buf,
>  	case IO_REPARSE_TAG_LX_CHR:
>  	case IO_REPARSE_TAG_LX_FIFO:
>  	case IO_REPARSE_TAG_AF_UNIX:
> +		buf_len = sizeof(struct reparse_data_buffer);
> +		*buf = kzalloc(buf_len, GFP_KERNEL);
> +		if (!*buf)
> +			return -ENOMEM;
> +		break;
> +	case IO_REPARSE_TAG_LX_SYMLINK:
> +		symname_utf16 = cifs_strndup_to_utf16(symname, strlen(symname),
> +						      &symname_utf16_len,
> +						      cifs_sb->local_nls,
> +						      NO_MAP_UNI_RSVD);
> +		if (!symname_utf16)
> +			return -ENOMEM;
> +		symname_utf8_maxlen = symname_utf16_len/2*3;
> +		symlink_buf = kzalloc(sizeof(struct reparse_wsl_symlink_data_buffer) +
> +				      symname_utf8_maxlen, GFP_KERNEL);
> +		if (!symlink_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 */
> +		symname_utf8_len = utf16s_to_utf8s(symname_utf16, symname_utf16_len/2,
> +						   UTF16_LITTLE_ENDIAN,
> +						   symlink_buf->PathBuffer,
> +						   symname_utf8_maxlen);
> +		*buf = (struct reparse_data_buffer *)symlink_buf;
> +		buf_len = sizeof(struct reparse_wsl_symlink_data_buffer) + symname_utf8_len;
> +		kfree(symname_utf16);
>  		break;
> -	case IO_REPARSE_TAG_LX_SYMLINK: /* TODO: add support for WSL symlinks */
>  	default:
>  		return -EOPNOTSUPP;
>  	}
>  
> -	buf->ReparseTag = cpu_to_le32(tag);
> -	buf->Reserved = 0;
> -	buf->ReparseDataLength = 0;
> -	iov->iov_base = buf;
> -	iov->iov_len = sizeof(*buf);
> +	(*buf)->ReparseTag = cpu_to_le32(tag);
> +	(*buf)->Reserved = 0;
> +	(*buf)->ReparseDataLength = buf_len - sizeof(struct reparse_data_buffer);

ReparseDataLength is in little endian, so it should be:

  (*buf)->ReparseDataLength = cpu_to_le16(buf_len - sizeof(struct reparse_data_buffer));

> +	iov->iov_base = *buf;
> +	iov->iov_len = buf_len;
>  	return 0;
>  }
>  
> @@ -618,25 +654,29 @@ static int mknod_wsl(unsigned int xid, struct inode *inode,
>  		     const char *full_path, umode_t mode, dev_t dev,
>  		     const char *symname)
>  {
> +	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
>  	struct cifs_open_info_data data;
> -	struct reparse_data_buffer buf;
> +	struct reparse_data_buffer *buf;
>  	struct smb2_create_ea_ctx *cc;
>  	struct inode *new;
>  	unsigned int len;
>  	struct kvec reparse_iov, xattr_iov;
>  	int rc;
>  
> -	rc = wsl_set_reparse_buf(&buf, mode, &reparse_iov);
> +	rc = wsl_set_reparse_buf(&buf, mode, symname, cifs_sb, &reparse_iov);
>  	if (rc)
>  		return rc;
>  
>  	rc = wsl_set_xattrs(inode, mode, dev, &xattr_iov);
> -	if (rc)
> +	if (rc) {
> +		kfree(buf);
>  		return rc;
> +	}
>  
>  	data = (struct cifs_open_info_data) {
>  		.reparse_point = true,
> -		.reparse = { .tag = le32_to_cpu(buf.ReparseTag), .buf = &buf, },
> +		.reparse = { .tag = le32_to_cpu(buf->ReparseTag), .buf = buf, },
> +		.symlink_target = kstrdup(symname, GFP_KERNEL),
>  	};
>  
>  	cc = xattr_iov.iov_base;
> @@ -653,6 +693,7 @@ static int mknod_wsl(unsigned int xid, struct inode *inode,
>  		rc = PTR_ERR(new);
>  	cifs_free_open_info(&data);
>  	kfree(xattr_iov.iov_base);
> +	kfree(buf);
>  	return rc;
>  }
>  
> -- 
> 2.20.1
> 




[Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux