diff options
Diffstat (limited to 'fs/smb/client/smb2pdu.c')
| -rw-r--r-- | fs/smb/client/smb2pdu.c | 877 |
1 files changed, 491 insertions, 386 deletions
diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 2ae2dbb6202b..5d57c895ca37 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -26,22 +26,25 @@ #include <linux/netfs.h> #include <trace/events/netfs.h> #include "cifsglob.h" -#include "cifsacl.h" #include "cifsproto.h" +#include "cifsacl.h" #include "smb2proto.h" #include "cifs_unicode.h" #include "cifs_debug.h" #include "ntlmssp.h" -#include "smb2status.h" +#include "../common/smb2status.h" #include "smb2glob.h" #include "cifspdu.h" #include "cifs_spnego.h" +#include "../common/smbdirect/smbdirect.h" #include "smbdirect.h" #include "trace.h" #ifdef CONFIG_CIFS_DFS_UPCALL #include "dfs_cache.h" #endif #include "cached_dir.h" +#include "compress.h" +#include "fs_context.h" /* * The following table defines the expected "StructureSize" of SMB2 requests @@ -82,6 +85,9 @@ int smb3_encryption_required(const struct cifs_tcon *tcon) if (tcon->seal && (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) return 1; + if (((global_secflags & CIFSSEC_MUST_SEAL) == CIFSSEC_MUST_SEAL) && + (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) + return 1; return 0; } @@ -162,7 +168,7 @@ out: static int cifs_chan_skip_or_disable(struct cifs_ses *ses, struct TCP_Server_Info *server, - bool from_reconnect) + bool from_reconnect, bool disable_mchan) { struct TCP_Server_Info *pserver; unsigned int chan_index; @@ -200,22 +206,53 @@ skip_terminate: return -EHOSTDOWN; } - cifs_server_dbg(VFS, - "server does not support multichannel anymore. Disable all other channels\n"); - cifs_disable_secondary_channels(ses); - + cifs_decrease_secondary_channels(ses, disable_mchan); return 0; } +/* + * smb3_update_ses_channels - Synchronize session channels with new configuration + * @ses: pointer to the CIFS session structure + * @server: pointer to the TCP server info structure + * @from_reconnect: indicates if called from reconnect context + * @disable_mchan: indicates if called from reconnect to disable multichannel + * + * Returns 0 on success or error code on failure. + * + * Outside of reconfigure, this function is called from cifs_mount() during mount + * and from reconnect scenarios to adjust channel count when the + * server's multichannel support changes. + */ +int smb3_update_ses_channels(struct cifs_ses *ses, struct TCP_Server_Info *server, + bool from_reconnect, bool disable_mchan) +{ + int rc = 0; + /* + * Manage session channels based on current count vs max: + * - If disable requested, skip or disable the channel + * - If below max channels, attempt to add more + * - If above max channels, skip or disable excess channels + */ + if (disable_mchan) + rc = cifs_chan_skip_or_disable(ses, server, from_reconnect, disable_mchan); + else { + if (ses->chan_count < ses->chan_max) + rc = cifs_try_adding_channels(ses); + else if (ses->chan_count > ses->chan_max) + rc = cifs_chan_skip_or_disable(ses, server, from_reconnect, disable_mchan); + } + + return rc; +} + static int smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, struct TCP_Server_Info *server, bool from_reconnect) { - int rc = 0; - struct nls_table *nls_codepage = NULL; struct cifs_ses *ses; int xid; + int rc = 0; /* * SMB2s NegProt, SessSetup, Logoff do not have tcon yet so @@ -225,11 +262,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, if (tcon == NULL) return 0; - /* - * Need to also skip SMB2_IOCTL because it is used for checking nested dfs links in - * cifs_tree_connect(). - */ - if (smb2_command == SMB2_TREE_CONNECT || smb2_command == SMB2_IOCTL) + if (smb2_command == SMB2_TREE_CONNECT) return 0; spin_lock(&tcon->tc_lock); @@ -239,8 +272,8 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, */ if (smb2_command != SMB2_TREE_DISCONNECT) { spin_unlock(&tcon->tc_lock); - cifs_dbg(FYI, "can not send cmd %d while umounting\n", - smb2_command); + cifs_tcon_dbg(FYI, "can not send cmd %d while umounting\n", + smb2_command); return -ENODEV; } } @@ -248,15 +281,15 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, ses = tcon->ses; if (!ses) - return -EIO; + return smb_EIO(smb_eio_trace_null_pointers); spin_lock(&ses->ses_lock); if (ses->ses_status == SES_EXITING) { spin_unlock(&ses->ses_lock); - return -EIO; + return smb_EIO(smb_eio_trace_sess_exiting); } spin_unlock(&ses->ses_lock); if (!ses->server || !server) - return -EIO; + return smb_EIO(smb_eio_trace_null_pointers); spin_lock(&server->srv_lock); if (server->tcpStatus == CifsNeedReconnect) { @@ -295,43 +328,32 @@ again: return 0; } spin_unlock(&ses->chan_lock); - cifs_dbg(FYI, "sess reconnect mask: 0x%lx, tcon reconnect: %d", - tcon->ses->chans_need_reconnect, - tcon->need_reconnect); + cifs_tcon_dbg(FYI, "sess reconnect mask: 0x%lx, tcon reconnect: %d\n", + tcon->ses->chans_need_reconnect, + tcon->need_reconnect); mutex_lock(&ses->session_mutex); /* - * if this is called by delayed work, and the channel has been disabled - * in parallel, the delayed work can continue to execute in parallel - * there's a chance that this channel may not exist anymore + * Handle the case where a concurrent thread failed to negotiate or + * killed a channel. */ spin_lock(&server->srv_lock); - if (server->tcpStatus == CifsExiting) { + switch (server->tcpStatus) { + case CifsExiting: spin_unlock(&server->srv_lock); mutex_unlock(&ses->session_mutex); - rc = -EHOSTDOWN; - goto out; - } - - /* - * Recheck after acquire mutex. If another thread is negotiating - * and the server never sends an answer the socket will be closed - * and tcpStatus set to reconnect. - */ - if (server->tcpStatus == CifsNeedReconnect) { + return -EHOSTDOWN; + case CifsNeedReconnect: spin_unlock(&server->srv_lock); mutex_unlock(&ses->session_mutex); - - if (tcon->retry) - goto again; - - rc = -EHOSTDOWN; - goto out; + if (!tcon->retry) + return -EHOSTDOWN; + goto again; + default: + break; } spin_unlock(&server->srv_lock); - nls_codepage = ses->local_nls; - /* * need to prevent multiple threads trying to simultaneously * reconnect the same SMB session @@ -353,43 +375,41 @@ again: spin_unlock(&ses->ses_lock); rc = cifs_negotiate_protocol(0, ses, server); - if (!rc) { - /* - * if server stopped supporting multichannel - * and the first channel reconnected, disable all the others. - */ - if (ses->chan_count > 1 && - !(server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) { - rc = cifs_chan_skip_or_disable(ses, server, - from_reconnect); - if (rc) { - mutex_unlock(&ses->session_mutex); - goto out; - } - } - - rc = cifs_setup_session(0, ses, server, nls_codepage); - if ((rc == -EACCES) || (rc == -EKEYEXPIRED) || (rc == -EKEYREVOKED)) { - /* - * Try alternate password for next reconnect (key rotation - * could be enabled on the server e.g.) if an alternate - * password is available and the current password is expired, - * but do not swap on non pwd related errors like host down - */ - if (ses->password2) - swap(ses->password2, ses->password); - } - - if ((rc == -EACCES) && !tcon->retry) { - mutex_unlock(&ses->session_mutex); - rc = -EHOSTDOWN; - goto failed; - } else if (rc) { + if (rc) { + mutex_unlock(&ses->session_mutex); + if (!tcon->retry) + return -EHOSTDOWN; + goto again; + } + /* + * if server stopped supporting multichannel + * and the first channel reconnected, disable all the others. + */ + if (ses->chan_count > 1 && + !(server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) { + rc = smb3_update_ses_channels(ses, server, + from_reconnect, true /* disable_mchan */); + if (rc) { mutex_unlock(&ses->session_mutex); goto out; } - } else { + } + + rc = cifs_setup_session(0, ses, server, ses->local_nls); + if ((rc == -EACCES) || (rc == -EKEYEXPIRED) || (rc == -EKEYREVOKED)) { + /* + * Try alternate password for next reconnect (key rotation + * could be enabled on the server e.g.) if an alternate + * password is available and the current password is expired, + * but do not swap on non pwd related errors like host down + */ + if (ses->password2) + swap(ses->password2, ses->password); + } + if (rc) { mutex_unlock(&ses->session_mutex); + if (rc == -EACCES && !tcon->retry) + return -EHOSTDOWN; goto out; } @@ -402,13 +422,13 @@ skip_sess_setup: if (tcon->use_persistent) tcon->need_reopen_files = true; - rc = cifs_tree_connect(0, tcon, nls_codepage); + rc = cifs_tree_connect(0, tcon); - cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc); + cifs_tcon_dbg(FYI, "reconnect tcon rc = %d\n", rc); if (rc) { /* If sess reconnected but tcon didn't, something strange ... */ mutex_unlock(&ses->session_mutex); - cifs_dbg(VFS, "reconnect tcon failed rc = %d\n", rc); + cifs_tcon_dbg(VFS, "reconnect tcon failed rc = %d\n", rc); goto out; } @@ -424,14 +444,23 @@ skip_sess_setup: if (!rc && (server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL) && server->ops->query_server_interfaces) { - mutex_unlock(&ses->session_mutex); - /* - * query server network interfaces, in case they change + * query server network interfaces, in case they change. + * Also mark the session as pending this update while the query + * is in progress. This will be used to avoid calling + * smb2_reconnect recursively. */ + ses->flags |= CIFS_SES_FLAGS_PENDING_QUERY_INTERFACES; xid = get_xid(); rc = server->ops->query_server_interfaces(xid, tcon, false); free_xid(xid); + ses->flags &= ~CIFS_SES_FLAGS_PENDING_QUERY_INTERFACES; + + if (!tcon->ipc && !tcon->dummy) + queue_delayed_work(cifsiod_wq, &tcon->query_interfaces, + (SMB_INTERFACE_POLL_INTERVAL * HZ)); + + mutex_unlock(&ses->session_mutex); if (rc == -EOPNOTSUPP && ses->chan_count > 1) { /* @@ -441,23 +470,22 @@ skip_sess_setup: * treat this as server not supporting multichannel */ - rc = cifs_chan_skip_or_disable(ses, server, - from_reconnect); + rc = smb3_update_ses_channels(ses, server, + from_reconnect, + true /* disable_mchan */); goto skip_add_channels; } else if (rc) - cifs_dbg(FYI, "%s: failed to query server interfaces: %d\n", - __func__, rc); + cifs_tcon_dbg(FYI, "%s: failed to query server interfaces: %d\n", + __func__, rc); if (ses->chan_max > ses->chan_count && ses->iface_count && !SERVER_IS_CHAN(server)) { - if (ses->chan_count == 1) { + if (ses->chan_count == 1) cifs_server_dbg(VFS, "supports multichannel now\n"); - queue_delayed_work(cifsiod_wq, &tcon->query_interfaces, - (SMB_INTERFACE_POLL_INTERVAL * HZ)); - } - cifs_try_adding_channels(ses); + smb3_update_ses_channels(ses, server, from_reconnect, + false /* disable_mchan */); } } else { mutex_unlock(&ses->session_mutex); @@ -469,7 +497,7 @@ skip_add_channels: spin_unlock(&ses->ses_lock); if (smb2_command != SMB2_INTERNAL_CMD) - mod_delayed_work(cifsiod_wq, &server->reconnect, 0); + cifs_queue_server_reconn(server); atomic_inc(&tconInfoReconnectCount); out: @@ -490,9 +518,9 @@ out: case SMB2_CHANGE_NOTIFY: case SMB2_QUERY_INFO: case SMB2_SET_INFO: + case SMB2_IOCTL: rc = -EAGAIN; } -failed: return rc; } @@ -573,11 +601,18 @@ static int smb2_ioctl_req_init(u32 opcode, struct cifs_tcon *tcon, struct TCP_Server_Info *server, void **request_buf, unsigned int *total_len) { - /* Skip reconnect only for FSCTL_VALIDATE_NEGOTIATE_INFO IOCTLs */ - if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO) { + /* + * Skip reconnect in one of the following cases: + * 1. For FSCTL_VALIDATE_NEGOTIATE_INFO IOCTLs + * 2. For FSCTL_QUERY_NETWORK_INTERFACE_INFO IOCTL when called from + * smb2_reconnect (indicated by CIFS_SES_FLAG_SCALE_CHANNELS ses flag) + */ + if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO || + (opcode == FSCTL_QUERY_NETWORK_INTERFACE_INFO && + (tcon->ses->flags & CIFS_SES_FLAGS_PENDING_QUERY_INTERFACES))) return __smb2_plain_req_init(SMB2_IOCTL, tcon, server, request_buf, total_len); - } + return smb2_plain_req_init(SMB2_IOCTL, tcon, server, request_buf, total_len); } @@ -1060,7 +1095,7 @@ SMB2_negotiate(const unsigned int xid, if (!server) { WARN(1, "%s: server is NULL!\n", __func__); - return -EIO; + return smb_EIO(smb_eio_trace_null_pointers); } rc = smb2_plain_req_init(SMB2_NEGOTIATE, NULL, server, @@ -1104,8 +1139,7 @@ SMB2_negotiate(const unsigned int xid, req->SecurityMode = 0; req->Capabilities = cpu_to_le32(server->vals->req_capabilities); - if (ses->chan_max > 1) - req->Capabilities |= cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL); + req->Capabilities |= cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL); /* ClientGUID must be zero for SMB2.02 dialect */ if (server->vals->protocol_id == SMB20_PROT_ID) @@ -1141,64 +1175,84 @@ SMB2_negotiate(const unsigned int xid, } else if (rc != 0) goto neg_exit; - rc = -EIO; + u16 dialect = le16_to_cpu(rsp->DialectRevision); if (strcmp(server->vals->version_string, SMB3ANY_VERSION_STRING) == 0) { - if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) { + switch (dialect) { + case SMB20_PROT_ID: cifs_server_dbg(VFS, "SMB2 dialect returned but not requested\n"); + rc = smb_EIO2(smb_eio_trace_neg_unreq_dialect, dialect, 3); goto neg_exit; - } else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) { + case SMB21_PROT_ID: cifs_server_dbg(VFS, "SMB2.1 dialect returned but not requested\n"); + rc = smb_EIO2(smb_eio_trace_neg_unreq_dialect, dialect, 3); goto neg_exit; - } else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) { + case SMB311_PROT_ID: /* ops set to 3.0 by default for default so update */ server->ops = &smb311_operations; server->vals = &smb311_values; + break; + default: + break; } } else if (strcmp(server->vals->version_string, - SMBDEFAULT_VERSION_STRING) == 0) { - if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) { + SMBDEFAULT_VERSION_STRING) == 0) { + switch (dialect) { + case SMB20_PROT_ID: cifs_server_dbg(VFS, "SMB2 dialect returned but not requested\n"); + rc = smb_EIO2(smb_eio_trace_neg_unreq_dialect, dialect, 0); goto neg_exit; - } else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) { + case SMB21_PROT_ID: /* ops set to 3.0 by default for default so update */ server->ops = &smb21_operations; server->vals = &smb21_values; - } else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) { + break; + case SMB311_PROT_ID: server->ops = &smb311_operations; server->vals = &smb311_values; + break; + default: + break; } - } else if (le16_to_cpu(rsp->DialectRevision) != - server->vals->protocol_id) { + } else if (dialect != server->vals->protocol_id) { /* if requested single dialect ensure returned dialect matched */ cifs_server_dbg(VFS, "Invalid 0x%x dialect returned: not requested\n", - le16_to_cpu(rsp->DialectRevision)); + dialect); + rc = smb_EIO2(smb_eio_trace_neg_unreq_dialect, + dialect, server->vals->protocol_id); goto neg_exit; } cifs_dbg(FYI, "mode 0x%x\n", rsp->SecurityMode); - if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) + switch (dialect) { + case SMB20_PROT_ID: cifs_dbg(FYI, "negotiated smb2.0 dialect\n"); - else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) + break; + case SMB21_PROT_ID: cifs_dbg(FYI, "negotiated smb2.1 dialect\n"); - else if (rsp->DialectRevision == cpu_to_le16(SMB30_PROT_ID)) + break; + case SMB30_PROT_ID: cifs_dbg(FYI, "negotiated smb3.0 dialect\n"); - else if (rsp->DialectRevision == cpu_to_le16(SMB302_PROT_ID)) + break; + case SMB302_PROT_ID: cifs_dbg(FYI, "negotiated smb3.02 dialect\n"); - else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) + break; + case SMB311_PROT_ID: cifs_dbg(FYI, "negotiated smb3.1.1 dialect\n"); - else { + break; + default: cifs_server_dbg(VFS, "Invalid dialect returned by server 0x%x\n", - le16_to_cpu(rsp->DialectRevision)); + dialect); + rc = smb_EIO1(smb_eio_trace_neg_inval_dialect, dialect); goto neg_exit; } rc = 0; - server->dialect = le16_to_cpu(rsp->DialectRevision); + server->dialect = dialect; /* * Keep a copy of the hash after negprot. This hash will be @@ -1227,7 +1281,9 @@ SMB2_negotiate(const unsigned int xid, * SMB3.0 supports only 1 cipher and doesn't have a encryption neg context * Set the cipher type manually. */ - if (server->dialect == SMB30_PROT_ID && (server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) + if ((server->dialect == SMB30_PROT_ID || + server->dialect == SMB302_PROT_ID) && + (server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) server->cipher_type = SMB2_ENCRYPTION_AES128_CCM; security_blob = smb2_get_data_area_len(&blob_offset, &blob_length, @@ -1252,16 +1308,19 @@ SMB2_negotiate(const unsigned int xid, if (rc == 1) rc = 0; else if (rc == 0) - rc = -EIO; + rc = smb_EIO1(smb_eio_trace_neg_decode_token, rc); } - if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) { + if (server->dialect == SMB311_PROT_ID) { if (rsp->NegotiateContextCount) rc = smb311_decode_neg_context(rsp, server, rsp_iov.iov_len); else cifs_server_dbg(VFS, "Missing expected negotiate contexts\n"); } + + if (server->cipher_type && !rc) + rc = smb3_crypto_aead_allocate(server); neg_exit: free_rsp_buf(resp_buftype, rsp); return rc; @@ -1306,8 +1365,7 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) pneg_inbuf->Capabilities = cpu_to_le32(server->vals->req_capabilities); - if (tcon->ses->chan_max > 1) - pneg_inbuf->Capabilities |= cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL); + pneg_inbuf->Capabilities |= cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL); memcpy(pneg_inbuf->Guid, server->client_guid, SMB2_CLIENT_GUID_SIZE); @@ -1365,32 +1423,47 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) } else if (rc != 0) { cifs_tcon_dbg(VFS, "validate protocol negotiate failed: %d\n", rc); - rc = -EIO; + rc = smb_EIO1(smb_eio_trace_neg_info_fail, rc); goto out_free_inbuf; } - rc = -EIO; if (rsplen != sizeof(*pneg_rsp)) { cifs_tcon_dbg(VFS, "Invalid protocol negotiate response size: %d\n", rsplen); /* relax check since Mac returns max bufsize allowed on ioctl */ - if (rsplen > CIFSMaxBufSize || rsplen < sizeof(*pneg_rsp)) + if (rsplen > CIFSMaxBufSize || rsplen < sizeof(*pneg_rsp)) { + rc = smb_EIO1(smb_eio_trace_neg_bad_rsplen, rsplen); goto out_free_rsp; + } } /* check validate negotiate info response matches what we got earlier */ - if (pneg_rsp->Dialect != cpu_to_le16(server->dialect)) + u16 dialect = le16_to_cpu(pneg_rsp->Dialect); + + if (dialect != server->dialect) { + rc = smb_EIO2(smb_eio_trace_neg_info_dialect, + dialect, server->dialect); goto vneg_out; + } + + u16 sec_mode = le16_to_cpu(pneg_rsp->SecurityMode); - if (pneg_rsp->SecurityMode != cpu_to_le16(server->sec_mode)) + if (sec_mode != server->sec_mode) { + rc = smb_EIO2(smb_eio_trace_neg_info_sec_mode, + sec_mode, server->sec_mode); goto vneg_out; + } /* do not validate server guid because not saved at negprot time yet */ + u32 caps = le32_to_cpu(pneg_rsp->Capabilities); - if ((le32_to_cpu(pneg_rsp->Capabilities) | SMB2_NT_FIND | - SMB2_LARGE_FILES) != server->capabilities) + if ((caps | SMB2_NT_FIND | + SMB2_LARGE_FILES) != server->capabilities) { + rc = smb_EIO2(smb_eio_trace_neg_info_caps, + caps, server->capabilities); goto vneg_out; + } /* validate negotiate successful */ rc = 0; @@ -1419,7 +1492,7 @@ smb2_select_sectype(struct TCP_Server_Info *server, enum securityEnum requested) if (server->sec_ntlmssp && (global_secflags & CIFSSEC_MAY_NTLMSSP)) return RawNTLMSSP; - if ((server->sec_kerberos || server->sec_mskerberos) && + if ((server->sec_kerberos || server->sec_mskerberos || server->sec_iakerb) && (global_secflags & CIFSSEC_MAY_KRB5)) return Kerberos; fallthrough; @@ -1562,8 +1635,14 @@ SMB2_sess_sendreceive(struct SMB2_sess_data *sess_data) cifs_small_buf_release(sess_data->iov[0].iov_base); if (rc == 0) sess_data->ses->expired_pwd = false; - else if ((rc == -EACCES) || (rc == -EKEYEXPIRED) || (rc == -EKEYREVOKED)) + else if ((rc == -EACCES) || (rc == -EKEYEXPIRED) || (rc == -EKEYREVOKED)) { + if (sess_data->ses->expired_pwd == false) + trace_smb3_key_expired(sess_data->server->hostname, + sess_data->ses->user_name, + sess_data->server->conn_id, + &sess_data->server->dstaddr, rc); sess_data->ses->expired_pwd = true; + } memcpy(&sess_data->iov[0], &rsp_iov, sizeof(struct kvec)); @@ -1616,8 +1695,6 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data) spnego_key = cifs_get_spnego_key(ses, server); if (IS_ERR(spnego_key)) { rc = PTR_ERR(spnego_key); - if (rc == -ENOKEY) - cifs_dbg(VFS, "Verify user has a krb5 ticket and keyutils is installed\n"); spnego_key = NULL; goto out; } @@ -1746,11 +1823,11 @@ SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data) if (rc) goto out; - if (offsetof(struct smb2_sess_setup_rsp, Buffer) != - le16_to_cpu(rsp->SecurityBufferOffset)) { - cifs_dbg(VFS, "Invalid security buffer offset %d\n", - le16_to_cpu(rsp->SecurityBufferOffset)); - rc = -EIO; + u16 boff = le16_to_cpu(rsp->SecurityBufferOffset); + + if (offsetof(struct smb2_sess_setup_rsp, Buffer) != boff) { + cifs_dbg(VFS, "Invalid security buffer offset %d\n", boff); + rc = smb_EIO1(smb_eio_trace_sess_buf_off, boff); goto out; } rc = decode_ntlmssp_challenge(rsp->Buffer, @@ -1904,7 +1981,7 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, if (!server) { WARN(1, "%s: server is NULL!\n", __func__); - return -EIO; + return smb_EIO(smb_eio_trace_null_pointers); } sess_data = kzalloc(sizeof(struct SMB2_sess_data), GFP_KERNEL); @@ -1954,10 +2031,9 @@ SMB2_logoff(const unsigned int xid, struct cifs_ses *ses) cifs_dbg(FYI, "disconnect session %p\n", ses); - if (ses && (ses->server)) - server = ses->server; - else - return -EIO; + if (!ses || !ses->server) + return smb_EIO(smb_eio_trace_null_pointers); + server = ses->server; /* no need to send SMB logoff if uid already closed due to reconnect */ spin_lock(&ses->chan_lock); @@ -2036,7 +2112,7 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, cifs_dbg(FYI, "TCON\n"); if (!server || !tree) - return -EIO; + return smb_EIO(smb_eio_trace_null_pointers); unc_path = kmalloc(MAX_SHARENAME_LENGTH * 2, GFP_KERNEL); if (unc_path == NULL) @@ -2153,7 +2229,7 @@ tcon_exit: tcon_error_exit: if (rsp && rsp->hdr.Status == STATUS_BAD_NETWORK_NAME) - cifs_tcon_dbg(VFS, "BAD_NETWORK_NAME: %s\n", tree); + cifs_dbg(VFS | ONCE, "BAD_NETWORK_NAME: %s\n", tree); goto tcon_exit; } @@ -2174,7 +2250,7 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon) cifs_dbg(FYI, "Tree Disconnect\n"); if (!ses || !(ses->server)) - return -EIO; + return smb_EIO(smb_eio_trace_null_pointers); trace_smb3_tdis_enter(xid, tcon->tid, ses->Suid, tcon->tree_name); spin_lock(&ses->chan_lock); @@ -2217,21 +2293,20 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon) return rc; } - -static struct create_durable * +static create_durable_req_t * create_durable_buf(void) { - struct create_durable *buf; + create_durable_req_t *buf; - buf = kzalloc(sizeof(struct create_durable), GFP_KERNEL); + buf = kzalloc(sizeof(create_durable_req_t), GFP_KERNEL); if (!buf) return NULL; buf->ccontext.DataOffset = cpu_to_le16(offsetof - (struct create_durable, Data)); + (create_durable_req_t, Data)); buf->ccontext.DataLength = cpu_to_le32(16); buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct create_durable, Name)); + (create_durable_req_t, Name)); buf->ccontext.NameLength = cpu_to_le16(4); /* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DHnQ" */ buf->Name[0] = 'D'; @@ -2241,20 +2316,20 @@ create_durable_buf(void) return buf; } -static struct create_durable * +static create_durable_req_t * create_reconnect_durable_buf(struct cifs_fid *fid) { - struct create_durable *buf; + create_durable_req_t *buf; - buf = kzalloc(sizeof(struct create_durable), GFP_KERNEL); + buf = kzalloc(sizeof(create_durable_req_t), GFP_KERNEL); if (!buf) return NULL; buf->ccontext.DataOffset = cpu_to_le16(offsetof - (struct create_durable, Data)); + (create_durable_req_t, Data)); buf->ccontext.DataLength = cpu_to_le32(16); buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct create_durable, Name)); + (create_durable_req_t, Name)); buf->ccontext.NameLength = cpu_to_le16(4); buf->Data.Fid.PersistentFileId = fid->persistent_fid; buf->Data.Fid.VolatileFileId = fid->volatile_fid; @@ -2313,7 +2388,7 @@ parse_posix_ctxt(struct create_context *cc, struct smb2_file_all_info *info, int smb2_parse_contexts(struct TCP_Server_Info *server, struct kvec *rsp_iov, - unsigned int *epoch, + __u16 *epoch, char *lease_key, __u8 *oplock, struct smb2_file_all_info *buf, struct create_posix_rsp *posix) @@ -2394,11 +2469,16 @@ static int add_lease_context(struct TCP_Server_Info *server, struct smb2_create_req *req, struct kvec *iov, - unsigned int *num_iovec, u8 *lease_key, __u8 *oplock) + unsigned int *num_iovec, + u8 *lease_key, + __u8 *oplock, + u8 *parent_lease_key, + __le32 flags) { unsigned int num = *num_iovec; - iov[num].iov_base = server->ops->create_lease_buf(lease_key, *oplock); + iov[num].iov_base = server->ops->create_lease_buf(lease_key, *oplock, + parent_lease_key, flags); if (iov[num].iov_base == NULL) return -ENOMEM; iov[num].iov_len = server->vals->create_lease_size; @@ -2407,21 +2487,21 @@ add_lease_context(struct TCP_Server_Info *server, return 0; } -static struct create_durable_v2 * +static struct create_durable_req_v2 * create_durable_v2_buf(struct cifs_open_parms *oparms) { struct cifs_fid *pfid = oparms->fid; - struct create_durable_v2 *buf; + struct create_durable_req_v2 *buf; - buf = kzalloc(sizeof(struct create_durable_v2), GFP_KERNEL); + buf = kzalloc(sizeof(struct create_durable_req_v2), GFP_KERNEL); if (!buf) return NULL; buf->ccontext.DataOffset = cpu_to_le16(offsetof - (struct create_durable_v2, dcontext)); - buf->ccontext.DataLength = cpu_to_le32(sizeof(struct durable_context_v2)); + (struct create_durable_req_v2, dcontext)); + buf->ccontext.DataLength = cpu_to_le32(sizeof(struct durable_context_v2_req)); buf->ccontext.NameOffset = cpu_to_le16(offsetof - (struct create_durable_v2, Name)); + (struct create_durable_req_v2, Name)); buf->ccontext.NameLength = cpu_to_le16(4); /* @@ -2491,7 +2571,7 @@ add_durable_v2_context(struct kvec *iov, unsigned int *num_iovec, iov[num].iov_base = create_durable_v2_buf(oparms); if (iov[num].iov_base == NULL) return -ENOMEM; - iov[num].iov_len = sizeof(struct create_durable_v2); + iov[num].iov_len = sizeof(struct create_durable_req_v2); *num_iovec = num + 1; return 0; } @@ -2535,7 +2615,7 @@ add_durable_context(struct kvec *iov, unsigned int *num_iovec, iov[num].iov_base = create_durable_buf(); if (iov[num].iov_base == NULL) return -ENOMEM; - iov[num].iov_len = sizeof(struct create_durable); + iov[num].iov_len = sizeof(create_durable_req_t); *num_iovec = num + 1; return 0; } @@ -2614,7 +2694,7 @@ create_sd_buf(umode_t mode, bool set_owner, unsigned int *len) unsigned int group_offset = 0; struct smb3_acl acl = {}; - *len = round_up(sizeof(struct crt_sd_ctxt) + (sizeof(struct cifs_ace) * 4), 8); + *len = round_up(sizeof(struct crt_sd_ctxt) + (sizeof(struct smb_ace) * 4), 8); if (set_owner) { /* sizeof(struct owner_group_sids) is already multiple of 8 so no need to round */ @@ -2663,21 +2743,21 @@ create_sd_buf(umode_t mode, bool set_owner, unsigned int *len) ptr += sizeof(struct smb3_acl); /* create one ACE to hold the mode embedded in reserved special SID */ - acelen = setup_special_mode_ACE((struct cifs_ace *)ptr, (__u64)mode); + acelen = setup_special_mode_ACE((struct smb_ace *)ptr, false, (__u64)mode); ptr += acelen; acl_size = acelen + sizeof(struct smb3_acl); ace_count = 1; if (set_owner) { /* we do not need to reallocate buffer to add the two more ACEs. plenty of space */ - acelen = setup_special_user_owner_ACE((struct cifs_ace *)ptr); + acelen = setup_special_user_owner_ACE((struct smb_ace *)ptr); ptr += acelen; acl_size += acelen; ace_count += 1; } /* and one more ACE to allow access for authenticated users */ - acelen = setup_authusers_ACE((struct cifs_ace *)ptr); + acelen = setup_authusers_ACE((struct smb_ace *)ptr); ptr += acelen; acl_size += acelen; ace_count += 1; @@ -2840,7 +2920,7 @@ replay_again: return -ENOMEM; if (!ses || !server) { - rc = -EIO; + rc = smb_EIO(smb_eio_trace_null_pointers); goto err_free_path; } @@ -2923,6 +3003,7 @@ replay_again: req->CreateContextsOffset = cpu_to_le32( sizeof(struct smb2_create_req) + iov[1].iov_len); + le32_add_cpu(&req->CreateContextsLength, iov[n_iov-1].iov_len); pc_buf = iov[n_iov-1].iov_base; } @@ -2956,7 +3037,7 @@ replay_again: */ rsp = (struct smb2_create_rsp *)rsp_iov.iov_base; if (rsp == NULL) { - rc = -EIO; + rc = smb_EIO(smb_eio_trace_mkdir_no_rsp); kfree(pc_buf); goto err_free_req; } @@ -2966,10 +3047,10 @@ replay_again: SMB2_close(xid, tcon, rsp->PersistentFileId, rsp->VolatileFileId); - /* Eventually save off posix specific response info and timestaps */ + /* Eventually save off posix specific response info and timestamps */ err_free_rsp_buf: - free_rsp_buf(resp_buftype, rsp); + free_rsp_buf(resp_buftype, rsp_iov.iov_base); kfree(pc_buf); err_free_req: cifs_small_buf_release(req); @@ -3070,7 +3151,9 @@ SMB2_open_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, req->RequestedOplockLevel = *oplock; /* no srv lease support */ else { rc = add_lease_context(server, req, iov, &n_iov, - oparms->fid->lease_key, oplock); + oparms->fid->lease_key, oplock, + oparms->fid->parent_lease_key, + oparms->lease_flags); if (rc) return rc; } @@ -3192,7 +3275,7 @@ replay_again: cifs_dbg(FYI, "create/open\n"); if (!ses || !server) - return -EIO; + return smb_EIO(smb_eio_trace_null_pointers); if (smb3_encryption_required(tcon)) flags |= CIFS_TRANSFORM_REQ; @@ -3257,7 +3340,7 @@ replay_again: buf->EndOfFile = rsp->EndofFile; buf->Attributes = rsp->FileAttributes; buf->NumberOfLinks = cpu_to_le32(1); - buf->DeletePending = 0; + buf->DeletePending = 0; /* successful open = not delete pending */ } @@ -3398,11 +3481,11 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, int retries = 0, cur_sleep = 1; if (!tcon) - return -EIO; + return smb_EIO(smb_eio_trace_null_pointers); ses = tcon->ses; if (!ses) - return -EIO; + return smb_EIO(smb_eio_trace_null_pointers); replay_again: /* reinitialize for possible replay */ @@ -3410,7 +3493,7 @@ replay_again: server = cifs_pick_channel(ses); if (!server) - return -EIO; + return smb_EIO(smb_eio_trace_null_pointers); cifs_dbg(FYI, "SMB2 IOCTL\n"); @@ -3473,7 +3556,7 @@ replay_again: * warning) */ if (rsp == NULL) { - rc = -EIO; + rc = smb_EIO(smb_eio_trace_ioctl_no_rsp); goto ioctl_exit; } @@ -3484,16 +3567,18 @@ replay_again: goto ioctl_exit; /* server returned no data */ else if (*plen > rsp_iov.iov_len || *plen > 0xFF00) { cifs_tcon_dbg(VFS, "srv returned invalid ioctl length: %d\n", *plen); + rc = smb_EIO2(smb_eio_trace_ioctl_data_len, *plen, rsp_iov.iov_len); *plen = 0; - rc = -EIO; goto ioctl_exit; } - if (rsp_iov.iov_len - *plen < le32_to_cpu(rsp->OutputOffset)) { - cifs_tcon_dbg(VFS, "Malformed ioctl resp: len %d offset %d\n", *plen, - le32_to_cpu(rsp->OutputOffset)); + u32 outoff = le32_to_cpu(rsp->OutputOffset); + + if (rsp_iov.iov_len - *plen < outoff) { + cifs_tcon_dbg(VFS, "Malformed ioctl resp: len %d offset %d\n", + *plen, outoff); + rc = smb_EIO2(smb_eio_trace_ioctl_out_off, rsp_iov.iov_len - *plen, outoff); *plen = 0; - rc = -EIO; goto ioctl_exit; } @@ -3601,7 +3686,7 @@ replay_again: cifs_dbg(FYI, "Close\n"); if (!ses || !server) - return -EIO; + return smb_EIO(smb_eio_trace_null_pointers); if (smb3_encryption_required(tcon)) flags |= CIFS_TRANSFORM_REQ; @@ -3798,7 +3883,7 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon, cifs_dbg(FYI, "Query Info\n"); if (!ses) - return -EIO; + return smb_EIO(smb_eio_trace_null_pointers); replay_again: /* reinitialize for possible replay */ @@ -3807,7 +3892,7 @@ replay_again: server = cifs_pick_channel(ses); if (!server) - return -EIO; + return smb_EIO(smb_eio_trace_null_pointers); if (smb3_encryption_required(tcon)) flags |= CIFS_TRANSFORM_REQ; @@ -3894,10 +3979,11 @@ int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, /* currently unused, as now we are doing compounding instead (see smb311_posix_query_path_info) */ int SMB311_posix_query_info(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, struct smb311_posix_qinfo *data, u32 *plen) + u64 persistent_fid, u64 volatile_fid, + struct smb311_posix_qinfo *data, u32 *plen) { size_t output_len = sizeof(struct smb311_posix_qinfo *) + - (sizeof(struct cifs_sid) * 2) + (PATH_MAX * 2); + (sizeof(struct smb_sid) * 2) + (PATH_MAX * 2); *plen = 0; return query_info(xid, tcon, persistent_fid, volatile_fid, @@ -3912,12 +3998,10 @@ SMB2_query_acl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, void **data, u32 *plen, u32 extra_info) { - __u32 additional_info = OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO | - extra_info; *plen = 0; return query_info(xid, tcon, persistent_fid, volatile_fid, - 0, SMB2_O_INFO_SECURITY, additional_info, + 0, SMB2_O_INFO_SECURITY, extra_info, SMB2_MAX_BUFFER_SIZE, MIN_SEC_DESC_LEN, data, plen); } @@ -3994,7 +4078,7 @@ replay_again: cifs_dbg(FYI, "change notify\n"); if (!ses || !server) - return -EIO; + return smb_EIO(smb_eio_trace_null_pointers); if (smb3_encryption_required(tcon)) flags |= CIFS_TRANSFORM_REQ; @@ -4036,9 +4120,12 @@ replay_again: smb_rsp = (struct smb2_change_notify_rsp *)rsp_iov.iov_base; - smb2_validate_iov(le16_to_cpu(smb_rsp->OutputBufferOffset), - le32_to_cpu(smb_rsp->OutputBufferLength), &rsp_iov, + rc = smb2_validate_iov(le16_to_cpu(smb_rsp->OutputBufferOffset), + le32_to_cpu(smb_rsp->OutputBufferLength), + &rsp_iov, sizeof(struct file_notify_information)); + if (rc) + goto cnotify_exit; *out_data = kmemdup((char *)smb_rsp + le16_to_cpu(smb_rsp->OutputBufferOffset), le32_to_cpu(smb_rsp->OutputBufferLength), GFP_KERNEL); @@ -4071,9 +4158,8 @@ replay_again: * FIXME: maybe we should consider checking that the reply matches request? */ static void -smb2_echo_callback(struct mid_q_entry *mid) +smb2_echo_callback(struct TCP_Server_Info *server, struct mid_q_entry *mid) { - struct TCP_Server_Info *server = mid->callback_data; struct smb2_echo_rsp *rsp = (struct smb2_echo_rsp *)mid->resp_buf; struct cifs_credits credits = { .value = 0, .instance = 0 }; @@ -4083,10 +4169,24 @@ smb2_echo_callback(struct mid_q_entry *mid) credits.instance = server->reconnect_instance; } - release_mid(mid); + release_mid(server, mid); add_credits(server, &credits, CIFS_ECHO_OP); } +static void cifs_renegotiate_iosize(struct TCP_Server_Info *server, + struct cifs_tcon *tcon) +{ + struct cifs_sb_info *cifs_sb; + + if (server == NULL || tcon == NULL) + return; + + spin_lock(&tcon->sb_list_lock); + list_for_each_entry(cifs_sb, &tcon->cifs_sb_list, tcon_sb_link) + cifs_negotiate_iosize(server, cifs_sb->ctx, tcon); + spin_unlock(&tcon->sb_list_lock); +} + void smb2_reconnect_server(struct work_struct *work) { struct TCP_Server_Info *server = container_of(work, @@ -4172,9 +4272,10 @@ void smb2_reconnect_server(struct work_struct *work) list_for_each_entry_safe(tcon, tcon2, &tmp_list, rlist) { rc = smb2_reconnect(SMB2_INTERNAL_CMD, tcon, server, true); - if (!rc) + if (!rc) { + cifs_renegotiate_iosize(server, tcon); cifs_reopen_persistent_handles(tcon); - else + } else resched = true; list_del_init(&tcon->rlist); if (tcon->ipc) @@ -4196,10 +4297,8 @@ void smb2_reconnect_server(struct work_struct *work) } goto done; } - tcon->status = TID_GOOD; - tcon->retry = false; - tcon->need_reconnect = false; + tcon->dummy = true; /* now reconnect sessions for necessary channels */ list_for_each_entry_safe(ses, ses2, &tmp_ses_list, rlist) { @@ -4215,7 +4314,7 @@ void smb2_reconnect_server(struct work_struct *work) done: cifs_dbg(FYI, "Reconnecting tcons and channels finished\n"); if (resched) - queue_delayed_work(cifsiod_wq, &server->reconnect, 2 * HZ); + cifs_requeue_server_reconn(server); mutex_unlock(&pserver->reconnect_mutex); /* now we can safely release srv struct */ @@ -4239,7 +4338,7 @@ SMB2_echo(struct TCP_Server_Info *server) server->ops->need_neg(server)) { spin_unlock(&server->srv_lock); /* No need to send echo on newly established connections */ - mod_delayed_work(cifsiod_wq, &server->reconnect, 0); + cifs_queue_server_reconn(server); return rc; } spin_unlock(&server->srv_lock); @@ -4315,7 +4414,7 @@ replay_again: cifs_dbg(FYI, "flush\n"); if (!ses || !(ses->server)) - return -EIO; + return smb_EIO(smb_eio_trace_null_pointers); if (smb3_encryption_required(tcon)) flags |= CIFS_TRANSFORM_REQ; @@ -4380,7 +4479,7 @@ static inline bool smb3_use_rdma_offload(struct cifs_io_parms *io_parms) return false; /* offload also has its overhead, so only do it if desired */ - if (io_parms->length < server->smbd_conn->rdma_readwrite_threshold) + if (io_parms->length < server->rdma_readwrite_threshold) return false; return true; @@ -4430,10 +4529,10 @@ smb2_new_read_req(void **buf, unsigned int *total_len, #ifdef CONFIG_CIFS_SMB_DIRECT /* * If we want to do a RDMA write, fill in and append - * smbd_buffer_descriptor_v1 to the end of read request + * smbdirect_buffer_descriptor_v1 to the end of read request */ - if (smb3_use_rdma_offload(io_parms)) { - struct smbd_buffer_descriptor_v1 *v1; + if (rdata && smb3_use_rdma_offload(io_parms)) { + struct smbdirect_buffer_descriptor_v1 *v1; bool need_invalidate = server->dialect == SMB30_PROT_ID; rdata->mr = smbd_register_mr(server->smbd_conn, &rdata->subreq.io_iter, @@ -4447,8 +4546,8 @@ smb2_new_read_req(void **buf, unsigned int *total_len, req->ReadChannelInfoOffset = cpu_to_le16(offsetof(struct smb2_read_req, Buffer)); req->ReadChannelInfoLength = - cpu_to_le16(sizeof(struct smbd_buffer_descriptor_v1)); - v1 = (struct smbd_buffer_descriptor_v1 *) &req->Buffer[0]; + cpu_to_le16(sizeof(struct smbdirect_buffer_descriptor_v1)); + v1 = (struct smbdirect_buffer_descriptor_v1 *) &req->Buffer[0]; v1->offset = cpu_to_le64(rdata->mr->mr->iova); v1->token = cpu_to_le32(rdata->mr->mr->rkey); v1->length = cpu_to_le32(rdata->mr->mr->length); @@ -4484,39 +4583,34 @@ smb2_new_read_req(void **buf, unsigned int *total_len, return rc; } -static void smb2_readv_worker(struct work_struct *work) -{ - struct cifs_io_subrequest *rdata = - container_of(work, struct cifs_io_subrequest, subreq.work); - - netfs_subreq_terminated(&rdata->subreq, - (rdata->result == 0 || rdata->result == -EAGAIN) ? - rdata->got_bytes : rdata->result, true); -} - static void -smb2_readv_callback(struct mid_q_entry *mid) +smb2_readv_callback(struct TCP_Server_Info *server, struct mid_q_entry *mid) { struct cifs_io_subrequest *rdata = mid->callback_data; + struct netfs_inode *ictx = netfs_inode(rdata->rreq->inode); struct cifs_tcon *tcon = tlink_tcon(rdata->req->cfile->tlink); - struct TCP_Server_Info *server = rdata->server; - struct smb2_hdr *shdr = - (struct smb2_hdr *)rdata->iov[0].iov_base; - struct cifs_credits credits = { .value = 0, .instance = 0 }; - struct smb_rqst rqst = { .rq_iov = &rdata->iov[1], .rq_nvec = 1 }; + struct smb2_hdr *shdr = (struct smb2_hdr *)rdata->iov[0].iov_base; + struct cifs_credits credits = { + .value = 0, + .instance = 0, + .rreq_debug_id = rdata->rreq->debug_id, + .rreq_debug_index = rdata->subreq.debug_index, + }; + struct smb_rqst rqst = { .rq_iov = &rdata->iov[0], .rq_nvec = 1 }; + unsigned int rreq_debug_id = rdata->rreq->debug_id; + unsigned int subreq_debug_index = rdata->subreq.debug_index; if (rdata->got_bytes) { rqst.rq_iter = rdata->subreq.io_iter; - rqst.rq_iter_size = iov_iter_count(&rdata->subreq.io_iter); } - WARN_ONCE(rdata->server != mid->server, + WARN_ONCE(rdata->server != server, "rdata server %p != mid server %p", - rdata->server, mid->server); + rdata->server, server); - cifs_dbg(FYI, "%s: mid=%llu state=%d result=%d bytes=%zu\n", + cifs_dbg(FYI, "%s: mid=%llu state=%d result=%d bytes=%zu/%zu\n", __func__, mid->mid, mid->mid_state, rdata->result, - rdata->subreq.len); + rdata->got_bytes, rdata->subreq.len - rdata->subreq.transferred); switch (mid->mid_state) { case MID_RESPONSE_RECEIVED: @@ -4537,7 +4631,12 @@ smb2_readv_callback(struct mid_q_entry *mid) cifs_stats_bytes_read(tcon, rdata->got_bytes); break; case MID_REQUEST_SUBMITTED: + trace_netfs_sreq(&rdata->subreq, netfs_sreq_trace_io_req_submitted); + goto do_retry; case MID_RETRY_NEEDED: + trace_netfs_sreq(&rdata->subreq, netfs_sreq_trace_io_retry_needed); +do_retry: + __set_bit(NETFS_SREQ_NEED_RETRY, &rdata->subreq.flags); rdata->result = -EAGAIN; if (server->sign && rdata->got_bytes) /* reset bytes number since we can not check a sign */ @@ -4547,15 +4646,20 @@ smb2_readv_callback(struct mid_q_entry *mid) cifs_stats_bytes_read(tcon, rdata->got_bytes); break; case MID_RESPONSE_MALFORMED: + trace_netfs_sreq(&rdata->subreq, netfs_sreq_trace_io_malformed); credits.value = le16_to_cpu(shdr->CreditRequest); credits.instance = server->reconnect_instance; - fallthrough; + rdata->result = smb_EIO(smb_eio_trace_read_rsp_malformed); + break; default: - rdata->result = -EIO; + trace_netfs_sreq(&rdata->subreq, netfs_sreq_trace_io_unknown); + rdata->result = smb_EIO1(smb_eio_trace_read_mid_state_unknown, + mid->mid_state); + break; } #ifdef CONFIG_CIFS_SMB_DIRECT /* - * If this rdata has a memmory registered, the MR can be freed + * If this rdata has a memory registered, the MR can be freed * MR needs to be freed as soon as I/O finishes to prevent deadlock * because they have limited number and are used for future I/Os */ @@ -4570,27 +4674,44 @@ smb2_readv_callback(struct mid_q_entry *mid) rdata->subreq.debug_index, rdata->xid, rdata->req->cfile->fid.persistent_fid, - tcon->tid, tcon->ses->Suid, rdata->subreq.start, - rdata->subreq.len, rdata->result); + tcon->tid, tcon->ses->Suid, + rdata->subreq.start + rdata->subreq.transferred, + rdata->subreq.len - rdata->subreq.transferred, + rdata->result); } else trace_smb3_read_done(rdata->rreq->debug_id, rdata->subreq.debug_index, rdata->xid, rdata->req->cfile->fid.persistent_fid, tcon->tid, tcon->ses->Suid, - rdata->subreq.start, rdata->got_bytes); + rdata->subreq.start + rdata->subreq.transferred, + rdata->got_bytes); if (rdata->result == -ENODATA) { - /* We may have got an EOF error because fallocate - * failed to enlarge the file. - */ - if (rdata->subreq.start < rdata->subreq.rreq->i_size) + __set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags); + rdata->result = 0; + } else { + size_t trans = rdata->subreq.transferred + rdata->got_bytes; + if (trans < rdata->subreq.len && + rdata->subreq.start + trans >= ictx->remote_i_size) { + __set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags); rdata->result = 0; + } + if (rdata->got_bytes) + __set_bit(NETFS_SREQ_MADE_PROGRESS, &rdata->subreq.flags); } + trace_smb3_rw_credits(rreq_debug_id, subreq_debug_index, rdata->credits.value, + server->credits, server->in_flight, + 0, cifs_trace_rw_credits_read_response_clear); rdata->credits.value = 0; - INIT_WORK(&rdata->subreq.work, smb2_readv_worker); - queue_work(cifsiod_wq, &rdata->subreq.work); - release_mid(mid); + rdata->subreq.error = rdata->result; + rdata->subreq.transferred += rdata->got_bytes; + trace_netfs_sreq(&rdata->subreq, netfs_sreq_trace_io_progress); + netfs_read_subreq_terminated(&rdata->subreq); + release_mid(server, mid); + trace_smb3_rw_credits(rreq_debug_id, subreq_debug_index, 0, + server->credits, server->in_flight, + credits.value, cifs_trace_rw_credits_read_response_add); add_credits(server, &credits, 0); } @@ -4600,6 +4721,7 @@ smb2_async_readv(struct cifs_io_subrequest *rdata) { int rc, flags = 0; char *buf; + struct netfs_io_subrequest *subreq = &rdata->subreq; struct smb2_hdr *shdr; struct cifs_io_parms io_parms; struct smb_rqst rqst = { .rq_iov = rdata->iov, @@ -4610,15 +4732,15 @@ smb2_async_readv(struct cifs_io_subrequest *rdata) int credit_request; cifs_dbg(FYI, "%s: offset=%llu bytes=%zu\n", - __func__, rdata->subreq.start, rdata->subreq.len); + __func__, subreq->start, subreq->len); if (!rdata->server) rdata->server = cifs_pick_channel(tcon->ses); io_parms.tcon = tlink_tcon(rdata->req->cfile->tlink); io_parms.server = server = rdata->server; - io_parms.offset = rdata->subreq.start; - io_parms.length = rdata->subreq.len; + io_parms.offset = subreq->start + subreq->transferred; + io_parms.length = subreq->len - subreq->transferred; io_parms.persistent_fid = rdata->req->cfile->fid.persistent_fid; io_parms.volatile_fid = rdata->req->cfile->fid.volatile_fid; io_parms.pid = rdata->req->pid; @@ -4633,11 +4755,13 @@ smb2_async_readv(struct cifs_io_subrequest *rdata) rdata->iov[0].iov_base = buf; rdata->iov[0].iov_len = total_len; + rdata->got_bytes = 0; + rdata->result = 0; shdr = (struct smb2_hdr *)buf; if (rdata->credits.value > 0) { - shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->subreq.len, + shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(io_parms.length, SMB2_MAX_BUFFER_SIZE)); credit_request = le16_to_cpu(shdr->CreditCharge) + 8; if (server->credits >= server->max_credits) @@ -4647,7 +4771,7 @@ smb2_async_readv(struct cifs_io_subrequest *rdata) min_t(int, server->max_credits - server->credits, credit_request)); - rc = adjust_credits(server, &rdata->credits, rdata->subreq.len); + rc = adjust_credits(server, rdata, cifs_trace_rw_credits_call_readv_adjust); if (rc) goto async_readv_out; @@ -4661,11 +4785,12 @@ smb2_async_readv(struct cifs_io_subrequest *rdata) if (rc) { cifs_stats_fail_inc(io_parms.tcon, SMB2_READ_HE); trace_smb3_read_err(rdata->rreq->debug_id, - rdata->subreq.debug_index, + subreq->debug_index, rdata->xid, io_parms.persistent_fid, io_parms.tcon->tid, io_parms.tcon->ses->Suid, - io_parms.offset, io_parms.length, rc); + io_parms.offset, + subreq->len - subreq->transferred, rc); } async_readv_out: @@ -4738,7 +4863,8 @@ SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms, (*nbytes > io_parms->length)) { cifs_dbg(FYI, "bad length %d for count %d\n", *nbytes, io_parms->length); - rc = -EIO; + rc = smb_EIO2(smb_eio_trace_read_overlarge, + *nbytes, io_parms->length); *nbytes = 0; } @@ -4760,27 +4886,36 @@ SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms, * workqueue completion task. */ static void -smb2_writev_callback(struct mid_q_entry *mid) +smb2_writev_callback(struct TCP_Server_Info *server, struct mid_q_entry *mid) { struct cifs_io_subrequest *wdata = mid->callback_data; struct cifs_tcon *tcon = tlink_tcon(wdata->req->cfile->tlink); - struct TCP_Server_Info *server = wdata->server; struct smb2_write_rsp *rsp = (struct smb2_write_rsp *)mid->resp_buf; - struct cifs_credits credits = { .value = 0, .instance = 0 }; + struct cifs_credits credits = { + .value = 0, + .instance = 0, + .rreq_debug_id = wdata->rreq->debug_id, + .rreq_debug_index = wdata->subreq.debug_index, + }; + unsigned int rreq_debug_id = wdata->rreq->debug_id; + unsigned int subreq_debug_index = wdata->subreq.debug_index; ssize_t result = 0; size_t written; - WARN_ONCE(wdata->server != mid->server, + WARN_ONCE(wdata->server != server, "wdata server %p != mid server %p", - wdata->server, mid->server); + wdata->server, server); switch (mid->mid_state) { case MID_RESPONSE_RECEIVED: + trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_progress); credits.value = le16_to_cpu(rsp->hdr.CreditRequest); credits.instance = server->reconnect_instance; result = smb2_check_receive(mid, server, 0); - if (result != 0) + if (result != 0) { + trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_bad); break; + } written = le32_to_cpu(rsp->DataLength); /* @@ -4792,21 +4927,35 @@ smb2_writev_callback(struct mid_q_entry *mid) if (written > wdata->subreq.len) written &= 0xFFFF; - if (written < wdata->subreq.len) + cifs_stats_bytes_written(tcon, written); + + if (written < wdata->subreq.len) { wdata->result = -ENOSPC; - else + } else if (written > 0) { wdata->subreq.len = written; + __set_bit(NETFS_SREQ_MADE_PROGRESS, &wdata->subreq.flags); + } break; case MID_REQUEST_SUBMITTED: + trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_req_submitted); + __set_bit(NETFS_SREQ_NEED_RETRY, &wdata->subreq.flags); + result = -EAGAIN; + break; case MID_RETRY_NEEDED: + trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_retry_needed); + __set_bit(NETFS_SREQ_NEED_RETRY, &wdata->subreq.flags); result = -EAGAIN; break; case MID_RESPONSE_MALFORMED: + trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_malformed); credits.value = le16_to_cpu(rsp->hdr.CreditRequest); credits.instance = server->reconnect_instance; - fallthrough; + result = smb_EIO(smb_eio_trace_write_rsp_malformed); + break; default: - result = -EIO; + trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_unknown); + result = smb_EIO1(smb_eio_trace_write_mid_state_unknown, + mid->mid_state); break; } #ifdef CONFIG_CIFS_SMB_DIRECT @@ -4824,7 +4973,9 @@ smb2_writev_callback(struct mid_q_entry *mid) #endif if (result) { cifs_stats_fail_inc(tcon, SMB2_WRITE_HE); - trace_smb3_write_err(wdata->xid, + trace_smb3_write_err(wdata->rreq->debug_id, + wdata->subreq.debug_index, + wdata->xid, wdata->req->cfile->fid.persistent_fid, tcon->tid, tcon->ses->Suid, wdata->subreq.start, wdata->subreq.len, wdata->result); @@ -4832,14 +4983,22 @@ smb2_writev_callback(struct mid_q_entry *mid) pr_warn_once("Out of space writing to %s\n", tcon->tree_name); } else - trace_smb3_write_done(0 /* no xid */, + trace_smb3_write_done(wdata->rreq->debug_id, + wdata->subreq.debug_index, + wdata->xid, wdata->req->cfile->fid.persistent_fid, tcon->tid, tcon->ses->Suid, wdata->subreq.start, wdata->subreq.len); + trace_smb3_rw_credits(rreq_debug_id, subreq_debug_index, wdata->credits.value, + server->credits, server->in_flight, + 0, cifs_trace_rw_credits_write_response_clear); wdata->credits.value = 0; - cifs_write_subrequest_terminated(wdata, result ?: written, true); - release_mid(mid); + cifs_write_subrequest_terminated(wdata, result ?: written); + release_mid(server, mid); + trace_smb3_rw_credits(rreq_debug_id, subreq_debug_index, 0, + server->credits, server->in_flight, + credits.value, cifs_trace_rw_credits_write_response_add); add_credits(server, &credits, 0); } @@ -4859,9 +5018,6 @@ smb2_async_writev(struct cifs_io_subrequest *wdata) struct cifs_io_parms *io_parms = NULL; int credit_request; - if (!wdata->server || test_bit(NETFS_SREQ_RETRYING, &wdata->subreq.flags)) - server = wdata->server = cifs_pick_channel(tcon->ses); - /* * in future we may get cifs_io_parms passed in from the caller, * but for now we construct it here... @@ -4882,6 +5038,13 @@ smb2_async_writev(struct cifs_io_subrequest *wdata) if (rc) goto out; + rqst.rq_iov = iov; + rqst.rq_iter = wdata->subreq.io_iter; + + rqst.rq_iov[0].iov_len = total_len - 1; + rqst.rq_iov[0].iov_base = (char *)req; + rqst.rq_nvec += 1; + if (smb3_encryption_required(tcon)) flags |= CIFS_TRANSFORM_REQ; @@ -4893,12 +5056,15 @@ smb2_async_writev(struct cifs_io_subrequest *wdata) req->WriteChannelInfoOffset = 0; req->WriteChannelInfoLength = 0; req->Channel = SMB2_CHANNEL_NONE; + req->Length = cpu_to_le32(io_parms->length); req->Offset = cpu_to_le64(io_parms->offset); req->DataOffset = cpu_to_le16( offsetof(struct smb2_write_req, Buffer)); req->RemainingBytes = 0; - trace_smb3_write_enter(wdata->xid, + trace_smb3_write_enter(wdata->rreq->debug_id, + wdata->subreq.debug_index, + wdata->xid, io_parms->persistent_fid, io_parms->tcon->tid, io_parms->tcon->ses->Suid, @@ -4908,11 +5074,10 @@ smb2_async_writev(struct cifs_io_subrequest *wdata) #ifdef CONFIG_CIFS_SMB_DIRECT /* * If we want to do a server RDMA read, fill in and append - * smbd_buffer_descriptor_v1 to the end of write request + * smbdirect_buffer_descriptor_v1 to the end of write request */ if (smb3_use_rdma_offload(io_parms)) { - struct smbd_buffer_descriptor_v1 *v1; - size_t data_size = iov_iter_count(&wdata->subreq.io_iter); + struct smbdirect_buffer_descriptor_v1 *v1; bool need_invalidate = server->dialect == SMB30_PROT_ID; wdata->mr = smbd_register_mr(server->smbd_conn, &wdata->subreq.io_iter, @@ -4921,45 +5086,37 @@ smb2_async_writev(struct cifs_io_subrequest *wdata) rc = -EAGAIN; goto async_writev_out; } + /* For RDMA read, I/O size is in RemainingBytes not in Length */ + req->RemainingBytes = req->Length; req->Length = 0; req->DataOffset = 0; - req->RemainingBytes = cpu_to_le32(data_size); req->Channel = SMB2_CHANNEL_RDMA_V1_INVALIDATE; if (need_invalidate) req->Channel = SMB2_CHANNEL_RDMA_V1; req->WriteChannelInfoOffset = cpu_to_le16(offsetof(struct smb2_write_req, Buffer)); req->WriteChannelInfoLength = - cpu_to_le16(sizeof(struct smbd_buffer_descriptor_v1)); - v1 = (struct smbd_buffer_descriptor_v1 *) &req->Buffer[0]; + cpu_to_le16(sizeof(struct smbdirect_buffer_descriptor_v1)); + v1 = (struct smbdirect_buffer_descriptor_v1 *) &req->Buffer[0]; v1->offset = cpu_to_le64(wdata->mr->mr->iova); v1->token = cpu_to_le32(wdata->mr->mr->rkey); v1->length = cpu_to_le32(wdata->mr->mr->length); + + rqst.rq_iov[0].iov_len += sizeof(*v1); + + /* + * We keep wdata->subreq.io_iter, + * but we have to truncate rqst.rq_iter + */ + iov_iter_truncate(&rqst.rq_iter, 0); } #endif - iov[0].iov_len = total_len - 1; - iov[0].iov_base = (char *)req; - rqst.rq_iov = iov; - rqst.rq_nvec = 1; - rqst.rq_iter = wdata->subreq.io_iter; - rqst.rq_iter_size = iov_iter_count(&rqst.rq_iter); - if (test_bit(NETFS_SREQ_RETRYING, &wdata->subreq.flags)) + if (wdata->subreq.retry_count > 0) smb2_set_replay(server, &rqst); -#ifdef CONFIG_CIFS_SMB_DIRECT - if (wdata->mr) - iov[0].iov_len += sizeof(struct smbd_buffer_descriptor_v1); -#endif - cifs_dbg(FYI, "async write at %llu %u bytes iter=%zx\n", - io_parms->offset, io_parms->length, iov_iter_count(&rqst.rq_iter)); -#ifdef CONFIG_CIFS_SMB_DIRECT - /* For RDMA read, I/O size is in RemainingBytes not in Length */ - if (!wdata->mr) - req->Length = cpu_to_le32(io_parms->length); -#else - req->Length = cpu_to_le32(io_parms->length); -#endif + cifs_dbg(FYI, "async write at %llu %u bytes iter=%zx\n", + io_parms->offset, io_parms->length, iov_iter_count(&wdata->subreq.io_iter)); if (wdata->credits.value > 0) { shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(wdata->subreq.len, @@ -4972,18 +5129,24 @@ smb2_async_writev(struct cifs_io_subrequest *wdata) min_t(int, server->max_credits - server->credits, credit_request)); - rc = adjust_credits(server, &wdata->credits, io_parms->length); + rc = adjust_credits(server, wdata, cifs_trace_rw_credits_call_writev_adjust); if (rc) goto async_writev_out; flags |= CIFS_HAS_CREDITS; } + /* XXX: compression + encryption is unsupported for now */ + if (((flags & CIFS_TRANSFORM_REQ) != CIFS_TRANSFORM_REQ) && should_compress(tcon, &rqst)) + flags |= CIFS_COMPRESS_REQ; + rc = cifs_call_async(server, &rqst, NULL, smb2_writev_callback, NULL, wdata, flags, &wdata->credits); /* Can't touch wdata if rc == 0 */ if (rc) { - trace_smb3_write_err(xid, + trace_smb3_write_err(wdata->rreq->debug_id, + wdata->subreq.debug_index, + xid, io_parms->persistent_fid, io_parms->tcon->tid, io_parms->tcon->ses->Suid, @@ -4997,8 +5160,14 @@ async_writev_out: cifs_small_buf_release(req); out: if (rc) { + trace_smb3_rw_credits(wdata->rreq->debug_id, + wdata->subreq.debug_index, + wdata->credits.value, + server->credits, server->in_flight, + -(int)wdata->credits.value, + cifs_trace_rw_credits_write_response_clear); add_credits_and_wake_if(wdata->server, &wdata->credits, 0); - cifs_write_subrequest_terminated(wdata, rc, true); + cifs_write_subrequest_terminated(wdata, rc); } } @@ -5057,7 +5226,7 @@ replay_again: offsetof(struct smb2_write_req, Buffer)); req->RemainingBytes = 0; - trace_smb3_write_enter(xid, io_parms->persistent_fid, + trace_smb3_write_enter(0, 0, xid, io_parms->persistent_fid, io_parms->tcon->tid, io_parms->tcon->ses->Suid, io_parms->offset, io_parms->length); @@ -5078,7 +5247,7 @@ replay_again: rsp = (struct smb2_write_rsp *)rsp_iov.iov_base; if (rc) { - trace_smb3_write_err(xid, + trace_smb3_write_err(0, 0, xid, req->PersistentFileId, io_parms->tcon->tid, io_parms->tcon->ses->Suid, @@ -5087,7 +5256,8 @@ replay_again: cifs_dbg(VFS, "Send error in write = %d\n", rc); } else { *nbytes = le32_to_cpu(rsp->DataLength); - trace_smb3_write_done(xid, + cifs_stats_bytes_written(io_parms->tcon, *nbytes); + trace_smb3_write_done(0, 0, xid, req->PersistentFileId, io_parms->tcon->tid, io_parms->tcon->ses->Suid, @@ -5349,7 +5519,7 @@ smb2_parse_query_directory(struct cifs_tcon *tcon, info_buf_size = sizeof(FILE_DIRECTORY_INFO); break; case SMB_FIND_FILE_ID_FULL_DIR_INFO: - info_buf_size = sizeof(SEARCH_ID_FULL_DIR_INFO); + info_buf_size = sizeof(FILE_ID_FULL_DIR_INFO); break; case SMB_FIND_FILE_POSIX_INFO: /* note that posix payload are variable size */ @@ -5428,7 +5598,7 @@ replay_again: server = cifs_pick_channel(ses); if (!ses || !(ses->server)) - return -EIO; + return smb_EIO(smb_eio_trace_null_pointers); if (smb3_encryption_required(tcon)) flags |= CIFS_TRANSFORM_REQ; @@ -5563,7 +5733,7 @@ replay_again: server = cifs_pick_channel(ses); if (!ses || !server) - return -EIO; + return smb_EIO(smb_eio_trace_null_pointers); if (!num) return -EINVAL; @@ -5636,7 +5806,7 @@ SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, int SMB2_set_acl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, - struct cifs_ntsd *pnntsd, int pacllen, int aclflag) + struct smb_ntsd *pnntsd, int pacllen, int aclflag) { return send_set_info(xid, tcon, persistent_fid, volatile_fid, current->tgid, 0, SMB2_O_INFO_SECURITY, aclflag, @@ -5760,7 +5930,7 @@ build_qfs_info_req(struct kvec *iov, struct cifs_tcon *tcon, cifs_dbg(FYI, "Query FSInfo level %d\n", level); if ((tcon->ses == NULL) || server == NULL) - return -EIO; + return smb_EIO(smb_eio_trace_null_pointers); rc = smb2_plain_req_init(SMB2_QUERY_INFO, tcon, server, (void **) &req, &total_len); @@ -5853,71 +6023,6 @@ posix_qfsinf_exit: } int -SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata) -{ - struct smb_rqst rqst; - struct smb2_query_info_rsp *rsp = NULL; - struct kvec iov; - struct kvec rsp_iov; - int rc = 0; - int resp_buftype; - struct cifs_ses *ses = tcon->ses; - struct TCP_Server_Info *server; - struct smb2_fs_full_size_info *info = NULL; - int flags = 0; - int retries = 0, cur_sleep = 1; - -replay_again: - /* reinitialize for possible replay */ - flags = 0; - server = cifs_pick_channel(ses); - - rc = build_qfs_info_req(&iov, tcon, server, - FS_FULL_SIZE_INFORMATION, - sizeof(struct smb2_fs_full_size_info), - persistent_fid, volatile_fid); - if (rc) - return rc; - - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov = &iov; - rqst.rq_nvec = 1; - - if (retries) - smb2_set_replay(server, &rqst); - - rc = cifs_send_recv(xid, ses, server, - &rqst, &resp_buftype, flags, &rsp_iov); - free_qfs_info_req(&iov); - if (rc) { - cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); - goto qfsinf_exit; - } - rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; - - info = (struct smb2_fs_full_size_info *)( - le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp); - rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), - le32_to_cpu(rsp->OutputBufferLength), &rsp_iov, - sizeof(struct smb2_fs_full_size_info)); - if (!rc) - smb2_copy_fs_info_to_kstatfs(info, fsdata); - -qfsinf_exit: - free_rsp_buf(resp_buftype, rsp_iov.iov_base); - - if (is_replayable_error(rc) && - smb2_should_replay(tcon, &retries, &cur_sleep)) - goto replay_again; - - return rc; -} - -int SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, int level) { @@ -5942,8 +6047,8 @@ replay_again: max_len = sizeof(FILE_SYSTEM_DEVICE_INFO); min_len = sizeof(FILE_SYSTEM_DEVICE_INFO); } else if (level == FS_ATTRIBUTE_INFORMATION) { - max_len = sizeof(FILE_SYSTEM_ATTRIBUTE_INFO); - min_len = MIN_FS_ATTR_INFO_SIZE; + max_len = sizeof(FILE_SYSTEM_ATTRIBUTE_INFO) + MAX_FS_NAME_LEN; + min_len = sizeof(FILE_SYSTEM_ATTRIBUTE_INFO); } else if (level == FS_SECTOR_SIZE_INFORMATION) { max_len = sizeof(struct smb3_fs_ss_info); min_len = sizeof(struct smb3_fs_ss_info); @@ -5989,7 +6094,7 @@ replay_again: if (level == FS_ATTRIBUTE_INFORMATION) memcpy(&tcon->fsAttrInfo, offset + (char *)rsp, min_t(unsigned int, - rsp_len, max_len)); + rsp_len, min_len)); else if (level == FS_DEVICE_INFORMATION) memcpy(&tcon->fsDevInfo, offset + (char *)rsp, sizeof(FILE_SYSTEM_DEVICE_INFO)); @@ -6135,7 +6240,7 @@ SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon, req->StructureSize = cpu_to_le16(36); total_len += 12; - memcpy(req->LeaseKey, lease_key, 16); + memcpy(req->LeaseKey, lease_key, SMB2_LEASE_KEY_SIZE); req->LeaseState = lease_state; flags |= CIFS_NO_RSP_BUF; @@ -6155,11 +6260,11 @@ SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon, please_key_high = (__u64 *)(lease_key+8); if (rc) { cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE); - trace_smb3_lease_err(le32_to_cpu(lease_state), tcon->tid, + trace_smb3_lease_ack_err(le32_to_cpu(lease_state), tcon->tid, ses->Suid, *please_key_low, *please_key_high, rc); cifs_dbg(FYI, "Send error in Lease Break = %d\n", rc); } else - trace_smb3_lease_done(le32_to_cpu(lease_state), tcon->tid, + trace_smb3_lease_ack_done(le32_to_cpu(lease_state), tcon->tid, ses->Suid, *please_key_low, *please_key_high); return rc; |
