[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <20241209180058.7w2ytnmgg34xnsci@pali>
Date: Mon, 9 Dec 2024 19:00:58 +0100
From: Pali Rohár <pali@...nel.org>
To: Steve French <sfrench@...ba.org>, Paulo Alcantara <pc@...guebit.com>,
Ronnie Sahlberg <ronniesahlberg@...il.com>
Cc: linux-cifs@...r.kernel.org, linux-kernel@...r.kernel.org
Subject: Re: [PATCH v2 4/7] cifs: Add support for creating NFS-style symlinks
On Saturday 12 October 2024 10:56:01 Pali Rohár wrote:
> CIFS client is currently able to parse NFS-style symlinks, but is not able
> to create them. This functionality is useful when the mounted SMB share is
> used also by Windows NFS server (on Windows Server 2012 or new). It allows
> interop of symlinks between SMB share mounted by Linux CIFS client and same
> export from Windows NFS server mounted by some NFS client.
>
> New symlinks would be created in NFS-style only in case the mount option
> -o reparse=nfs is specified, which is not by default. So default CIFS
> mounts are not affected by this change.
>
> Signed-off-by: Pali Rohár <pali@...nel.org>
> ---
> fs/smb/client/reparse.c | 47 ++++++++++++++++++++++++++++++++++-------
> 1 file changed, 39 insertions(+), 8 deletions(-)
>
> diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c
> index 57320a4c4d79..cd12704cae0c 100644
> --- a/fs/smb/client/reparse.c
> +++ b/fs/smb/client/reparse.c
> @@ -406,6 +406,8 @@ static int create_native_socket(const unsigned int xid, struct inode *inode,
>
> static int nfs_set_reparse_buf(struct reparse_posix_data *buf,
> mode_t mode, dev_t dev,
> + __le16 *symname_utf16,
> + int symname_utf16_len,
> struct kvec *iov)
> {
> u64 type;
> @@ -416,13 +418,18 @@ static int nfs_set_reparse_buf(struct reparse_posix_data *buf,
> switch ((type = reparse_mode_nfs_type(mode))) {
> case NFS_SPECFILE_BLK:
> case NFS_SPECFILE_CHR:
> - dlen = sizeof(__le64);
> + dlen = 2 * sizeof(__le32);
> + ((__le32 *)buf->DataBuffer)[0] = MAJOR(dev);
> + ((__le32 *)buf->DataBuffer)[1] = MINOR(dev);
> + break;
> + case NFS_SPECFILE_LNK:
> + dlen = symname_utf16_len;
> + memcpy(buf->DataBuffer, symname_utf16, symname_utf16_len);
> break;
> case NFS_SPECFILE_FIFO:
> case NFS_SPECFILE_SOCK:
> dlen = 0;
> break;
> - case NFS_SPECFILE_LNK: /* TODO: add support for NFS symlinks */
> default:
> return -EOPNOTSUPP;
> }
> @@ -432,8 +439,6 @@ static int nfs_set_reparse_buf(struct reparse_posix_data *buf,
> buf->InodeType = cpu_to_le64(type);
> buf->ReparseDataLength = cpu_to_le16(len + dlen -
> sizeof(struct reparse_data_buffer));
> - *(__le64 *)buf->DataBuffer = cpu_to_le64(((u64)MINOR(dev) << 32) |
> - MAJOR(dev));
> iov->iov_base = buf;
> iov->iov_len = len + dlen;
> return 0;
> @@ -444,21 +449,42 @@ static int mknod_nfs(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_posix_data *p;
> + struct reparse_posix_data *p = NULL;
> + __le16 *symname_utf16 = NULL;
> + int symname_utf16_len = 0;
> struct inode *new;
> struct kvec iov;
> __u8 buf[sizeof(*p) + sizeof(__le64)];
> int rc;
>
> - p = (struct reparse_posix_data *)buf;
> - rc = nfs_set_reparse_buf(p, mode, dev, &iov);
> + if (S_ISLNK(mode)) {
> + symname_utf16 = cifs_strndup_to_utf16(symname, strlen(symname),
> + &symname_utf16_len,
> + cifs_sb->local_nls,
> + NO_MAP_UNI_RSVD);
> + if (!symname_utf16) {
> + rc = -ENOMEM;
> + goto out;
> + }
> + symname_utf16_len -= 2; /* symlink is without trailing wide-nul */
> + p = kzalloc(sizeof(*p) + symname_utf16_len, GFP_KERNEL);
> + if (!p) {
> + rc = -ENOMEM;
> + goto out;
> + }
> + } else {
> + p = (struct reparse_posix_data *)buf;
> + }
> + rc = nfs_set_reparse_buf(p, mode, dev, symname_utf16, symname_utf16_len, &iov);
> if (rc)
> - return rc;
> + goto out;
>
> data = (struct cifs_open_info_data) {
> .reparse_point = true,
> .reparse = { .tag = IO_REPARSE_TAG_NFS, .posix = p, },
> + .symlink_target = kstrdup(symname, GFP_KERNEL),
> };
>
> new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
> @@ -468,6 +494,11 @@ static int mknod_nfs(unsigned int xid, struct inode *inode,
> else
> rc = PTR_ERR(new);
> cifs_free_open_info(&data);
> +out:
> + if (S_ISLNK(mode)) {
> + kfree(symname_utf16);
> + kfree(p);
> + }
> return rc;
> }
>
> --
> 2.20.1
>
This change also needs fixup for big endian systems:
fixup! cifs: Add support for creating NFS-style symlinks
diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c
index af08e5918adb..c2569347d746 100644
--- a/fs/smb/client/reparse.c
+++ b/fs/smb/client/reparse.c
@@ -419,8 +419,8 @@ static int nfs_set_reparse_buf(struct reparse_nfs_data_buffer *buf,
case NFS_SPECFILE_BLK:
case NFS_SPECFILE_CHR:
dlen = 2 * sizeof(__le32);
- ((__le32 *)buf->DataBuffer)[0] = MAJOR(dev);
- ((__le32 *)buf->DataBuffer)[1] = MINOR(dev);
+ ((__le32 *)buf->DataBuffer)[0] = cpu_to_le32(MAJOR(dev));
+ ((__le32 *)buf->DataBuffer)[1] = cpu_to_le32(MINOR(dev));
break;
case NFS_SPECFILE_LNK:
dlen = symname_utf16_len;
Powered by blists - more mailing lists