]> git.ipfire.org Git - people/ms/linux.git/blobdiff - fs/cifs/smb2ops.c
Merge branch 'for-6.0/dax' into libnvdimm-fixes
[people/ms/linux.git] / fs / cifs / smb2ops.c
index 8802995b2d3d634e0d4687115904f9f87fda82c8..421be43af42533d2e254157b74ec0c166c6ff310 100644 (file)
@@ -27,6 +27,7 @@
 #include "smbdirect.h"
 #include "fscache.h"
 #include "fs_context.h"
+#include "cached_dir.h"
 
 /* Change credits for different ops and return the total number of credits */
 static int
@@ -126,13 +127,13 @@ smb2_add_credits(struct TCP_Server_Info *server,
                         optype, scredits, add);
        }
 
-       spin_lock(&cifs_tcp_ses_lock);
+       spin_lock(&server->srv_lock);
        if (server->tcpStatus == CifsNeedReconnect
            || server->tcpStatus == CifsExiting) {
-               spin_unlock(&cifs_tcp_ses_lock);
+               spin_unlock(&server->srv_lock);
                return;
        }
-       spin_unlock(&cifs_tcp_ses_lock);
+       spin_unlock(&server->srv_lock);
 
        switch (rc) {
        case -1:
@@ -218,12 +219,12 @@ smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size,
                        spin_lock(&server->req_lock);
                } else {
                        spin_unlock(&server->req_lock);
-                       spin_lock(&cifs_tcp_ses_lock);
+                       spin_lock(&server->srv_lock);
                        if (server->tcpStatus == CifsExiting) {
-                               spin_unlock(&cifs_tcp_ses_lock);
+                               spin_unlock(&server->srv_lock);
                                return -ENOENT;
                        }
-                       spin_unlock(&cifs_tcp_ses_lock);
+                       spin_unlock(&server->srv_lock);
 
                        spin_lock(&server->req_lock);
                        scredits = server->credits;
@@ -319,19 +320,19 @@ smb2_get_next_mid(struct TCP_Server_Info *server)
 {
        __u64 mid;
        /* for SMB2 we need the current value */
-       spin_lock(&GlobalMid_Lock);
+       spin_lock(&server->mid_lock);
        mid = server->CurrentMid++;
-       spin_unlock(&GlobalMid_Lock);
+       spin_unlock(&server->mid_lock);
        return mid;
 }
 
 static void
 smb2_revert_current_mid(struct TCP_Server_Info *server, const unsigned int val)
 {
-       spin_lock(&GlobalMid_Lock);
+       spin_lock(&server->mid_lock);
        if (server->CurrentMid >= val)
                server->CurrentMid -= val;
-       spin_unlock(&GlobalMid_Lock);
+       spin_unlock(&server->mid_lock);
 }
 
 static struct mid_q_entry *
@@ -346,7 +347,7 @@ __smb2_find_mid(struct TCP_Server_Info *server, char *buf, bool dequeue)
                return NULL;
        }
 
-       spin_lock(&GlobalMid_Lock);
+       spin_lock(&server->mid_lock);
        list_for_each_entry(mid, &server->pending_mid_q, qhead) {
                if ((mid->mid == wire_mid) &&
                    (mid->mid_state == MID_REQUEST_SUBMITTED) &&
@@ -356,11 +357,11 @@ __smb2_find_mid(struct TCP_Server_Info *server, char *buf, bool dequeue)
                                list_del_init(&mid->qhead);
                                mid->mid_flags |= MID_DELETED;
                        }
-                       spin_unlock(&GlobalMid_Lock);
+                       spin_unlock(&server->mid_lock);
                        return mid;
                }
        }
-       spin_unlock(&GlobalMid_Lock);
+       spin_unlock(&server->mid_lock);
        return NULL;
 }
 
@@ -386,7 +387,7 @@ smb2_dump_detail(void *buf, struct TCP_Server_Info *server)
                 shdr->Command, shdr->Status, shdr->Flags, shdr->MessageId,
                 shdr->Id.SyncId.ProcessId);
        cifs_server_dbg(VFS, "smb buf %p len %u\n", buf,
-                server->ops->calc_smb_size(buf, server));
+                server->ops->calc_smb_size(buf));
 #endif
 }
 
