]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
smb: client: move most of reparse point handling code to common file
authorPaulo Alcantara <pc@manguebit.com>
Wed, 6 Mar 2024 05:28:48 +0000 (23:28 -0600)
committerSteve French <stfrench@microsoft.com>
Mon, 11 Mar 2024 00:33:57 +0000 (19:33 -0500)
In preparation to add support for creating special files also via WSL
reparse points in next commits.

Signed-off-by: Paulo Alcantara <pc@manguebit.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/client/Makefile
fs/smb/client/cifsglob.h
fs/smb/client/cifsproto.h
fs/smb/client/inode.c
fs/smb/client/readdir.c
fs/smb/client/reparse.c [new file with mode: 0644]
fs/smb/client/reparse.h [new file with mode: 0644]
fs/smb/client/smb2ops.c
fs/smb/client/smb2proto.h

index 0b07eb94c93b38e8cb76f8d495b61e3efa97d578..e11985f2460b267efb265b6dc0ea2bde34770af3 100644 (file)
@@ -12,7 +12,7 @@ cifs-y := trace.o cifsfs.o cifs_debug.o connect.o dir.o file.o \
          smb2ops.o smb2maperror.o smb2transport.o \
          smb2misc.o smb2pdu.o smb2inode.o smb2file.o cifsacl.o fs_context.o \
          dns_resolve.o cifs_spnego_negtokeninit.asn1.o asn1.o \
-         namespace.o
+         namespace.o reparse.o
 
 $(obj)/asn1.o: $(obj)/cifs_spnego_negtokeninit.asn1.h
 
index 14359cb14af4c9ba0d372b5ab10dce70fec2d0ac..afff1a91a9c8d1afb87f3008a0413dd615b13011 100644 (file)
@@ -223,19 +223,6 @@ struct cifs_open_info_data {
        };
 };
 
