SMB3: Clean up query symlink when reparse point
authorRonnie Sahlberg <lsahlber@redhat.com>
Tue, 9 Apr 2019 22:44:46 +0000 (08:44 +1000)
committerSteve French <stfrench@microsoft.com>
Wed, 8 May 2019 04:24:55 +0000 (23:24 -0500)
Two of the common symlink formats use reparse points
(unlike mfsymlinks and also unlike the SMB1 posix
extensions).  This is the first part of the fixes
to allow these reparse points (NFS style and Windows
symlinks) to be resolved properly as symlinks by the
client.

Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/cifs/cifsglob.h
fs/cifs/link.c
fs/cifs/smb1ops.c
fs/cifs/smb2ops.c

index 561f1395eddda9d6c98213cd20359c2abab35be7..33c251b408aa6bdb84a7d472364ffaad0cfe9365 100644 (file)
@@ -355,7 +355,8 @@ struct smb_version_operations {
                               struct cifs_sb_info *);
        /* query symlink target */
        int (*query_symlink)(const unsigned int, struct cifs_tcon *,
-                            const char *, char **, struct cifs_sb_info *);
+                            struct cifs_sb_info *, const char *,
+                            char **, bool);
        /* open a file for non-posix mounts */
        int (*open)(const unsigned int, struct cifs_open_parms *,
                    __u32 *, FILE_ALL_INFO *);
index 62216dc8f9f522fc041f2462c74a9d3feb094394..b736acd3917bb4e12ce3315f592925f154a4a47e 100644 (file)
@@ -648,9 +648,16 @@ cifs_get_link(struct dentry *direntry, struct inode *inode,
                rc = query_mf_symlink(xid, tcon, cifs_sb, full_path,
                                      &target_path);
 
-       if (rc != 0 && server->ops->query_symlink)
-               rc = server->ops->query_symlink(xid, tcon, full_path,
-                                               &target_path, cifs_sb);
+       if (rc != 0 && server->ops->query_symlink) {
+               struct cifsInodeInfo *cifsi = CIFS_I(inode);
+               bool reparse_point = false;
+
+               if (cifsi->cifsAttrs & ATTR_REPARSE)
+                       reparse_point = true;
+
+               rc = server->ops->query_symlink(xid, tcon, cifs_sb, full_path,
+                                               &target_path, reparse_point);
+       }
 
        kfree(full_path);
        free_xid(xid);
index c711f1f39bf2eaded7827d3ae29ae4b438328edf..c4e75afa325858be45d6b9f9888e3d8b394f6703 100644 (file)
@@ -950,8 +950,8 @@ cifs_unix_dfs_readlink(const unsigned int xid, struct cifs_tcon *tcon,
 
 static int
 cifs_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
-                  const char *full_path, char **target_path,
-                  struct cifs_sb_info *cifs_sb)
+                  struct cifs_sb_info *cifs_sb, const char *full_path,
+                  char **target_path, bool is_reparse_point)
 {
        int rc;
        int oplock = 0;
@@ -960,6 +960,11 @@ cifs_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
 
        cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path);
 
+       if (is_reparse_point) {
+               cifs_dbg(VFS, "reparse points not handled for SMB1 symlinks\n");
+               return -EOPNOTSUPP;
+       }
+
        /* Check for unix extensions */
        if (cap_unix(tcon->ses)) {
                rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, target_path,
index 4313cb04549e283a7f31e597395ffd3aa7345595..a930c8965e5ca6ef082e3d2add6d0ad95bf7137e 100644 (file)
@@ -2390,46 +2390,129 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
 
 static int
 smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
-                  const char *full_path, char **target_path,
-                  struct cifs_sb_info *cifs_sb)
+                  struct cifs_sb_info *cifs_sb, const char *full_path,
+                  char **target_path, bool is_reparse_point)
 {
        int rc;
-       __le16 *utf16_path;
+       __le16 *utf16_path = NULL;
        __u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
        struct cifs_open_parms oparms;
        struct cifs_fid fid;
        struct kvec err_iov = {NULL, 0};
        struct smb2_err_rsp *err_buf = NULL;
-       int resp_buftype;
        struct smb2_symlink_err_rsp *symlink;
        unsigned int sub_len;
        unsigned int sub_offset;
        unsigned int print_len;
        unsigned int print_offset;
+       int flags = 0;
+       struct smb_rqst rqst[3];
+       int resp_buftype[3];
+       struct kvec rsp_iov[3];
+       struct kvec open_iov[SMB2_CREATE_IOV_SIZE];
+       struct kvec io_iov[SMB2_IOCTL_IOV_SIZE];
+       struct kvec close_iov[1];
+       struct smb2_create_rsp *create_rsp;
+       struct smb2_ioctl_rsp *ioctl_rsp;
+       char *ioctl_buf;
+       u32 plen;
 
        cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path);
 