@@ -403,9 +404,9 @@ smb2_negotiate(const unsigned int xid,
 {
        int rc;
 
-       spin_lock(&GlobalMid_Lock);
+       spin_lock(&server->mid_lock);
        server->CurrentMid = 0;
-       spin_unlock(&GlobalMid_Lock);
+       spin_unlock(&server->mid_lock);
        rc = SMB2_negotiate(xid, ses, server);
        /* BB we probably don't need to retry with modern servers */
        if (rc == -EAGAIN)
@@ -680,7 +681,7 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon)
        struct cifs_ses *ses = tcon->ses;
 
        rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
-                       FSCTL_QUERY_NETWORK_INTERFACE_INFO, true /* is_fsctl */,
+                       FSCTL_QUERY_NETWORK_INTERFACE_INFO,
                        NULL /* no data input */, 0 /* no data input */,
                        CIFSMaxBufSize, (char **)&out_buf, &ret_data_len);
        if (rc == -EOPNOTSUPP) {
@@ -701,300 +702,6 @@ out:
        return rc;
 }
 
-static void
-smb2_close_cached_fid(struct kref *ref)
-{
-       struct cached_fid *cfid = container_of(ref, struct cached_fid,
-                                              refcount);
-       struct cached_dirent *dirent, *q;
-
-       if (cfid->is_valid) {
-               cifs_dbg(FYI, "clear cached root file handle\n");
-               SMB2_close(0, cfid->tcon, cfid->fid->persistent_fid,
-                          cfid->fid->volatile_fid);
-       }
-
-       /*
-        * We only check validity above to send SMB2_close,
-        * but we still need to invalidate these entries
-        * when this function is called
-        */
-       cfid->is_valid = false;
-       cfid->file_all_info_is_valid = false;
-       cfid->has_lease = false;
-       if (cfid->dentry) {
-               dput(cfid->dentry);
-               cfid->dentry = NULL;
-       }
-       /*
-        * Delete all cached dirent names
-        */
-       mutex_lock(&cfid->dirents.de_mutex);
-       list_for_each_entry_safe(dirent, q, &cfid->dirents.entries, entry) {
-               list_del(&dirent->entry);
-               kfree(dirent->name);
-               kfree(dirent);
-       }
-       cfid->dirents.is_valid = 0;
-       cfid->dirents.is_failed = 0;
-       cfid->dirents.ctx = NULL;
-       cfid->dirents.pos = 0;
-       mutex_unlock(&cfid->dirents.de_mutex);
-
-}
-
-void close_cached_dir(struct cached_fid *cfid)
-{
-       mutex_lock(&cfid->fid_mutex);
-       kref_put(&cfid->refcount, smb2_close_cached_fid);
-       mutex_unlock(&cfid->fid_mutex);
-}
-
-void close_cached_dir_lease_locked(struct cached_fid *cfid)
-{
-       if (cfid->has_lease) {
-               cfid->has_lease = false;
-               kref_put(&cfid->refcount, smb2_close_cached_fid);
-       }
-}
-
-void close_cached_dir_lease(struct cached_fid *cfid)
-{
-       mutex_lock(&cfid->fid_mutex);
-       close_cached_dir_lease_locked(cfid);
-       mutex_unlock(&cfid->fid_mutex);
-}
-
-void
-smb2_cached_lease_break(struct work_struct *work)
-{
-       struct cached_fid *cfid = container_of(work,
-                               struct cached_fid, lease_break);
-
-       close_cached_dir_lease(cfid);
-}
-
-/*
- * Open the and cache a directory handle.
- * Only supported for the root handle.
- * If error then *cfid is not initialized.
- */
-int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
-               const char *path,
-               struct cifs_sb_info *cifs_sb,
-               struct cached_fid **cfid)
-{
-       struct cifs_ses *ses;
-       struct TCP_Server_Info *server;
-       struct cifs_open_parms oparms;
-       struct smb2_create_rsp *o_rsp = NULL;
-       struct smb2_query_info_rsp *qi_rsp = NULL;
-       int resp_buftype[2];
-       struct smb_rqst rqst[2];
-       struct kvec rsp_iov[2];
-       struct kvec open_iov[SMB2_CREATE_IOV_SIZE];
-       struct kvec qi_iov[1];
-       int rc, flags = 0;
-       __le16 utf16_path = 0; /* Null - since an open of top of share */
-       u8 oplock = SMB2_OPLOCK_LEVEL_II;
-       struct cifs_fid *pfid;
-       struct dentry *dentry;
-
-       if (tcon == NULL || tcon->nohandlecache ||
-           is_smb1_server(tcon->ses->server))
-               return -ENOTSUPP;
-
-       ses = tcon->ses;
-       server = ses->server;
-
-       if (cifs_sb->root == NULL)
-               return -ENOENT;
-
-       if (strlen(path))
-               return -ENOENT;
-
-       dentry = cifs_sb->root;
-
-       mutex_lock(&tcon->crfid.fid_mutex);
-       if (tcon->crfid.is_valid) {
-               cifs_dbg(FYI, "found a cached root file handle\n");
-               *cfid = &tcon->crfid;
-               kref_get(&tcon->crfid.refcount);
-               mutex_unlock(&tcon->crfid.fid_mutex);
-               return 0;
-       }
-
-       /*
-        * We do not hold the lock for the open because in case
-        * SMB2_open needs to reconnect, it will end up calling
-        * cifs_mark_open_files_invalid() which takes the lock again
-        * thus causing a deadlock
-        */
-
-       mutex_unlock(&tcon->crfid.fid_mutex);
-
-       if (smb3_encryption_required(tcon))
-               flags |= CIFS_TRANSFORM_REQ;
-
-       if (!server->ops->new_lease_key)
-               return -EIO;
-
-       pfid = tcon->crfid.fid;
-       server->ops->new_lease_key(pfid);
-
-       memset(rqst, 0, sizeof(rqst));
-       resp_buftype[0] = resp_buftype[1] = CIFS_NO_BUFFER;
-       memset(rsp_iov, 0, sizeof(rsp_iov));
-
-       /* Open */
-       memset(&open_iov, 0, sizeof(open_iov));
-       rqst[0].rq_iov = open_iov;
-       rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE;
-
-       oparms.tcon = tcon;
-       oparms.create_options = cifs_create_options(cifs_sb, CREATE_NOT_FILE);
-       oparms.desired_access = FILE_READ_ATTRIBUTES;
-       oparms.disposition = FILE_OPEN;
-       oparms.fid = pfid;
-       oparms.reconnect = false;
-
-       rc = SMB2_open_init(tcon, server,
-                           &rqst[0], &oplock, &oparms, &utf16_path);
-       if (rc)
-               goto oshr_free;
-       smb2_set_next_command(tcon, &rqst[0]);
-
-       memset(&qi_iov, 0, sizeof(qi_iov));
-       rqst[1].rq_iov = qi_iov;
-       rqst[1].rq_nvec = 1;
-
-       rc = SMB2_query_info_init(tcon, server,
-                                 &rqst[1], COMPOUND_FID,
-                                 COMPOUND_FID, FILE_ALL_INFORMATION,
-                                 SMB2_O_INFO_FILE, 0,
-                                 sizeof(struct smb2_file_all_info) +
-                                 PATH_MAX * 2, 0, NULL);
-       if (rc)
-               goto oshr_free;
-
-       smb2_set_related(&rqst[1]);
-
-       rc = compound_send_recv(xid, ses, server,
-                               flags, 2, rqst,
-                               resp_buftype, rsp_iov);
-       mutex_lock(&tcon->crfid.fid_mutex);
-
-       /*
-        * Now we need to check again as the cached root might have
-        * been successfully re-opened from a concurrent process
-        */
-
-       if (tcon->crfid.is_valid) {
-               /* work was already done */
-
-               /* stash fids for close() later */
-               struct cifs_fid fid = {
-                       .persistent_fid = pfid->persistent_fid,
-                       .volatile_fid = pfid->volatile_fid,
-               };
-
-               /*
-                * caller expects this func to set the fid in crfid to valid
-                * cached root, so increment the refcount.
-                */
-               kref_get(&tcon->crfid.refcount);
-
-               mutex_unlock(&tcon->crfid.fid_mutex);
-
-               if (rc == 0) {
-                       /* close extra handle outside of crit sec */
-                       SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
-               }
-               rc = 0;
-               goto oshr_free;
-       }
-
-       /* Cached root is still invalid, continue normaly */
-
-       if (rc) {
-               if (rc == -EREMCHG) {
-                       tcon->need_reconnect = true;
-                       pr_warn_once("server share %s deleted\n",
-                                    tcon->treeName);
-               }
-               goto oshr_exit;
-       }
-
-       atomic_inc(&tcon->num_remote_opens);
-
-       o_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base;
-       oparms.fid->persistent_fid = o_rsp->PersistentFileId;
-       oparms.fid->volatile_fid = o_rsp->VolatileFileId;
-#ifdef CONFIG_CIFS_DEBUG2
-       oparms.fid->mid = le64_to_cpu(o_rsp->hdr.MessageId);
-#endif /* CIFS_DEBUG2 */
-
-       tcon->crfid.tcon = tcon;
-       tcon->crfid.is_valid = true;
-       tcon->crfid.dentry = dentry;
-       dget(dentry);
-       kref_init(&tcon->crfid.refcount);
-
-       /* BB TBD check to see if oplock level check can be removed below */
-       if (o_rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE) {
-               /*
-                * See commit 2f94a3125b87. Increment the refcount when we
-                * get a lease for root, release it if lease break occurs
-                */
-               kref_get(&tcon->crfid.refcount);
-               tcon->crfid.has_lease = true;
-               smb2_parse_contexts(server, o_rsp,
-                               &oparms.fid->epoch,
-                                   oparms.fid->lease_key, &oplock,
-                                   NULL, NULL);
-       } else
-               goto oshr_exit;
-
-       qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base;
-       if (le32_to_cpu(qi_rsp->OutputBufferLength) < sizeof(struct smb2_file_all_info))
-               goto oshr_exit;
-       if (!smb2_validate_and_copy_iov(
-                               le16_to_cpu(qi_rsp->OutputBufferOffset),
-                               sizeof(struct smb2_file_all_info),
-                               &rsp_iov[1], sizeof(struct smb2_file_all_info),
-                               (char *)&tcon->crfid.file_all_info))
-               tcon->crfid.file_all_info_is_valid = true;
-       tcon->crfid.time = jiffies;
-
-
-oshr_exit:
-       mutex_unlock(&tcon->crfid.fid_mutex);
-oshr_free:
-       SMB2_open_free(&rqst[0]);
-       SMB2_query_info_free(&rqst[1]);
-       free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
-       free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
-       if (rc == 0)
-               *cfid = &tcon->crfid;
-       return rc;
-}
-
-int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
-                             struct dentry *dentry,
-                             struct cached_fid **cfid)
-{
-       mutex_lock(&tcon->crfid.fid_mutex);
-       if (tcon->crfid.dentry == dentry) {
-               cifs_dbg(FYI, "found a cached root file handle by dentry\n");
-               *cfid = &tcon->crfid;
-               kref_get(&tcon->crfid.refcount);
-               mutex_unlock(&tcon->crfid.fid_mutex);
-               return 0;
-       }
-       mutex_unlock(&tcon->crfid.fid_mutex);
-       return -ENOENT;
-}
-
 static void
 smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon,
              struct cifs_sb_info *cifs_sb)