-static inline bool cifs_open_data_reparse(struct cifs_open_info_data *data)
-{
-       struct smb2_file_all_info *fi = &data->fi;
-       u32 attrs = le32_to_cpu(fi->Attributes);
-       bool ret;
-
-       ret = data->reparse_point || (attrs & ATTR_REPARSE);
-       if (ret)
-               attrs |= ATTR_REPARSE;
-       fi->Attributes = cpu_to_le32(attrs);
-       return ret;
-}
-
 /*
  *****************************************************************
  * Except the CIFS PDUs themselves all the
index 66d896d4b1444264b3044ee44c1743f08071fa4f..0723e1b57256b8fe0d07e0a4698d60074914ec38 100644 (file)
@@ -210,10 +210,6 @@ extern struct inode *cifs_iget(struct super_block *sb,
 int cifs_get_inode_info(struct inode **inode, const char *full_path,
                        struct cifs_open_info_data *data, struct super_block *sb, int xid,
                        const struct cifs_fid *fid);
-bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
-                                struct cifs_fattr *fattr,
-                                struct cifs_open_info_data *data);
-
 extern int smb311_posix_get_inode_info(struct inode **inode,
                                       const char *full_path,
                                       struct cifs_open_info_data *data,
index 78b3cbd5b0c4b4b3d9f164725a56065d393ee73a..540dca85fae0cd6b7d562c999fdb99fe1580c551 100644 (file)
@@ -26,6 +26,7 @@
 #include "fs_context.h"
 #include "cifs_ioctl.h"
 #include "cached_dir.h"
+#include "reparse.h"
 
 static void cifs_set_ops(struct inode *inode)
 {
@@ -728,84 +729,6 @@ out_reparse:
                fattr->cf_mode, fattr->cf_uniqueid, fattr->cf_nlink);
 }
 
-static inline dev_t nfs_mkdev(struct reparse_posix_data *buf)
-{
-       u64 v = le64_to_cpu(*(__le64 *)buf->DataBuffer);
-
-       return MKDEV(v >> 32, v & 0xffffffff);
-}
-
-bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
-                                struct cifs_fattr *fattr,
-                                struct cifs_open_info_data *data)
-{
-       struct reparse_posix_data *buf = data->reparse.posix;
-       u32 tag = data->reparse.tag;
-
-       if (tag == IO_REPARSE_TAG_NFS && buf) {
-               switch (le64_to_cpu(buf->InodeType)) {
-               case NFS_SPECFILE_CHR:
-                       fattr->cf_mode |= S_IFCHR;
-                       fattr->cf_dtype = DT_CHR;
-                       fattr->cf_rdev = nfs_mkdev(buf);
-                       break;
-               case NFS_SPECFILE_BLK:
-                       fattr->cf_mode |= S_IFBLK;
-                       fattr->cf_dtype = DT_BLK;
-                       fattr->cf_rdev = nfs_mkdev(buf);
-                       break;
-               case NFS_SPECFILE_FIFO:
-                       fattr->cf_mode |= S_IFIFO;
-                       fattr->cf_dtype = DT_FIFO;
-                       break;
-               case NFS_SPECFILE_SOCK:
-                       fattr->cf_mode |= S_IFSOCK;
-                       fattr->cf_dtype = DT_SOCK;
-                       break;
-               case NFS_SPECFILE_LNK:
-                       fattr->cf_mode |= S_IFLNK;
-                       fattr->cf_dtype = DT_LNK;
-                       break;
-               default:
-                       WARN_ON_ONCE(1);
-                       return false;
-               }
-               return true;
-       }
-
-       switch (tag) {
-       case IO_REPARSE_TAG_LX_SYMLINK:
-               fattr->cf_mode |= S_IFLNK;
-               fattr->cf_dtype = DT_LNK;
-               break;
-       case IO_REPARSE_TAG_LX_FIFO:
-               fattr->cf_mode |= S_IFIFO;
-               fattr->cf_dtype = DT_FIFO;
-               break;
-       case IO_REPARSE_TAG_AF_UNIX:
-               fattr->cf_mode |= S_IFSOCK;
-               fattr->cf_dtype = DT_SOCK;
-               break;
-       case IO_REPARSE_TAG_LX_CHR:
-               fattr->cf_mode |= S_IFCHR;
-               fattr->cf_dtype = DT_CHR;
-               break;
-       case IO_REPARSE_TAG_LX_BLK:
-               fattr->cf_mode |= S_IFBLK;
-               fattr->cf_dtype = DT_BLK;
-               break;
-       case 0: /* SMB1 symlink */
-       case IO_REPARSE_TAG_SYMLINK:
-       case IO_REPARSE_TAG_NFS:
-               fattr->cf_mode |= S_IFLNK;
-               fattr->cf_dtype = DT_LNK;
-               break;
-       default:
-               return false;
-       }
-       return true;
-}
-
 static void cifs_open_info_to_fattr(struct cifs_fattr *fattr,
                                    struct cifs_open_info_data *data,
                                    struct super_block *sb)
index 132ae7d884a97c69c8895cb58ac21f4972140895..88c88d50297c034a8ed802fd48486d487c1a8f75 100644 (file)
@@ -22,6 +22,7 @@
 #include "smb2proto.h"
 #include "fs_context.h"
 #include "cached_dir.h"
+#include "reparse.h"
 
 /*
  * To be safe - for UCS to UTF-8 with strings loaded with the rare long
@@ -55,23 +56,6 @@ static inline void dump_cifs_file_struct(struct file *file, char *label)
 }
 #endif /* DEBUG2 */
 
