diff --git a/fs/smb/client/cached_dir.c b/fs/smb/client/cached_dir.c index 88d5e9a32f28..647fa26da4d2 100644 --- a/fs/smb/client/cached_dir.c +++ b/fs/smb/client/cached_dir.c @@ -8,6 +8,7 @@ #include #include "cifsglob.h" #include "cifsproto.h" +#include "../common/smb2status.h" #include "cifs_debug.h" #include "smb2proto.h" #include "cached_dir.h" @@ -323,25 +324,37 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, rc = compound_send_recv(xid, ses, server, flags, 2, rqst, resp_buftype, rsp_iov); - if (rc) { - if (rc == -EREMCHG) { - tcon->need_reconnect = true; - pr_warn_once("server share %s deleted\n", - tcon->tree_name); - } + if (rc == -EREMCHG) { + tcon->need_reconnect = true; + pr_warn_once("server share %s deleted\n", + tcon->tree_name); + } + + if (!rsp_iov[0].iov_base || rsp_iov[0].iov_len < sizeof(*o_rsp)) { + if (!rc) + rc = -EIO; goto oshr_free; } - cfid->is_open = true; - - spin_lock(&cfids->cfid_list_lock); o_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base; + if (o_rsp->hdr.Status != STATUS_SUCCESS) { + if (!rc) + rc = -EIO; + goto oshr_free; + } + 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 */ + cfid->is_open = true; + atomic_inc(&tcon->num_remote_opens); + if (rc) + goto oshr_free; + + spin_lock(&cfids->cfid_list_lock); if (o_rsp->OplockLevel != SMB2_OPLOCK_LEVEL_LEASE) { spin_unlock(&cfids->cfid_list_lock); @@ -408,7 +421,6 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, close_cached_dir(cfid); } else { *ret_cfid = cfid; - atomic_inc(&tcon->num_remote_opens); } kfree(utf16_path); diff --git a/fs/smb/client/dfs_cache.c b/fs/smb/client/dfs_cache.c index 29dfd7595941..26956e33037e 100644 --- a/fs/smb/client/dfs_cache.c +++ b/fs/smb/client/dfs_cache.c @@ -798,13 +798,13 @@ static int get_targets(struct cache_entry *ce, struct dfs_cache_tgt_list *tl) INIT_LIST_HEAD(head); list_for_each_entry(t, &ce->tlist, list) { - it = kzalloc_obj(*it, GFP_ATOMIC); + it = kzalloc_obj(*it, GFP_KERNEL); if (!it) { rc = -ENOMEM; goto err_free_it; } - it->it_name = kstrdup(t->name, GFP_ATOMIC); + it->it_name = kstrdup(t->name, GFP_KERNEL); if (!it->it_name) { kfree(it); rc = -ENOMEM; diff --git a/fs/smb/client/dir.c b/fs/smb/client/dir.c index 6fa6d48fdfd3..1a56fa4d0e89 100644 --- a/fs/smb/client/dir.c +++ b/fs/smb/client/dir.c @@ -199,7 +199,7 @@ static int __cifs_do_create(struct inode *dir, struct dentry *direntry, struct tcon_link *tlink, unsigned int oflags, umode_t mode, __u32 *oplock, struct cifs_fid *fid, struct cifs_open_info_data *buf, - struct inode **inode) + struct inode **inode, bool *opened) { int rc = -ENOENT; int create_options = CREATE_NOT_DIR; @@ -216,6 +216,7 @@ static int __cifs_do_create(struct inode *dir, struct dentry *direntry, __le32 lease_flags = 0; *inode = NULL; + *opened = false; *oplock = 0; if (tcon->ses->server->oplocks) *oplock = REQ_OPLOCK; @@ -232,6 +233,7 @@ static int __cifs_do_create(struct inode *dir, struct dentry *direntry, oflags, oplock, &fid->netfid, xid); switch (rc) { case 0: + *opened = true; if (newinode == NULL) { /* query inode info */ goto cifs_create_get_file_info; @@ -253,11 +255,9 @@ static int __cifs_do_create(struct inode *dir, struct dentry *direntry, /* * The server may allow us to open things like * FIFOs, but the client isn't set up to deal - * with that. If it's not a regular file, just - * close it and proceed as if it were a normal - * lookup. + * with that. Keep the handle until the caller + * can finish the lookup. */ - CIFSSMBClose(xid, tcon, fid->netfid); goto cifs_create_get_file_info; } /* success, no need to query */ @@ -384,6 +384,7 @@ static int __cifs_do_create(struct inode *dir, struct dentry *direntry, } return rc; } + *opened = true; if (rdwr_for_fscache == 2) cifs_invalidate_cache(dir, FSCACHE_INVAL_DIO_WRITE); @@ -475,7 +476,7 @@ static int __cifs_do_create(struct inode *dir, struct dentry *direntry, return rc; out_err: - if (server->ops->close) + if (*opened && server->ops->close) server->ops->close(xid, tcon, fid); if (newinode) iput(newinode); @@ -487,7 +488,7 @@ static int cifs_do_create(struct inode *dir, struct dentry *direntry, unsigned int oflags, umode_t mode, __u32 *oplock, struct cifs_fid *fid, struct cifs_open_info_data *buf, - struct inode **inode) + struct inode **inode, bool *opened) { void *page = alloc_dentry_path(); const char *full_path; @@ -496,10 +497,11 @@ static int cifs_do_create(struct inode *dir, struct dentry *direntry, full_path = build_path_from_dentry(direntry, page); if (IS_ERR(full_path)) { rc = PTR_ERR(full_path); + *opened = false; } else { rc = __cifs_do_create(dir, direntry, full_path, xid, tlink, oflags, mode, oplock, - fid, buf, inode); + fid, buf, inode, opened); } free_dentry_path(page); return rc; @@ -529,6 +531,8 @@ int cifs_atomic_open(struct inode *dir, struct dentry *direntry, struct inode *inode; unsigned int xid; __u32 oplock; + bool is_regular; + bool opened; int rc; if (unlikely(cifs_forced_shutdown(cifs_sb))) @@ -581,12 +585,26 @@ int cifs_atomic_open(struct inode *dir, struct dentry *direntry, cifs_add_pending_open(&fid, tlink, &open); rc = cifs_do_create(dir, direntry, xid, tlink, oflags, mode, - &oplock, &fid, &buf, &inode); + &oplock, &fid, &buf, &inode, &opened); if (rc) { cifs_del_pending_open(&open); goto out; } + is_regular = S_ISREG(inode->i_mode); + if (!is_regular || !opened) { + if (opened && server->ops->close) + server->ops->close(xid, tcon, &fid); + cifs_del_pending_open(&open); + if (S_ISLNK(inode->i_mode) && + (oflags & (O_NOFOLLOW | __O_REGULAR)) == + (O_NOFOLLOW | __O_REGULAR) && !(oflags & O_EXCL)) { + iput(inode); + rc = -ELOOP; + goto out; + } + } + if (d_in_lookup(direntry)) { alias = d_splice_alias(inode, direntry); if (!IS_ERR_OR_NULL(alias)) @@ -595,9 +613,15 @@ int cifs_atomic_open(struct inode *dir, struct dentry *direntry, d_instantiate(direntry, inode); } - if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) + if (is_regular && opened && + (oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) file->f_mode |= FMODE_CREATED; + if (!is_regular || !opened) { + rc = finish_no_open(file, NULL); + goto out; + } + rc = finish_open(file, direntry, generic_file_open); if (rc) { if (server->ops->close) @@ -660,6 +684,7 @@ int cifs_create(struct mnt_idmap *idmap, struct inode *dir, struct inode *inode; struct cifs_fid fid; __u32 oplock; + bool opened; struct cifs_open_info_data buf = {}; cifs_dbg(FYI, "cifs_create parent inode = 0x%p name is: %pd and dentry = 0x%p\n", @@ -682,10 +707,10 @@ int cifs_create(struct mnt_idmap *idmap, struct inode *dir, server->ops->new_lease_key(&fid); rc = cifs_do_create(dir, direntry, xid, tlink, oflags, - mode, &oplock, &fid, &buf, &inode); + mode, &oplock, &fid, &buf, &inode, &opened); if (!rc) { d_instantiate(direntry, inode); - if (server->ops->close) + if (opened && server->ops->close) server->ops->close(xid, tcon, &fid); } @@ -1078,6 +1103,7 @@ int cifs_tmpfile(struct mnt_idmap *idmap, struct inode *dir, struct inode *inode; unsigned int xid; __u32 oplock; + bool opened; int namelen; int rc; @@ -1116,7 +1142,7 @@ int cifs_tmpfile(struct mnt_idmap *idmap, struct inode *dir, namelen = scnprintf(name, namesize, CIFS_TMPNAME_PREFIX "%x", atomic_inc_return(&cifs_tmpcounter)); rc = __cifs_do_create(dir, dentry, path, xid, tlink, oflags, - mode, &oplock, &fid, NULL, &inode); + mode, &oplock, &fid, NULL, &inode, &opened); if (!rc) { rc = d_mark_tmpfile_name(file, &QSTR_LEN(name, namelen)); if (rc) { diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c index 13fe8e3b48f3..f46a62eae659 100644 --- a/fs/smb/client/smb2inode.c +++ b/fs/smb/client/smb2inode.c @@ -598,8 +598,10 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon, /* smb2_parse_contexts() fills idata->fi.IndexNumber */ rc = smb2_parse_contexts(server, &rsp_iov[0], &oparms->fid->epoch, oparms->fid->lease_key, &oplock, &idata->fi, NULL); - if (rc) + if (rc) { cifs_dbg(VFS, "rc: %d parsing context of compound op\n", rc); + tmp_rc = rc; + } } for (i = 0; i < num_cmds; i++) { @@ -1121,7 +1123,7 @@ smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name, struct kvec close_iov; int resp_buftype[2]; struct cifs_fid fid; - int flags = 0; + int flags = CIFS_CP_CREATE_CLOSE_OP; __u8 oplock; int rc; diff --git a/fs/smb/client/smb2misc.c b/fs/smb/client/smb2misc.c index 0cfe60ae42c3..5e5cf92d1eb3 100644 --- a/fs/smb/client/smb2misc.c +++ b/fs/smb/client/smb2misc.c @@ -834,7 +834,8 @@ smb2_cancelled_close_fid(struct work_struct *work) */ static int __smb2_handle_cancelled_cmd(struct cifs_tcon *tcon, __u16 cmd, __u64 mid, - __u64 persistent_fid, __u64 volatile_fid) + __u64 persistent_fid, __u64 volatile_fid, + bool account_remote_open) { struct close_cancelled_open *cancelled; @@ -848,6 +849,8 @@ __smb2_handle_cancelled_cmd(struct cifs_tcon *tcon, __u16 cmd, __u64 mid, cancelled->cmd = cmd; cancelled->mid = mid; INIT_WORK(&cancelled->work, smb2_cancelled_close_fid); + if (account_remote_open) + atomic_inc(&tcon->num_remote_opens); WARN_ON(queue_work(cifsiod_wq, &cancelled->work) == false); return 0; @@ -884,7 +887,7 @@ smb2_handle_cancelled_close(struct cifs_tcon *tcon, __u64 persistent_fid, spin_unlock(&tcon->tc_lock); rc = __smb2_handle_cancelled_cmd(tcon, SMB2_CLOSE_HE, 0, - persistent_fid, volatile_fid); + persistent_fid, volatile_fid, false); if (rc) cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_cancelled_close); @@ -912,7 +915,7 @@ smb2_handle_cancelled_mid(struct mid_q_entry *mid, struct TCP_Server_Info *serve le16_to_cpu(hdr->Command), le64_to_cpu(hdr->MessageId), rsp->PersistentFileId, - rsp->VolatileFileId); + rsp->VolatileFileId, true); if (rc) cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_cancelled_mid); diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index 3464470d3297..aa142420dae2 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -4572,25 +4572,37 @@ smb3_create_lease_buf(u8 *lease_key, u8 oplock, u8 *parent_lease_key, __le32 fla static __u8 smb2_parse_lease_buf(void *buf, __u16 *epoch, char *lease_key) { - struct create_lease *lc = (struct create_lease *)buf; + struct create_context *cc = buf; + struct lease_context lc; *epoch = 0; /* not used */ - if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE) + if (le32_to_cpu(cc->DataLength) != sizeof(lc)) + return 0; + + memcpy(&lc, (u8 *)cc + le16_to_cpu(cc->DataOffset), sizeof(lc)); + if (lc.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE) return SMB2_OPLOCK_LEVEL_NOCHANGE; - return le32_to_cpu(lc->lcontext.LeaseState); + return le32_to_cpu(lc.LeaseState); } static __u8 smb3_parse_lease_buf(void *buf, __u16 *epoch, char *lease_key) { - struct create_lease_v2 *lc = (struct create_lease_v2 *)buf; + struct create_context *cc = buf; + struct lease_context_v2 lc; - *epoch = le16_to_cpu(lc->lcontext.Epoch); - if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE) + if (le32_to_cpu(cc->DataLength) != sizeof(lc)) { + *epoch = 0; + return 0; + } + + memcpy(&lc, (u8 *)cc + le16_to_cpu(cc->DataOffset), sizeof(lc)); + *epoch = le16_to_cpu(lc.Epoch); + if (lc.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE) return SMB2_OPLOCK_LEVEL_NOCHANGE; if (lease_key) - memcpy(lease_key, &lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); - return le32_to_cpu(lc->lcontext.LeaseState); + memcpy(lease_key, lc.LeaseKey, SMB2_LEASE_KEY_SIZE); + return le32_to_cpu(lc.LeaseState); } static unsigned int diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 880ce12f50c4..3d7ead36d1a0 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -2379,23 +2379,32 @@ create_reconnect_durable_buf(struct cifs_fid *fid) static void parse_query_id_ctxt(struct create_context *cc, struct smb2_file_all_info *buf) { - struct create_disk_id_rsp *pdisk_id = (struct create_disk_id_rsp *)cc; + u16 doff = le16_to_cpu(cc->DataOffset); + u32 dlen = le32_to_cpu(cc->DataLength); + u8 *beg; - cifs_dbg(FYI, "parse query id context 0x%llx 0x%llx\n", - pdisk_id->DiskFileId, pdisk_id->VolumeId); - buf->IndexNumber = pdisk_id->DiskFileId; + if (dlen < sizeof(__le64)) + return; + + beg = (u8 *)cc + doff; + memcpy(&buf->IndexNumber, beg, sizeof(__le64)); + cifs_dbg(FYI, "parse query id context 0x%llx\n", + le64_to_cpu(buf->IndexNumber)); } static void parse_posix_ctxt(struct create_context *cc, struct smb2_file_all_info *info, struct create_posix_rsp *posix) { - int sid_len; u8 *beg = (u8 *)cc + le16_to_cpu(cc->DataOffset); - u8 *end = beg + le32_to_cpu(cc->DataLength); + u32 dlen = le32_to_cpu(cc->DataLength); + u8 *end = beg + dlen; + int sid_len; u8 *sid; memset(posix, 0, sizeof(*posix)); + if (dlen < 3 * sizeof(__le32)) + return; posix->nlink = get_unaligned_le32(beg); posix->reparse_tag = get_unaligned_le32(beg + 4); @@ -2431,6 +2440,7 @@ int smb2_parse_contexts(struct TCP_Server_Info *server, struct smb2_create_rsp *rsp = rsp_iov->iov_base; struct create_context *cc; size_t rem, off, len; + size_t cc_len; size_t doff, dlen; size_t noff, nlen; char *name; @@ -2453,29 +2463,41 @@ int smb2_parse_contexts(struct TCP_Server_Info *server, buf->IndexNumber = 0; while (rem >= sizeof(*cc)) { + off = le32_to_cpu(cc->Next); + if (off) { + if ((off & 0x7) || off >= rem || off < sizeof(*cc)) + return -EINVAL; + cc_len = off; + } else { + cc_len = rem; + } + doff = le16_to_cpu(cc->DataOffset); dlen = le32_to_cpu(cc->DataLength); - if (check_add_overflow(doff, dlen, &len) || len > rem) + if (doff < sizeof(*cc) || + check_add_overflow(doff, dlen, &len) || len > cc_len) return -EINVAL; noff = le16_to_cpu(cc->NameOffset); nlen = le16_to_cpu(cc->NameLength); - if (noff + nlen > doff) + if (noff < sizeof(*cc) || + check_add_overflow(noff, nlen, &len) || len > cc_len || + (dlen && len > doff)) return -EINVAL; name = (char *)cc + noff; switch (nlen) { case 4: - if (!strncmp(name, SMB2_CREATE_REQUEST_LEASE, 4)) { + if (dlen && !strncmp(name, SMB2_CREATE_REQUEST_LEASE, 4)) { *oplock = server->ops->parse_lease_buf(cc, epoch, lease_key); - } else if (buf && + } else if (dlen && buf && !strncmp(name, SMB2_CREATE_QUERY_ON_DISK_ID, 4)) { parse_query_id_ctxt(cc, buf); } break; case 16: - if (posix && !memcmp(name, smb3_create_tag_posix, 16)) + if (dlen && posix && !memcmp(name, smb3_create_tag_posix, 16)) parse_posix_ctxt(cc, buf, posix); break; default: @@ -2487,13 +2509,18 @@ int smb2_parse_contexts(struct TCP_Server_Info *server, } off = le32_to_cpu(cc->Next); - if (!off) + if (!off) { + rem = 0; break; + } if (check_sub_overflow(rem, off, &rem)) return -EINVAL; cc = (struct create_context *)((u8 *)cc + off); } + if (rem) + return -EINVAL; + if (rsp->OplockLevel != SMB2_OPLOCK_LEVEL_LEASE) *oplock = rsp->OplockLevel; @@ -3389,6 +3416,9 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, rc = smb2_parse_contexts(server, &rsp_iov, &oparms->fid->epoch, oparms->fid->lease_key, oplock, file_info, posix); + if (rc) + SMB2_close(xid, tcon, oparms->fid->persistent_fid, + oparms->fid->volatile_fid); trace_smb3_open_done(xid, rsp->PersistentFileId, tcon->tid, ses->Suid, oparms->create_options, oparms->desired_access, diff --git a/fs/smb/client/smb2pdu.h b/fs/smb/client/smb2pdu.h index ab6c667bebc0..b37a1e3941a1 100644 --- a/fs/smb/client/smb2pdu.h +++ b/fs/smb/client/smb2pdu.h @@ -224,8 +224,7 @@ struct smb2_file_id_extd_directory_info { extern char smb2_padding[7]; /* - * See POSIX-SMB2 2.2.14.2.16 - * Link: https://gitlab.com/samba-team/smb3-posix-spec/-/blob/master/smb3_posix_extensions.md + * See POSIX-SMB2 2.1.3.2.1 */ struct create_posix_rsp { u32 nlink; @@ -238,6 +237,7 @@ struct create_posix_rsp { #define SMB2_QUERY_DIRECTORY_IOV_SIZE 2 /* + * See POSIX-FSCC 2.2.1 * SMB2-only POSIX info level for query dir * * See posix_info_sid_size(), posix_info_extra_size() and @@ -256,13 +256,17 @@ struct smb2_posix_info { __le64 Inode; __le32 DeviceId; __le32 Zero; - /* beginning of POSIX Create Context Response */ + /* + * Beginning of POSIX Create Context Response + * See POSIX-SMB2 2.1.3.2.1 + */ __le32 HardLinks; __le32 ReparseTag; __le32 Mode; /* * var sized owner SID * var sized group SID + * End of POSIX Create Context Response * le32 filenamelength * u8 filename[] */ diff --git a/fs/smb/client/transport.c b/fs/smb/client/transport.c index e266859818a4..6e21b5f8754a 100644 --- a/fs/smb/client/transport.c +++ b/fs/smb/client/transport.c @@ -805,6 +805,18 @@ cifs_cancelled_callback(struct TCP_Server_Info *server, struct mid_q_entry *mid) release_mid(server, mid); } +static void +cifs_mark_compound_mids_cancelled(struct mid_q_entry **mid, int count) +{ + int i; + + for (i = 0; i < count; i++) { + spin_lock(&mid[i]->mid_lock); + mid[i]->wait_cancelled = true; + spin_unlock(&mid[i]->mid_lock); + } +} + /* * cifs_pick_channel - pick an eligible channel for network operations * @@ -865,6 +877,7 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses, int *resp_buf_type, struct kvec *resp_iov) { int i, j, optype, rc = 0; + int num_processed = 0; struct mid_q_entry *mid[MAX_COMPOUND]; bool cancelled_mid[MAX_COMPOUND] = {false}; struct cifs_credits credits[MAX_COMPOUND] = { @@ -965,6 +978,10 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses, if (rc < 0) { revert_current_mid(server, num_rqst); server->sequence_number -= 2; + for (i = 0; i < num_rqst; i++) { + delete_mid(server, mid[i]); + cancelled_mid[i] = true; + } } cifs_server_unlock(server); @@ -1011,6 +1028,14 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses, break; } if (rc != 0) { + /* + * A completed CREATE earlier in the compound chain may have + * opened a remote handle even though a later wait was + * interrupted. Mark it cancelled so __release_mid() invokes + * the existing unmatched-open cleanup. + */ + cifs_mark_compound_mids_cancelled(mid, i); + for (; i < num_rqst; i++) { cifs_server_dbg(FYI, "Cancelling wait for mid %llu cmd: %d\n", mid[i]->mid, le16_to_cpu(mid[i]->command)); @@ -1033,6 +1058,14 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses, rc = cifs_sync_mid_result(mid[i], server); if (rc != 0) { + /* + * A previous CREATE may have completed before this + * response failed. Mark it cancelled so its remote + * handle is closed when the mid is released. + */ + cifs_mark_compound_mids_cancelled(mid, i); + /* Keep their response buffers for cancelled-mid cleanup. */ + num_processed = 0; /* mark this mid as cancelled to not free it below */ cancelled_mid[i] = true; goto out; @@ -1042,13 +1075,24 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses, mid[i]->mid_state != MID_RESPONSE_READY) { rc = smb_EIO1(smb_eio_trace_rx_mid_unready, mid[i]->mid_state); cifs_dbg(FYI, "Bad MID state?\n"); + cifs_mark_compound_mids_cancelled(mid, i); + num_processed = 0; goto out; } rc = server->ops->check_receive(mid[i], server, flags & CIFS_LOG_ERROR); + num_processed = i + 1; + } - if (resp_iov) { +out: + /* + * Delay moving response buffers out of their mids until response + * synchronization completes. This lets cancelled-mid cleanup inspect + * an earlier CREATE response if a later MID fails. + */ + if (resp_iov) { + for (i = 0; i < num_processed; i++) { buf = (char *)mid[i]->resp_buf; resp_iov[i].iov_base = buf; resp_iov[i].iov_len = mid[i]->resp_buf_size; @@ -1067,21 +1111,22 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses, /* * Compounding is never used during session establish. */ - spin_lock(&ses->ses_lock); - if ((ses->ses_status == SES_NEW) || (optype & CIFS_NEG_OP) || (optype & CIFS_SESS_OP)) { - struct kvec iov = { - .iov_base = resp_iov[0].iov_base, - .iov_len = resp_iov[0].iov_len - }; - spin_unlock(&ses->ses_lock); - cifs_server_lock(server); - smb311_update_preauth_hash(ses, server, &iov, 1); - cifs_server_unlock(server); + if (num_processed == num_rqst) { spin_lock(&ses->ses_lock); + if ((ses->ses_status == SES_NEW) || (optype & CIFS_NEG_OP) || (optype & CIFS_SESS_OP)) { + struct kvec iov = { + .iov_base = resp_iov[0].iov_base, + .iov_len = resp_iov[0].iov_len + }; + spin_unlock(&ses->ses_lock); + cifs_server_lock(server); + smb311_update_preauth_hash(ses, server, &iov, 1); + cifs_server_unlock(server); + spin_lock(&ses->ses_lock); + } + spin_unlock(&ses->ses_lock); } - spin_unlock(&ses->ses_lock); -out: /* * This will dequeue all mids. After this it is important that the * demultiplex_thread will not process any of these mids any further.