+       if (smb3_encryption_required(tcon))
+               flags |= CIFS_TRANSFORM_REQ;
+
+       memset(rqst, 0, sizeof(rqst));
+       resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER;
+       memset(rsp_iov, 0, sizeof(rsp_iov));
+
        utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
        if (!utf16_path)
                return -ENOMEM;
 
+       /* Open */
+       memset(&open_iov, 0, sizeof(open_iov));
+       rqst[0].rq_iov = open_iov;
+       rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE;
+
+       memset(&oparms, 0, sizeof(oparms));
        oparms.tcon = tcon;
        oparms.desired_access = FILE_READ_ATTRIBUTES;
        oparms.disposition = FILE_OPEN;
+
        if (backup_cred(cifs_sb))
                oparms.create_options = CREATE_OPEN_BACKUP_INTENT;
        else
                oparms.create_options = 0;
+       if (is_reparse_point)
+               oparms.create_options = OPEN_REPARSE_POINT;
+
        oparms.fid = &fid;
        oparms.reconnect = false;
 
-       rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, &err_iov,
-                      &resp_buftype);
-       if (!rc)
-               SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
+       rc = SMB2_open_init(tcon, &rqst[0], &oplock, &oparms, utf16_path);
+       if (rc)
+               goto querty_exit;
+       smb2_set_next_command(tcon, &rqst[0]);
+
+
+       /* IOCTL */
+       memset(&io_iov, 0, sizeof(io_iov));
+       rqst[1].rq_iov = io_iov;
+       rqst[1].rq_nvec = SMB2_IOCTL_IOV_SIZE;
+
+       rc = SMB2_ioctl_init(tcon, &rqst[1], fid.persistent_fid,
+                            fid.volatile_fid, FSCTL_GET_REPARSE_POINT,
+                            true /* is_fctl */, NULL, 0, CIFSMaxBufSize);
+       if (rc)
+               goto querty_exit;
+
+       smb2_set_next_command(tcon, &rqst[1]);
+       smb2_set_related(&rqst[1]);
+
+
+       /* Close */
+       memset(&close_iov, 0, sizeof(close_iov));
+       rqst[2].rq_iov = close_iov;
+       rqst[2].rq_nvec = 1;
+
+       rc = SMB2_close_init(tcon, &rqst[2], COMPOUND_FID, COMPOUND_FID);
+       if (rc)
+               goto querty_exit;
+
+       smb2_set_related(&rqst[2]);
+
+       rc = compound_send_recv(xid, tcon->ses, flags, 3, rqst,
+                               resp_buftype, rsp_iov);
+
+       create_rsp = rsp_iov[0].iov_base;
+       if (create_rsp && create_rsp->sync_hdr.Status)
+               err_iov = rsp_iov[0];
+       ioctl_rsp = rsp_iov[1].iov_base;
+
+       /*
+        * Open was successful and we got an ioctl response.
+        */
+       if ((rc == 0) && (is_reparse_point)) {
+               /* See MS-FSCC 2.3.23 */
+
+               ioctl_buf = (char *)ioctl_rsp + le32_to_cpu(ioctl_rsp->OutputOffset);
+               plen = le32_to_cpu(ioctl_rsp->OutputCount);
+
+               if (plen + le32_to_cpu(ioctl_rsp->OutputOffset) >
+                   rsp_iov[1].iov_len) {
+                       cifs_dbg(VFS, "srv returned invalid ioctl length: %d\n", plen);
+                       rc = -EIO;
+                       goto querty_exit;
+               }
+
+               /* Do stuff with ioctl_buf/plen */
+               goto querty_exit;
+       }
+
        if (!rc || !err_iov.iov_base) {
                rc = -ENOENT;
-               goto free_path;
+               goto querty_exit;
        }
 
        err_buf = err_iov.iov_base;
@@ -2469,9 +2552,14 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
        cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path);
 
  querty_exit:
-       free_rsp_buf(resp_buftype, err_buf);
- free_path:
+       cifs_dbg(FYI, "query symlink rc %d\n", rc);
        kfree(utf16_path);
+       SMB2_open_free(&rqst[0]);
+       SMB2_ioctl_free(&rqst[1]);
+       SMB2_close_free(&rqst[2]);
+       free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
+       free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
+       free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base);
        return rc;
 }