-/*
- * Match a reparse point inode if reparse tag and ctime haven't changed.
- *
- * Windows Server updates ctime of reparse points when their data have changed.
- * The server doesn't allow changing reparse tags from existing reparse points,
- * though it's worth checking.
- */
-static inline bool reparse_inode_match(struct inode *inode,
-                                      struct cifs_fattr *fattr)
-{
-       struct timespec64 ctime = inode_get_ctime(inode);
-
-       return (CIFS_I(inode)->cifsAttrs & ATTR_REPARSE) &&
-               CIFS_I(inode)->reparse_tag == fattr->cf_cifstag &&
-               timespec64_equal(&ctime, &fattr->cf_ctime);
-}
-
 /*
  * Attempt to preload the dcache with the results from the FIND_FIRST/NEXT
  *
diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c
new file mode 100644 (file)
index 0000000..c405be4
--- /dev/null
@@ -0,0 +1,320 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2024 Paulo Alcantara <pc@manguebit.com>
+ */
+
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/slab.h>
+#include "cifsglob.h"
+#include "smb2proto.h"
+#include "cifsproto.h"
+#include "cifs_unicode.h"
+#include "cifs_debug.h"
+#include "reparse.h"
+
+int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode,
+                               struct dentry *dentry, struct cifs_tcon *tcon,
+                               const char *full_path, const char *symname)
+{
+       struct reparse_symlink_data_buffer *buf = NULL;
+       struct cifs_open_info_data data;
+       struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+       struct inode *new;
+       struct kvec iov;
+       __le16 *path;
+       char *sym, sep = CIFS_DIR_SEP(cifs_sb);
+       u16 len, plen;
+       int rc = 0;
+
+       sym = kstrdup(symname, GFP_KERNEL);
+       if (!sym)
+               return -ENOMEM;
+
+       data = (struct cifs_open_info_data) {
+               .reparse_point = true,
+               .reparse = { .tag = IO_REPARSE_TAG_SYMLINK, },
+               .symlink_target = sym,
+       };
+
+       convert_delimiter(sym, sep);
+       path = cifs_convert_path_to_utf16(sym, cifs_sb);
+       if (!path) {
+               rc = -ENOMEM;
+               goto out;
+       }
+
+       plen = 2 * UniStrnlen((wchar_t *)path, PATH_MAX);
+       len = sizeof(*buf) + plen * 2;
+       buf = kzalloc(len, GFP_KERNEL);
+       if (!buf) {
+               rc = -ENOMEM;
+               goto out;
+       }
+
+       buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_SYMLINK);
+       buf->ReparseDataLength = cpu_to_le16(len - sizeof(struct reparse_data_buffer));
+       buf->SubstituteNameOffset = cpu_to_le16(plen);
+       buf->SubstituteNameLength = cpu_to_le16(plen);
+       memcpy(&buf->PathBuffer[plen], path, plen);
+       buf->PrintNameOffset = 0;
+       buf->PrintNameLength = cpu_to_le16(plen);
+       memcpy(buf->PathBuffer, path, plen);
+       buf->Flags = cpu_to_le32(*symname != '/' ? SYMLINK_FLAG_RELATIVE : 0);
+       if (*sym != sep)
+               buf->Flags = cpu_to_le32(SYMLINK_FLAG_RELATIVE);
+
+       convert_delimiter(sym, '/');
+       iov.iov_base = buf;
+       iov.iov_len = len;
+       new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
+                                    tcon, full_path, &iov);
+       if (!IS_ERR(new))
+               d_instantiate(dentry, new);
+       else
+               rc = PTR_ERR(new);
+out:
+       kfree(path);
+       cifs_free_open_info(&data);
+       kfree(buf);
+       return rc;
+}
+
+static int nfs_set_reparse_buf(struct reparse_posix_data *buf,
+                              mode_t mode, dev_t dev,
+                              struct kvec *iov)
+{
+       u64 type;
+       u16 len, dlen;
+
+       len = sizeof(*buf);
+
+       switch ((type = reparse_mode_nfs_type(mode))) {
+       case NFS_SPECFILE_BLK:
+       case NFS_SPECFILE_CHR:
+               dlen = sizeof(__le64);
+               break;
+       case NFS_SPECFILE_FIFO:
+       case NFS_SPECFILE_SOCK:
+               dlen = 0;
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_NFS);
+       buf->Reserved = 0;
+       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)MAJOR(dev) << 32) |
+                                                MINOR(dev));
+       iov->iov_base = buf;
+       iov->iov_len = len + dlen;
+       return 0;
+}
+
+int smb2_make_nfs_node(unsigned int xid, struct inode *inode,
+                      struct dentry *dentry, struct cifs_tcon *tcon,
+                      const char *full_path, umode_t mode, dev_t dev)
+{
+       struct cifs_open_info_data data;
+       struct reparse_posix_data *p;
+       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 (rc)
+               return rc;
+
+       data = (struct cifs_open_info_data) {
+               .reparse_point = true,
+               .reparse = { .tag = IO_REPARSE_TAG_NFS, .posix = p, },
+       };
+
+       new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
+                                    tcon, full_path, &iov);
+       if (!IS_ERR(new))
+               d_instantiate(dentry, new);
+       else
+               rc = PTR_ERR(new);
+       cifs_free_open_info(&data);
+       return rc;
+}
+
+/* See MS-FSCC 2.1.2.6 for the 'NFS' style reparse tags */
+static int parse_reparse_posix(struct reparse_posix_data *buf,
+                              struct cifs_sb_info *cifs_sb,
+                              struct cifs_open_info_data *data)
+{
+       unsigned int len;
+       u64 type;
+
+       switch ((type = le64_to_cpu(buf->InodeType))) {
+       case NFS_SPECFILE_LNK:
+               len = le16_to_cpu(buf->ReparseDataLength);
+               data->symlink_target = cifs_strndup_from_utf16(buf->DataBuffer,
+                                                              len, true,
+                                                              cifs_sb->local_nls);
+               if (!data->symlink_target)
+                       return -ENOMEM;
+               convert_delimiter(data->symlink_target, '/');
+               cifs_dbg(FYI, "%s: target path: %s\n",
+                        __func__, data->symlink_target);
+               break;
+       case NFS_SPECFILE_CHR:
+       case NFS_SPECFILE_BLK:
+       case NFS_SPECFILE_FIFO:
+       case NFS_SPECFILE_SOCK:
+               break;
+       default:
+               cifs_dbg(VFS, "%s: unhandled inode type: 0x%llx\n",
+                        __func__, type);
+               return -EOPNOTSUPP;
+       }
+       return 0;
+}
+
+static int parse_reparse_symlink(struct reparse_symlink_data_buffer *sym,
+                                u32 plen, bool unicode,
+                                struct cifs_sb_info *cifs_sb,
+                                struct cifs_open_info_data *data)
+{
+       unsigned int len;
+       unsigned int offs;
+
+       /* We handle Symbolic Link reparse tag here. See: MS-FSCC 2.1.2.4 */
+
+       offs = le16_to_cpu(sym->SubstituteNameOffset);
+       len = le16_to_cpu(sym->SubstituteNameLength);
+       if (offs + 20 > plen || offs + len + 20 > plen) {
+               cifs_dbg(VFS, "srv returned malformed symlink buffer\n");
+               return -EIO;
+       }
+
+       data->symlink_target = cifs_strndup_from_utf16(sym->PathBuffer + offs,
+                                                      len, unicode,
+                                                      cifs_sb->local_nls);
+       if (!data->symlink_target)
+               return -ENOMEM;
+
+       convert_delimiter(data->symlink_target, '/');
+       cifs_dbg(FYI, "%s: target path: %s\n", __func__, data->symlink_target);
+
+       return 0;
+}
+
+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)
+{
+       data->reparse.buf = buf;
+
+       /* See MS-FSCC 2.1.2 */
+       switch (le32_to_cpu(buf->ReparseTag)) {
+       case IO_REPARSE_TAG_NFS:
+               return parse_reparse_posix((struct reparse_posix_data *)buf,
+                                          cifs_sb, data);
+       case IO_REPARSE_TAG_SYMLINK:
+               return parse_reparse_symlink(
+                       (struct reparse_symlink_data_buffer *)buf,
+                       plen, unicode, cifs_sb, data);
+       case IO_REPARSE_TAG_LX_SYMLINK:
+       case IO_REPARSE_TAG_AF_UNIX:
+       case IO_REPARSE_TAG_LX_FIFO:
+       case IO_REPARSE_TAG_LX_CHR:
+       case IO_REPARSE_TAG_LX_BLK:
+               return 0;
+       default:
+               cifs_dbg(VFS, "%s: unhandled reparse tag: 0x%08x\n",
+                        __func__, le32_to_cpu(buf->ReparseTag));
+               return -EOPNOTSUPP;
+       }
+}
+
+int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb,
+                            struct kvec *rsp_iov,
+                            struct cifs_open_info_data *data)
+{
+       struct reparse_data_buffer *buf;
+       struct smb2_ioctl_rsp *io = rsp_iov->iov_base;
+       u32 plen = le32_to_cpu(io->OutputCount);
+
+       buf = (struct reparse_data_buffer *)((u8 *)io +
+                                            le32_to_cpu(io->OutputOffset));
+       return parse_reparse_point(buf, plen, cifs_sb, true, data);
+}
+
+bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
+                                struct cifs_fattr *fattr,
+                                struct cifs_open_info_data *data)
+{
+       struct reparse_posix_data *buf = data->reparse.posix;
+       u32 tag = data->reparse.tag;
+
+       if (tag == IO_REPARSE_TAG_NFS && buf) {
+               switch (le64_to_cpu(buf->InodeType)) {
+               case NFS_SPECFILE_CHR:
+                       fattr->cf_mode |= S_IFCHR;
+                       fattr->cf_dtype = DT_CHR;
+                       fattr->cf_rdev = reparse_nfs_mkdev(buf);
+                       break;
+               case NFS_SPECFILE_BLK:
+                       fattr->cf_mode |= S_IFBLK;
+                       fattr->cf_dtype = DT_BLK;
+                       fattr->cf_rdev = reparse_nfs_mkdev(buf);
+                       break;
+               case NFS_SPECFILE_FIFO:
+                       fattr->cf_mode |= S_IFIFO;
+                       fattr->cf_dtype = DT_FIFO;
+                       break;
+               case NFS_SPECFILE_SOCK:
+                       fattr->cf_mode |= S_IFSOCK;
+                       fattr->cf_dtype = DT_SOCK;
+                       break;
+               case NFS_SPECFILE_LNK:
+                       fattr->cf_mode |= S_IFLNK;
+                       fattr->cf_dtype = DT_LNK;
+                       break;
+               default:
+                       WARN_ON_ONCE(1);
+                       return false;
+               }
+               return true;
+       }
+
+       switch (tag) {
+       case IO_REPARSE_TAG_LX_SYMLINK:
+               fattr->cf_mode |= S_IFLNK;
+               fattr->cf_dtype = DT_LNK;
+               break;
+       case IO_REPARSE_TAG_LX_FIFO:
+               fattr->cf_mode |= S_IFIFO;
+               fattr->cf_dtype = DT_FIFO;
+               break;
+       case IO_REPARSE_TAG_AF_UNIX:
+               fattr->cf_mode |= S_IFSOCK;
+               fattr->cf_dtype = DT_SOCK;
+               break;
+       case IO_REPARSE_TAG_LX_CHR:
+               fattr->cf_mode |= S_IFCHR;
+               fattr->cf_dtype = DT_CHR;
+               break;
+       case IO_REPARSE_TAG_LX_BLK:
+               fattr->cf_mode |= S_IFBLK;
+               fattr->cf_dtype = DT_BLK;
+               break;
+       case 0: /* SMB1 symlink */
+       case IO_REPARSE_TAG_SYMLINK:
+       case IO_REPARSE_TAG_NFS:
+               fattr->cf_mode |= S_IFLNK;
+               fattr->cf_dtype = DT_LNK;
+               break;
+       default:
+               return false;
+       }
+       return true;
+}
diff --git a/fs/smb/client/reparse.h b/fs/smb/client/reparse.h
new file mode 100644 (file)
index 0000000..3ceb90d
--- /dev/null
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2024 Paulo Alcantara <pc@manguebit.com>
+ */
+
+#ifndef _CIFS_REPARSE_H
+#define _CIFS_REPARSE_H
+
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include "cifsglob.h"
+
+static inline dev_t reparse_nfs_mkdev(struct reparse_posix_data *buf)
+{
+       u64 v = le64_to_cpu(*(__le64 *)buf->DataBuffer);
+
+       return MKDEV(v >> 32, v & 0xffffffff);
+}
+
+static inline u64 reparse_mode_nfs_type(mode_t mode)
+{
+       switch (mode & S_IFMT) {
+       case S_IFBLK: return NFS_SPECFILE_BLK;
+       case S_IFCHR: return NFS_SPECFILE_CHR;
+       case S_IFIFO: return NFS_SPECFILE_FIFO;
+       case S_IFSOCK: return NFS_SPECFILE_SOCK;
+       }
+       return 0;
+}
+
+/*
+ * Match a reparse point inode if reparse tag and ctime haven't changed.
+ *
+ * Windows Server updates ctime of reparse points when their data have changed.
+ * The server doesn't allow changing reparse tags from existing reparse points,
+ * though it's worth checking.
+ */
+static inline bool reparse_inode_match(struct inode *inode,
+                                      struct cifs_fattr *fattr)
+{
+       struct timespec64 ctime = inode_get_ctime(inode);
+
+       return (CIFS_I(inode)->cifsAttrs & ATTR_REPARSE) &&
+               CIFS_I(inode)->reparse_tag == fattr->cf_cifstag &&
+               timespec64_equal(&ctime, &fattr->cf_ctime);
+}
+
+static inline bool cifs_open_data_reparse(struct cifs_open_info_data *data)
+{
+       struct smb2_file_all_info *fi = &data->fi;
+       u32 attrs = le32_to_cpu(fi->Attributes);
+       bool ret;
+
+       ret = data->reparse_point || (attrs & ATTR_REPARSE);
+       if (ret)
+               attrs |= ATTR_REPARSE;
+       fi->Attributes = cpu_to_le32(attrs);
+       return ret;
+}
+
+bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
+                                struct cifs_fattr *fattr,
+                                struct cifs_open_info_data *data);
+int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode,
+                               struct dentry *dentry, struct cifs_tcon *tcon,
+                               const char *full_path, const char *symname);
+int smb2_make_nfs_node(unsigned int xid, struct inode *inode,
+                      struct dentry *dentry, struct cifs_tcon *tcon,
+                      const char *full_path, umode_t mode, dev_t dev);
+int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb, struct kvec *rsp_iov,
+                            struct cifs_open_info_data *data);
+
+#endif /* _CIFS_REPARSE_H */
index 4695433fcf397f529754cc9ec266cb5ac1727512..87f2d157c3b8213d2d0da50d142c162984f34a78 100644 (file)
@@ -28,6 +28,7 @@
 #include "fscache.h"
 #include "fs_context.h"
 #include "cached_dir.h"
+#include "reparse.h"
 
 /* Change credits for different ops and return the total number of credits */
 static int
@@ -2986,109 +2987,6 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
        return rc;
 }
 
-/* See MS-FSCC 2.1.2.6 for the 'NFS' style reparse tags */
-static int parse_reparse_posix(struct reparse_posix_data *buf,
-                              struct cifs_sb_info *cifs_sb,
-                              struct cifs_open_info_data *data)
-{
-       unsigned int len;
-       u64 type;
-
-       switch ((type = le64_to_cpu(buf->InodeType))) {
-       case NFS_SPECFILE_LNK:
-               len = le16_to_cpu(buf->ReparseDataLength);
-               data->symlink_target = cifs_strndup_from_utf16(buf->DataBuffer,
-                                                              len, true,
-                                                              cifs_sb->local_nls);
-               if (!data->symlink_target)
-                       return -ENOMEM;
-               convert_delimiter(data->symlink_target, '/');
-               cifs_dbg(FYI, "%s: target path: %s\n",
-                        __func__, data->symlink_target);
-               break;
-       case NFS_SPECFILE_CHR:
-       case NFS_SPECFILE_BLK:
-       case NFS_SPECFILE_FIFO:
-       case NFS_SPECFILE_SOCK:
-               break;
-       default:
-               cifs_dbg(VFS, "%s: unhandled inode type: 0x%llx\n",
-                        __func__, type);
-               return -EOPNOTSUPP;
-       }
-       return 0;
-}
-
-static int parse_reparse_symlink(struct reparse_symlink_data_buffer *sym,
-                                u32 plen, bool unicode,
-                                struct cifs_sb_info *cifs_sb,
-                                struct cifs_open_info_data *data)
-{
-       unsigned int len;
-       unsigned int offs;
-
-       /* We handle Symbolic Link reparse tag here. See: MS-FSCC 2.1.2.4 */
-
-       offs = le16_to_cpu(sym->SubstituteNameOffset);
-       len = le16_to_cpu(sym->SubstituteNameLength);
-       if (offs + 20 > plen || offs + len + 20 > plen) {
-               cifs_dbg(VFS, "srv returned malformed symlink buffer\n");
-               return -EIO;
-       }
-
-       data->symlink_target = cifs_strndup_from_utf16(sym->PathBuffer + offs,
-                                                      len, unicode,
-                                                      cifs_sb->local_nls);
-       if (!data->symlink_target)
-               return -ENOMEM;
-
-       convert_delimiter(data->symlink_target, '/');
-       cifs_dbg(FYI, "%s: target path: %s\n", __func__, data->symlink_target);
-
-       return 0;
-}
-
-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)
-{
-       data->reparse.buf = buf;
-
-       /* See MS-FSCC 2.1.2 */
-       switch (le32_to_cpu(buf->ReparseTag)) {
-       case IO_REPARSE_TAG_NFS:
-               return parse_reparse_posix((struct reparse_posix_data *)buf,
-                                          cifs_sb, data);
-       case IO_REPARSE_TAG_SYMLINK:
-               return parse_reparse_symlink(
-                       (struct reparse_symlink_data_buffer *)buf,
-                       plen, unicode, cifs_sb, data);
-       case IO_REPARSE_TAG_LX_SYMLINK:
-       case IO_REPARSE_TAG_AF_UNIX:
-       case IO_REPARSE_TAG_LX_FIFO:
-       case IO_REPARSE_TAG_LX_CHR:
-       case IO_REPARSE_TAG_LX_BLK:
-               return 0;
-       default:
-               cifs_dbg(VFS, "%s: unhandled reparse tag: 0x%08x\n",
-                        __func__, le32_to_cpu(buf->ReparseTag));
-               return -EOPNOTSUPP;
-       }
-}
-
-static int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb,
-                                   struct kvec *rsp_iov,
-                                   struct cifs_open_info_data *data)
-{
-       struct reparse_data_buffer *buf;
-       struct smb2_ioctl_rsp *io = rsp_iov->iov_base;
-       u32 plen = le32_to_cpu(io->OutputCount);
-
-       buf = (struct reparse_data_buffer *)((u8 *)io +
-                                            le32_to_cpu(io->OutputOffset));
-       return parse_reparse_point(buf, plen, cifs_sb, true, data);
-}
-
 static struct cifs_ntsd *
 get_smb2_acl_by_fid(struct cifs_sb_info *cifs_sb,
                    const struct cifs_fid *cifsfid, u32 *pacllen, u32 info)
@@ -5128,152 +5026,6 @@ int cifs_sfu_make_node(unsigned int xid, struct inode *inode,
        return rc;
 }
 
-static inline u64 mode_nfs_type(mode_t mode)
-{
-       switch (mode & S_IFMT) {
-       case S_IFBLK: return NFS_SPECFILE_BLK;
-       case S_IFCHR: return NFS_SPECFILE_CHR;
-       case S_IFIFO: return NFS_SPECFILE_FIFO;
-       case S_IFSOCK: return NFS_SPECFILE_SOCK;
-       }
-       return 0;
-}
-
-static int nfs_set_reparse_buf(struct reparse_posix_data *buf,
-                              mode_t mode, dev_t dev,
-                              struct kvec *iov)
-{
-       u64 type;
-       u16 len, dlen;
-
-       len = sizeof(*buf);
-
-       switch ((type = mode_nfs_type(mode))) {
-       case NFS_SPECFILE_BLK:
-       case NFS_SPECFILE_CHR:
-               dlen = sizeof(__le64);
-               break;
-       case NFS_SPECFILE_FIFO:
-       case NFS_SPECFILE_SOCK:
-               dlen = 0;
-               break;
-       default:
-               return -EOPNOTSUPP;
-       }
-
-       buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_NFS);
-       buf->Reserved = 0;
-       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)MAJOR(dev) << 32) |
-                                                MINOR(dev));
-       iov->iov_base = buf;
-       iov->iov_len = len + dlen;
-       return 0;
-}
-
-static int nfs_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)
-{
-       struct cifs_open_info_data data;
-       struct reparse_posix_data *p;
-       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 (rc)
-               return rc;
-
-       data = (struct cifs_open_info_data) {
-               .reparse_point = true,
-               .reparse = { .tag = IO_REPARSE_TAG_NFS, .posix = p, },
-       };
-
-       new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
-                                    tcon, full_path, &iov);
-       if (!IS_ERR(new))
-               d_instantiate(dentry, new);
-       else
-               rc = PTR_ERR(new);
-       cifs_free_open_info(&data);
-       return rc;
-}
-
-static int smb2_create_reparse_symlink(const unsigned int xid,
-                                      struct inode *inode,
-                                      struct dentry *dentry,
-                                      struct cifs_tcon *tcon,
-                                      const char *full_path,
-                                      const char *symname)
-{
-       struct reparse_symlink_data_buffer *buf = NULL;
-       struct cifs_open_info_data data;
-       struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
-       struct inode *new;
-       struct kvec iov;
-       __le16 *path;
-       char *sym, sep = CIFS_DIR_SEP(cifs_sb);
-       u16 len, plen;
-       int rc = 0;
-
-       sym = kstrdup(symname, GFP_KERNEL);
-       if (!sym)
-               return -ENOMEM;
-
-       data = (struct cifs_open_info_data) {
-               .reparse_point = true,
-               .reparse = { .tag = IO_REPARSE_TAG_SYMLINK, },
-               .symlink_target = sym,
-       };
-
-       convert_delimiter(sym, sep);
-       path = cifs_convert_path_to_utf16(sym, cifs_sb);
-       if (!path) {
-               rc = -ENOMEM;
-               goto out;
-       }
-
-       plen = 2 * UniStrnlen((wchar_t *)path, PATH_MAX);
-       len = sizeof(*buf) + plen * 2;
-       buf = kzalloc(len, GFP_KERNEL);
-       if (!buf) {
-               rc = -ENOMEM;
-               goto out;
-       }
-
-       buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_SYMLINK);
-       buf->ReparseDataLength = cpu_to_le16(len - sizeof(struct reparse_data_buffer));
-       buf->SubstituteNameOffset = cpu_to_le16(plen);
-       buf->SubstituteNameLength = cpu_to_le16(plen);
-       memcpy(&buf->PathBuffer[plen], path, plen);
-       buf->PrintNameOffset = 0;
-       buf->PrintNameLength = cpu_to_le16(plen);
-       memcpy(buf->PathBuffer, path, plen);
-       buf->Flags = cpu_to_le32(*symname != '/' ? SYMLINK_FLAG_RELATIVE : 0);
-       if (*sym != sep)
-               buf->Flags = cpu_to_le32(SYMLINK_FLAG_RELATIVE);
-
-       convert_delimiter(sym, '/');
-       iov.iov_base = buf;
-       iov.iov_len = len;
-       new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
-                                    tcon, full_path, &iov);
-       if (!IS_ERR(new))
-               d_instantiate(dentry, new);
-       else
-               rc = PTR_ERR(new);
-out:
-       kfree(path);
-       cifs_free_open_info(&data);
-       kfree(buf);
-       return rc;
-}
-
 static int smb2_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)