@@ -1013,9 +720,9 @@ smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon,
        oparms.fid = &fid;
        oparms.reconnect = false;
 
-       rc = open_cached_dir(xid, tcon, "", cifs_sb, &cfid);
+       rc = open_cached_dir(xid, tcon, "", cifs_sb, false, &cfid);
        if (rc == 0)
-               memcpy(&fid, cfid->fid, sizeof(struct cifs_fid));
+               memcpy(&fid, &cfid->fid, sizeof(struct cifs_fid));
        else
                rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL,
                               NULL, NULL);
@@ -1076,9 +783,16 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
        __u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
        struct cifs_open_parms oparms;
        struct cifs_fid fid;
+       struct cached_fid *cfid;
 
-       if ((*full_path == 0) && tcon->crfid.is_valid)
-               return 0;
+       rc = open_cached_dir(xid, tcon, full_path, cifs_sb, true, &cfid);
+       if (!rc) {
+               if (cfid->is_valid) {
+                       close_cached_dir(cfid);
+                       return 0;
+               }
+               close_cached_dir(cfid);
+       }
 
        utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
        if (!utf16_path)
@@ -1145,9 +859,7 @@ move_smb2_ea_to_cifs(char *dst, size_t dst_size,
        size_t name_len, value_len, user_name_len;
 
        while (src_size > 0) {
-               name = &src->ea_data[0];
                name_len = (size_t)src->ea_name_length;
-               value = &src->ea_data[src->ea_name_length + 1];
                value_len = (size_t)le16_to_cpu(src->ea_value_length);
 
                if (name_len == 0)
@@ -1159,6 +871,9 @@ move_smb2_ea_to_cifs(char *dst, size_t dst_size,
                        goto out;
                }
 
+               name = &src->ea_data[0];
+               value = &src->ea_data[src->ea_name_length + 1];
+
                if (ea_name) {
                        if (ea_name_len == name_len &&
                            memcmp(ea_name, name, name_len) == 0) {
@@ -1608,9 +1323,8 @@ SMB2_request_res_key(const unsigned int xid, struct cifs_tcon *tcon,
        struct resume_key_req *res_key;
 
        rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid,
-                       FSCTL_SRV_REQUEST_RESUME_KEY, true /* is_fsctl */,
-                       NULL, 0 /* no input */, CIFSMaxBufSize,
-                       (char **)&res_key, &ret_data_len);
+                       FSCTL_SRV_REQUEST_RESUME_KEY, NULL, 0 /* no input */,
+                       CIFSMaxBufSize, (char **)&res_key, &ret_data_len);
 
        if (rc == -EOPNOTSUPP) {
                pr_warn_once("Server share %s does not support copy range\n", tcon->treeName);
@@ -1752,7 +1466,7 @@ smb2_ioctl_query_info(const unsigned int xid,
                rqst[1].rq_nvec = SMB2_IOCTL_IOV_SIZE;
 
                rc = SMB2_ioctl_init(tcon, server, &rqst[1], COMPOUND_FID, COMPOUND_FID,
-                                    qi.info_type, true, buffer, qi.output_buffer_length,
+                                    qi.info_type, buffer, qi.output_buffer_length,
                                     CIFSMaxBufSize - MAX_SMB2_CREATE_RESPONSE_SIZE -
                                     MAX_SMB2_CLOSE_RESPONSE_SIZE);
                free_req1_func = SMB2_ioctl_free;
@@ -1886,17 +1600,8 @@ smb2_copychunk_range(const unsigned int xid,
        int chunks_copied = 0;
        bool chunk_sizes_updated = false;
        ssize_t bytes_written, total_bytes_written = 0;
-       struct inode *inode;
 
        pcchunk = kmalloc(sizeof(struct copychunk_ioctl), GFP_KERNEL);
-
-       /*
-        * We need to flush all unwritten data before we can send the
-        * copychunk ioctl to the server.
-        */
-       inode = d_inode(trgtfile->dentry);
-       filemap_write_and_wait(inode->i_mapping);
-
        if (pcchunk == NULL)
                return -ENOMEM;
 
@@ -1928,9 +1633,8 @@ smb2_copychunk_range(const unsigned int xid,
                retbuf = NULL;
                rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid,
                        trgtfile->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE,
-                       true /* is_fsctl */, (char *)pcchunk,
-                       sizeof(struct copychunk_ioctl), CIFSMaxBufSize,
-                       (char **)&retbuf, &ret_data_len);
+                       (char *)pcchunk, sizeof(struct copychunk_ioctl),
+                       CIFSMaxBufSize, (char **)&retbuf, &ret_data_len);
                if (rc == 0) {
                        if (ret_data_len !=
                                        sizeof(struct copychunk_ioctl_rsp)) {
@@ -2090,7 +1794,6 @@ static bool smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon,
 
        rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
                        cfile->fid.volatile_fid, FSCTL_SET_SPARSE,
-                       true /* is_fctl */,
                        &setsparse, 1, CIFSMaxBufSize, NULL, NULL);
        if (rc) {
                tcon->broken_sparse_sup = true;
@@ -2173,7 +1876,6 @@ smb2_duplicate_extents(const unsigned int xid,
        rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid,
                        trgtfile->fid.volatile_fid,
                        FSCTL_DUPLICATE_EXTENTS_TO_FILE,
-                       true /* is_fsctl */,
                        (char *)&dup_ext_buf,
                        sizeof(struct duplicate_extents_to_file),
                        CIFSMaxBufSize, NULL,
@@ -2208,7 +1910,6 @@ smb3_set_integrity(const unsigned int xid, struct cifs_tcon *tcon,
        return SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
                        cfile->fid.volatile_fid,
                        FSCTL_SET_INTEGRITY_INFORMATION,
-                       true /* is_fsctl */,
                        (char *)&integr_info,
                        sizeof(struct fsctl_set_integrity_information_req),
                        CIFSMaxBufSize, NULL,
@@ -2261,7 +1962,6 @@ smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon,
        rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
                        cfile->fid.volatile_fid,
                        FSCTL_SRV_ENUMERATE_SNAPSHOTS,
-                       true /* is_fsctl */,
                        NULL, 0 /* no input data */, max_response_size,
                        (char **)&retbuf,
                        &ret_data_len);
@@ -2574,7 +2274,6 @@ static void
 smb2_is_network_name_deleted(char *buf, struct TCP_Server_Info *server)
 {
        struct smb2_hdr *shdr = (struct smb2_hdr *)buf;
-       struct list_head *tmp, *tmp1;
        struct cifs_ses *ses;
        struct cifs_tcon *tcon;
 
@@ -2582,12 +2281,12 @@ smb2_is_network_name_deleted(char *buf, struct TCP_Server_Info *server)
                return;
 
        spin_lock(&cifs_tcp_ses_lock);
-       list_for_each(tmp, &server->smb_ses_list) {
-               ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
-               list_for_each(tmp1, &ses->tcon_list) {
-                       tcon = list_entry(tmp1, struct cifs_tcon, tcon_list);
+       list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
+               list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
                        if (tcon->tid == le32_to_cpu(shdr->Id.SyncId.TreeId)) {
+                               spin_lock(&tcon->tc_lock);
                                tcon->need_reconnect = true;
+                               spin_unlock(&tcon->tc_lock);
                                spin_unlock(&cifs_tcp_ses_lock);
                                pr_warn_once("Server share %s deleted.\n",
                                             tcon->treeName);
@@ -2723,8 +2422,12 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
        resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER;
        memset(rsp_iov, 0, sizeof(rsp_iov));
 
+       /*
+        * We can only call this for things we know are directories.
+        */
        if (!strcmp(path, ""))
-               open_cached_dir(xid, tcon, path, cifs_sb, &cfid); /* cfid null if open dir failed */
+               open_cached_dir(xid, tcon, path, cifs_sb, false,
+                               &cfid); /* cfid null if open dir failed */
 
        memset(&open_iov, 0, sizeof(open_iov));
        rqst[0].rq_iov = open_iov;
@@ -2750,8 +2453,8 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
        if (cfid) {
                rc = SMB2_query_info_init(tcon, server,
                                          &rqst[1],
-                                         cfid->fid->persistent_fid,
-                                         cfid->fid->volatile_fid,
+                                         cfid->fid.persistent_fid,
+                                         cfid->fid.volatile_fid,
                                          class, type, 0,
                                          output_len, 0,
                                          NULL);
@@ -2981,7 +2684,6 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
        do {
                rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
                                FSCTL_DFS_GET_REFERRALS,
-                               true /* is_fsctl */,
                                (char *)dfs_req, dfs_req_size, CIFSMaxBufSize,
                                (char **)&dfs_rsp, &dfs_rsp_size);
                if (!is_retryable_error(rc))
@@ -3188,8 +2890,7 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
 
        rc = SMB2_ioctl_init(tcon, server,
                             &rqst[1], fid.persistent_fid,
-                            fid.volatile_fid, FSCTL_GET_REPARSE_POINT,
-                            true /* is_fctl */, NULL, 0,
+                            fid.volatile_fid, FSCTL_GET_REPARSE_POINT, NULL, 0,
                             CIFSMaxBufSize -
                             MAX_SMB2_CREATE_RESPONSE_SIZE -
                             MAX_SMB2_CLOSE_RESPONSE_SIZE);
@@ -3369,8 +3070,7 @@ smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon,
 
        rc = SMB2_ioctl_init(tcon, server,
                             &rqst[1], COMPOUND_FID,
-                            COMPOUND_FID, FSCTL_GET_REPARSE_POINT,
-                            true /* is_fctl */, NULL, 0,
+                            COMPOUND_FID, FSCTL_GET_REPARSE_POINT, NULL, 0,
                             CIFSMaxBufSize -
                             MAX_SMB2_CREATE_RESPONSE_SIZE -
                             MAX_SMB2_CLOSE_RESPONSE_SIZE);
@@ -3598,26 +3298,43 @@ get_smb2_acl(struct cifs_sb_info *cifs_sb,
        return pntsd;
 }
 
+static long smb3_zero_data(struct file *file, struct cifs_tcon *tcon,
+                            loff_t offset, loff_t len, unsigned int xid)
+{
+       struct cifsFileInfo *cfile = file->private_data;
+       struct file_zero_data_information fsctl_buf;
+
+       cifs_dbg(FYI, "Offset %lld len %lld\n", offset, len);
+
+       fsctl_buf.FileOffset = cpu_to_le64(offset);
+       fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len);
+
+       return SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
+                         cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA,
+                         (char *)&fsctl_buf,
+                         sizeof(struct file_zero_data_information),
+                         0, NULL, NULL);
+}
+
 static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
                            loff_t offset, loff_t len, bool keep_size)
 {
        struct cifs_ses *ses = tcon->ses;
-       struct inode *inode;
-       struct cifsInodeInfo *cifsi;
+       struct inode *inode = file_inode(file);
+       struct cifsInodeInfo *cifsi = CIFS_I(inode);
        struct cifsFileInfo *cfile = file->private_data;
-       struct file_zero_data_information fsctl_buf;
        long rc;
        unsigned int xid;
        __le64 eof;
 
        xid = get_xid();
 
-       inode = d_inode(cfile->dentry);
-       cifsi = CIFS_I(inode);
-
        trace_smb3_zero_enter(xid, cfile->fid.persistent_fid, tcon->tid,
                              ses->Suid, offset, len);
 
+       inode_lock(inode);
+       filemap_invalidate_lock(inode->i_mapping);
+
        /*
         * We zero the range through ioctl, so we need remove the page caches
         * first, otherwise the data may be inconsistent with the server.
@@ -3625,26 +3342,12 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
        truncate_pagecache_range(inode, offset, offset + len - 1);
 
        /* if file not oplocked can't be sure whether asking to extend size */
-       if (!CIFS_CACHE_READ(cifsi))
-               if (keep_size == false) {
-                       rc = -EOPNOTSUPP;
-                       trace_smb3_zero_err(xid, cfile->fid.persistent_fid,
-                               tcon->tid, ses->Suid, offset, len, rc);
-                       free_xid(xid);
-                       return rc;
-               }
-
-       cifs_dbg(FYI, "Offset %lld len %lld\n", offset, len);
-
-       fsctl_buf.FileOffset = cpu_to_le64(offset);
-       fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len);
+       rc = -EOPNOTSUPP;
+       if (keep_size == false && !CIFS_CACHE_READ(cifsi))
+               goto zero_range_exit;
 
-       rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
-                       cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA, true,
-                       (char *)&fsctl_buf,
-                       sizeof(struct file_zero_data_information),
-                       0, NULL, NULL);
-       if (rc)
+       rc = smb3_zero_data(file, tcon, offset, len, xid);
+       if (rc < 0)
                goto zero_range_exit;
 
        /*
@@ -3657,6 +3360,8 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
        }
 
  zero_range_exit:
+       filemap_invalidate_unlock(inode->i_mapping);
+       inode_unlock(inode);
        free_xid(xid);
        if (rc)
                trace_smb3_zero_err(xid, cfile->fid.persistent_fid, tcon->tid,
@@ -3670,7 +3375,7 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
 static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
                            loff_t offset, loff_t len)
 {
-       struct inode *inode;
+       struct inode *inode = file_inode(file);
        struct cifsFileInfo *cfile = file->private_data;
        struct file_zero_data_information fsctl_buf;
        long rc;
@@ -3679,14 +3384,12 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
 
        xid = get_xid();
 
-       inode = d_inode(cfile->dentry);
-
+       inode_lock(inode);
        /* Need to make file sparse, if not already, before freeing range. */
        /* Consider adding equivalent for compressed since it could also work */
        if (!smb2_set_sparse(xid, tcon, cfile, inode, set_sparse)) {
                rc = -EOPNOTSUPP;
-               free_xid(xid);
-               return rc;
+               goto out;
        }
 
        filemap_invalidate_lock(inode->i_mapping);
@@ -3703,11 +3406,13 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
 
        rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
                        cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA,
-                       true /* is_fctl */, (char *)&fsctl_buf,
+                       (char *)&fsctl_buf,
                        sizeof(struct file_zero_data_information),
                        CIFSMaxBufSize, NULL, NULL);
-       free_xid(xid);
        filemap_invalidate_unlock(inode->i_mapping);
+out:
+       inode_unlock(inode);
+       free_xid(xid);
        return rc;
 }
 
@@ -3763,7 +3468,7 @@ static int smb3_simple_fallocate_range(unsigned int xid,
        in_data.length = cpu_to_le64(len);
        rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
                        cfile->fid.volatile_fid,
-                       FSCTL_QUERY_ALLOCATED_RANGES, true,
+                       FSCTL_QUERY_ALLOCATED_RANGES,
                        (char *)&in_data, sizeof(in_data),
                        1024 * sizeof(struct file_allocated_range_buffer),
                        (char **)&out_data, &out_data_len);
@@ -3964,39 +3669,50 @@ static long smb3_collapse_range(struct file *file, struct cifs_tcon *tcon,
 {
        int rc;
        unsigned int xid;
-       struct inode *inode;
+       struct inode *inode = file_inode(file);
        struct cifsFileInfo *cfile = file->private_data;
-       struct cifsInodeInfo *cifsi;
+       struct cifsInodeInfo *cifsi = CIFS_I(inode);
        __le64 eof;
+       loff_t old_eof;
 
        xid = get_xid();
 
-       inode = d_inode(cfile->dentry);
-       cifsi = CIFS_I(inode);
+       inode_lock(inode);
 
-       if (off >= i_size_read(inode) ||
-           off + len >= i_size_read(inode)) {
+       old_eof = i_size_read(inode);
+       if ((off >= old_eof) ||
+           off + len >= old_eof) {
                rc = -EINVAL;
                goto out;
        }
 
+       filemap_invalidate_lock(inode->i_mapping);
+       rc = filemap_write_and_wait_range(inode->i_mapping, off, old_eof - 1);
+       if (rc < 0)
+               goto out_2;
+
+       truncate_pagecache_range(inode, off, old_eof);
+
        rc = smb2_copychunk_range(xid, cfile, cfile, off + len,
-                                 i_size_read(inode) - off - len, off);
+                                 old_eof - off - len, off);
        if (rc < 0)
-               goto out;
+               goto out_2;
 
-       eof = cpu_to_le64(i_size_read(inode) - len);
+       eof = cpu_to_le64(old_eof - len);
        rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
                          cfile->fid.volatile_fid, cfile->pid, &eof);
        if (rc < 0)
-               goto out;
+               goto out_2;
 
        rc = 0;
 
        cifsi->server_eof = i_size_read(inode) - len;
        truncate_setsize(inode, cifsi->server_eof);
        fscache_resize_cookie(cifs_inode_cookie(inode), cifsi->server_eof);
+out_2:
+       filemap_invalidate_unlock(inode->i_mapping);
  out:
+       inode_unlock(inode);
        free_xid(xid);
        return rc;
 }
@@ -4007,34 +3723,47 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon,
        int rc;
        unsigned int xid;
        struct cifsFileInfo *cfile = file->private_data;
+       struct inode *inode = file_inode(file);
        __le64 eof;
-       __u64  count;
+       __u64  count, old_eof;
 
        xid = get_xid();
 
-       if (off >= i_size_read(file->f_inode)) {
+       inode_lock(inode);
+
+       old_eof = i_size_read(inode);
+       if (off >= old_eof) {
                rc = -EINVAL;
                goto out;
        }
 
-       count = i_size_read(file->f_inode) - off;
-       eof = cpu_to_le64(i_size_read(file->f_inode) + len);
+       count = old_eof - off;
+       eof = cpu_to_le64(old_eof + len);
+
+       filemap_invalidate_lock(inode->i_mapping);
+       rc = filemap_write_and_wait_range(inode->i_mapping, off, old_eof + len - 1);
+       if (rc < 0)
+               goto out_2;
+       truncate_pagecache_range(inode, off, old_eof);
 
        rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
                          cfile->fid.volatile_fid, cfile->pid, &eof);
        if (rc < 0)
-               goto out;
+               goto out_2;
 
        rc = smb2_copychunk_range(xid, cfile, cfile, off, count, off + len);
        if (rc < 0)
-               goto out;
+               goto out_2;
 
-       rc = smb3_zero_range(file, tcon, off, len, 1);
+       rc = smb3_zero_data(file, tcon, off, len, xid);
        if (rc < 0)
-               goto out;
+               goto out_2;
 
        rc = 0;
+out_2:
+       filemap_invalidate_unlock(inode->i_mapping);
  out:
+       inode_unlock(inode);
        free_xid(xid);
        return rc;
 }
@@ -4084,7 +3813,7 @@ static loff_t smb3_llseek(struct file *file, struct cifs_tcon *tcon, loff_t offs
 
        rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
                        cfile->fid.volatile_fid,
-                       FSCTL_QUERY_ALLOCATED_RANGES, true,
+                       FSCTL_QUERY_ALLOCATED_RANGES,
                        (char *)&in_data, sizeof(in_data),
                        sizeof(struct file_allocated_range_buffer),
                        (char **)&out_data, &out_data_len);
@@ -4144,7 +3873,7 @@ static int smb3_fiemap(struct cifs_tcon *tcon,
 
        rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
                        cfile->fid.volatile_fid,
-                       FSCTL_QUERY_ALLOCATED_RANGES, true,
+                       FSCTL_QUERY_ALLOCATED_RANGES,
                        (char *)&in_data, sizeof(in_data),
                        1024 * sizeof(struct file_allocated_range_buffer),
                        (char **)&out_data, &out_data_len);
@@ -4563,9 +4292,11 @@ smb2_get_enc_key(struct TCP_Server_Info *server, __u64 ses_id, int enc, u8 *key)
        list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
                list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
                        if (ses->Suid == ses_id) {
+                               spin_lock(&ses->ses_lock);
                                ses_enc_key = enc ? ses->smb3encryptionkey :
                                        ses->smb3decryptionkey;
                                memcpy(key, ses_enc_key, SMB3_ENC_DEC_KEY_SIZE);
+                               spin_unlock(&ses->ses_lock);
                                spin_unlock(&cifs_tcp_ses_lock);
                                return 0;
                        }
@@ -5080,23 +4811,24 @@ static void smb2_decrypt_offload(struct work_struct *work)
 
                        mid->callback(mid);
                } else {
-                       spin_lock(&cifs_tcp_ses_lock);
-                       spin_lock(&GlobalMid_Lock);
+                       spin_lock(&dw->server->srv_lock);
                        if (dw->server->tcpStatus == CifsNeedReconnect) {
+                               spin_lock(&dw->server->mid_lock);
                                mid->mid_state = MID_RETRY_NEEDED;
-                               spin_unlock(&GlobalMid_Lock);
-                               spin_unlock(&cifs_tcp_ses_lock);
+                               spin_unlock(&dw->server->mid_lock);
+                               spin_unlock(&dw->server->srv_lock);
                                mid->callback(mid);
                        } else {
+                               spin_lock(&dw->server->mid_lock);
                                mid->mid_state = MID_REQUEST_SUBMITTED;
                                mid->mid_flags &= ~(MID_DELETED);
                                list_add_tail(&mid->qhead,
                                        &dw->server->pending_mid_q);
-                               spin_unlock(&GlobalMid_Lock);
-                               spin_unlock(&cifs_tcp_ses_lock);
+                               spin_unlock(&dw->server->mid_lock);
+                               spin_unlock(&dw->server->srv_lock);
                        }
                }
-               cifs_mid_q_entry_release(mid);
+               release_mid(mid);
        }
 
 free_pages: