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 561f139..33c251b 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 62216dc..b736acd 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 c711f1f..c4e75af 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 4313cb0..a930c89 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;
 }