@@ -5291,8 +5043,8 @@ static int smb2_make_node(unsigned int xid, struct inode *inode,
                rc = cifs_sfu_make_node(xid, inode, dentry, tcon,
                                        full_path, mode, dev);
        } else {
-               rc = nfs_make_node(xid, inode, dentry, tcon,
-                                  full_path, mode, dev);
+               rc = smb2_make_nfs_node(xid, inode, dentry, tcon,
+                                       full_path, mode, dev);
        }
        return rc;
 }
index 221143788a1c0bf08ae7cf0b6b9840ad9844d8a7..64a0ef0409a6eedc6e4415ecef911fcf0cb444fd 100644 (file)
@@ -310,5 +310,11 @@ int smb311_posix_query_path_info(const unsigned int xid,
 int posix_info_parse(const void *beg, const void *end,
                     struct smb2_posix_info_parsed *out);
 int posix_info_sid_size(const void *beg, const void *end);
+int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode,
+                               struct dentry *dentry, struct cifs_tcon *tcon,
+                               const char *full_path, const char *symname);
+int smb2_make_nfs_node(unsigned int xid, struct inode *inode,
+                      struct dentry *dentry, struct cifs_tcon *tcon,
+                      const char *full_path, umode_t mode, dev_t dev);
 
 #endif                 /* _SMB2PROTO_H */