Re: [PATCH 1/4] cifs: Add support for creating SFU symlinks

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

 



merged into cifs-2.6.git for-next pending review/testing

On Sun, Sep 15, 2024 at 2:46 PM Pali Rohár <pali@xxxxxxxxxx> wrote:
>
> Linux cifs client can already detect SFU symlinks and reads it content
> (target location). But currently is not able to create new symlink. So
> implement this missing support.
>
> When 'sfu' mount option is specified and 'mfsymlinks' is not specified then
> create new symlinks in SFU-style. This will provide full SFU compatibility
> of symlinks when mounting cifs share with 'sfu' option. 'mfsymlinks' option
> override SFU for better Apple compatibility as explained in fs_context.c
> file in smb3_update_mnt_flags() function.
>
> Extend __cifs_sfu_make_node() function, which now can handle also S_IFLNK
> type and refactor structures passed to sync_write() in this function, by
> splitting SFU type and SFU data from original combined struct win_dev as
> combined fixed-length struct cannot be used for variable-length symlinks.
>
> Signed-off-by: Pali Rohár <pali@xxxxxxxxxx>
> ---
>  fs/smb/client/cifspdu.h    |  6 ---
>  fs/smb/client/cifsproto.h  |  4 ++
>  fs/smb/client/fs_context.c | 13 ++++---
>  fs/smb/client/link.c       |  3 ++
>  fs/smb/client/smb2ops.c    | 80 +++++++++++++++++++++++++++++---------
>  5 files changed, 77 insertions(+), 29 deletions(-)
>
> diff --git a/fs/smb/client/cifspdu.h b/fs/smb/client/cifspdu.h
> index a2072ab9e586..c3b6263060b0 100644
> --- a/fs/smb/client/cifspdu.h
> +++ b/fs/smb/client/cifspdu.h
> @@ -2573,12 +2573,6 @@ typedef struct {
>  } __attribute__((packed)) FIND_FILE_STANDARD_INFO; /* level 0x1 FF resp data */
>
>
> -struct win_dev {
> -       unsigned char type[8]; /* IntxCHR or IntxBLK or LnxFIFO or LnxSOCK */
> -       __le64 major;
> -       __le64 minor;
> -} __attribute__((packed));
> -
>  struct fea {
>         unsigned char EA_flags;
>         __u8 name_len;
> diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h
> index 497bf3c447bc..791bddac0396 100644
> --- a/fs/smb/client/cifsproto.h
> +++ b/fs/smb/client/cifsproto.h
> @@ -676,6 +676,10 @@ char *extract_sharename(const char *unc);
>  int parse_reparse_point(struct reparse_data_buffer *buf,
>                         u32 plen, struct cifs_sb_info *cifs_sb,
>                         bool unicode, struct cifs_open_info_data *data);
> +int __cifs_sfu_make_node(unsigned int xid, struct inode *inode,
> +                        struct dentry *dentry, struct cifs_tcon *tcon,
> +                        const char *full_path, umode_t mode, dev_t dev,
> +                        const char *symname);
>  int cifs_sfu_make_node(unsigned int xid, struct inode *inode,
>                        struct dentry *dentry, struct cifs_tcon *tcon,
>                        const char *full_path, umode_t mode, dev_t dev);
> diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c
> index bc926ab2555b..2f0c3894b0f7 100644
> --- a/fs/smb/client/fs_context.c
> +++ b/fs/smb/client/fs_context.c
> @@ -1896,14 +1896,17 @@ void smb3_update_mnt_flags(struct cifs_sb_info *cifs_sb)
>         if (ctx->mfsymlinks) {
>                 if (ctx->sfu_emul) {
>                         /*
> -                        * Our SFU ("Services for Unix" emulation does not allow
> -                        * creating symlinks but does allow reading existing SFU
> -                        * symlinks (it does allow both creating and reading SFU
> -                        * style mknod and FIFOs though). When "mfsymlinks" and
> +                        * Our SFU ("Services for Unix") emulation allows now
> +                        * creating new and reading existing SFU symlinks.
> +                        * Older Linux kernel versions were not able to neither
> +                        * read existing nor create new SFU symlinks. But
> +                        * creating and reading SFU style mknod and FIFOs was
> +                        * supported for long time. When "mfsymlinks" and
>                          * "sfu" are both enabled at the same time, it allows
>                          * reading both types of symlinks, but will only create
>                          * them with mfsymlinks format. This allows better
> -                        * Apple compatibility (probably better for Samba too)
> +                        * Apple compatibility, compatibility with older Linux
> +                        * kernel clients (probably better for Samba too)
>                          * while still recognizing old Windows style symlinks.
>                          */
>                         cifs_dbg(VFS, "mount options mfsymlinks and sfu both enabled\n");
> diff --git a/fs/smb/client/link.c b/fs/smb/client/link.c
> index 80099bbb333b..47ddeb7fa111 100644
> --- a/fs/smb/client/link.c
> +++ b/fs/smb/client/link.c
> @@ -606,6 +606,9 @@ cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,
>         /* BB what if DFS and this volume is on different share? BB */
>         if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) {
>                 rc = create_mf_symlink(xid, pTcon, cifs_sb, full_path, symname);
> +       } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
> +               rc = __cifs_sfu_make_node(xid, inode, direntry, pTcon,
> +                                         full_path, S_IFLNK, 0, symname);
>  #ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
>         } else if (pTcon->unix_ext) {
>                 rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname,
> diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
> index 9c2d065d3cc4..2c251e9a3a30 100644
> --- a/fs/smb/client/smb2ops.c
> +++ b/fs/smb/client/smb2ops.c
> @@ -5055,9 +5055,10 @@ static int smb2_next_header(struct TCP_Server_Info *server, char *buf,
>         return 0;
>  }
>
> -static int __cifs_sfu_make_node(unsigned int xid, struct inode *inode,
> +int __cifs_sfu_make_node(unsigned int xid, struct inode *inode,
>                                 struct dentry *dentry, struct cifs_tcon *tcon,
> -                               const char *full_path, umode_t mode, dev_t dev)
> +                               const char *full_path, umode_t mode, dev_t dev,
> +                               const char *symname)
>  {
>         struct TCP_Server_Info *server = tcon->ses->server;
>         struct cifs_open_parms oparms;
> @@ -5065,30 +5066,64 @@ static int __cifs_sfu_make_node(unsigned int xid, struct inode *inode,
>         struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
>         struct cifs_fid fid;
>         unsigned int bytes_written;
> -       struct win_dev pdev = {};
> -       struct kvec iov[2];
> +       u8 type[8];
> +       int type_len = 0;
> +       struct {
> +               __le64 major;
> +               __le64 minor;
> +       } __packed pdev = {};
> +       __le16 *symname_utf16 = NULL;
> +       u8 *data = NULL;
> +       int data_len = 0;
> +       struct kvec iov[3];
>         __u32 oplock = server->oplocks ? REQ_OPLOCK : 0;
>         int rc;
>
>         switch (mode & S_IFMT) {
>         case S_IFCHR:
> -               memcpy(pdev.type, "IntxCHR\0", 8);
> +               type_len = 8;
> +               memcpy(type, "IntxCHR\0", type_len);
>                 pdev.major = cpu_to_le64(MAJOR(dev));
>                 pdev.minor = cpu_to_le64(MINOR(dev));
> +               data = (u8 *)&pdev;
> +               data_len = sizeof(pdev);
>                 break;
>         case S_IFBLK:
> -               memcpy(pdev.type, "IntxBLK\0", 8);
> +               type_len = 8;
> +               memcpy(type, "IntxBLK\0", type_len);
>                 pdev.major = cpu_to_le64(MAJOR(dev));
>                 pdev.minor = cpu_to_le64(MINOR(dev));
> +               data = (u8 *)&pdev;
> +               data_len = sizeof(pdev);
> +               break;
> +       case S_IFLNK:
> +               type_len = 8;
> +               memcpy(type, "IntxLNK\1", type_len);
> +               symname_utf16 = cifs_strndup_to_utf16(symname, strlen(symname),
> +                                                     &data_len, cifs_sb->local_nls,
> +                                                     NO_MAP_UNI_RSVD);
> +               if (!symname_utf16) {
> +                       rc = -ENOMEM;
> +                       goto out;
> +               }
> +               data_len -= 2; /* symlink is without trailing wide-nul */
> +               data = (u8 *)symname_utf16;
>                 break;
>         case S_IFSOCK:
> -               strscpy(pdev.type, "LnxSOCK");
> +               type_len = 8;
> +               strscpy(type, "LnxSOCK");
> +               data = (u8 *)&pdev;
> +               data_len = sizeof(pdev);
>                 break;
>         case S_IFIFO:
> -               strscpy(pdev.type, "LnxFIFO");
> +               type_len = 8;
> +               strscpy(type, "LnxFIFO");
> +               data = (u8 *)&pdev;
> +               data_len = sizeof(pdev);
>                 break;
>         default:
> -               return -EPERM;
> +               rc = -EPERM;
> +               goto out;
>         }
>
>         oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, GENERIC_WRITE,
> @@ -5098,17 +5133,26 @@ static int __cifs_sfu_make_node(unsigned int xid, struct inode *inode,
>
>         rc = server->ops->open(xid, &oparms, &oplock, NULL);
>         if (rc)
> -               return rc;
> +               goto out;
>
> -       io_parms.pid = current->tgid;
> -       io_parms.tcon = tcon;
> -       io_parms.length = sizeof(pdev);
> -       iov[1].iov_base = &pdev;
> -       iov[1].iov_len = sizeof(pdev);
> +       if (type_len + data_len > 0) {
> +               io_parms.pid = current->tgid;
> +               io_parms.tcon = tcon;
> +               io_parms.length = type_len + data_len;
> +               iov[1].iov_base = type;
> +               iov[1].iov_len = type_len;
> +               iov[2].iov_base = data;
> +               iov[2].iov_len = data_len;
> +
> +               rc = server->ops->sync_write(xid, &fid, &io_parms,
> +                                            &bytes_written,
> +                                            iov, ARRAY_SIZE(iov)-1);
> +       }
>
> -       rc = server->ops->sync_write(xid, &fid, &io_parms,
> -                                    &bytes_written, iov, 1);
>         server->ops->close(xid, tcon, &fid);
> +
> +out:
> +       kfree(symname_utf16);
>         return rc;
>  }
>
> @@ -5120,7 +5164,7 @@ int cifs_sfu_make_node(unsigned int xid, struct inode *inode,
>         int rc;
>
>         rc = __cifs_sfu_make_node(xid, inode, dentry, tcon,
> -                                 full_path, mode, dev);
> +                                 full_path, mode, dev, NULL);
>         if (rc)
>                 return rc;
>
> --
> 2.20.1
>
>


-- 
Thanks,

Steve





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

  Powered by Linux