diff options
Diffstat (limited to 'fs/cifs/smb2pdu.c')
-rw-r--r-- | fs/cifs/smb2pdu.c | 786 |
1 files changed, 580 insertions, 206 deletions
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 31e4a1b0b170..1234f9ccab03 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -252,7 +252,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon) if (tcon == NULL) return 0; - if (smb2_command == SMB2_TREE_CONNECT || smb2_command == SMB2_IOCTL) + if (smb2_command == SMB2_TREE_CONNECT) return 0; if (tcon->tidStatus == CifsExiting) { @@ -312,7 +312,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon) if (server->tcpStatus != CifsNeedReconnect) break; - if (--retries) + if (retries && --retries) continue; /* @@ -350,9 +350,14 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon) } rc = cifs_negotiate_protocol(0, tcon->ses); - if (!rc && tcon->ses->need_reconnect) + if (!rc && tcon->ses->need_reconnect) { rc = cifs_setup_session(0, tcon->ses, nls_codepage); - + if ((rc == -EACCES) && !tcon->retry) { + rc = -EHOSTDOWN; + mutex_unlock(&tcon->ses->session_mutex); + goto failed; + } + } if (rc || !tcon->need_reconnect) { mutex_unlock(&tcon->ses->session_mutex); goto out; @@ -397,6 +402,7 @@ out: case SMB2_SET_INFO: rc = -EAGAIN; } +failed: unload_nls(nls_codepage); return rc; } @@ -426,16 +432,9 @@ fill_small_buf(__le16 smb2_command, struct cifs_tcon *tcon, void *buf, * SMB information in the SMB header. If the return code is zero, this * function must have filled in request_buf pointer. */ -static int -smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon, - void **request_buf, unsigned int *total_len) +static int __smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon, + void **request_buf, unsigned int *total_len) { - int rc; - - rc = smb2_reconnect(smb2_command, tcon); - if (rc) - return rc; - /* BB eventually switch this to SMB2 specific small buf size */ if (smb2_command == SMB2_SET_INFO) *request_buf = cifs_buf_get(); @@ -456,7 +455,31 @@ smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon, cifs_stats_inc(&tcon->num_smbs_sent); } - return rc; + return 0; +} + +static int smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon, + void **request_buf, unsigned int *total_len) +{ + int rc; + + rc = smb2_reconnect(smb2_command, tcon); + if (rc) + return rc; + + return __smb2_plain_req_init(smb2_command, tcon, request_buf, + total_len); +} + +static int smb2_ioctl_req_init(u32 opcode, struct cifs_tcon *tcon, + void **request_buf, unsigned int *total_len) +{ + /* Skip reconnect only for FSCTL_VALIDATE_NEGOTIATE_INFO IOCTLs */ + if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO) { + return __smb2_plain_req_init(SMB2_IOCTL, tcon, request_buf, + total_len); + } + return smb2_plain_req_init(SMB2_IOCTL, tcon, request_buf, total_len); } /* For explanation of negotiate contexts see MS-SMB2 section 2.2.3.1 */ @@ -503,8 +526,7 @@ build_netname_ctxt(struct smb2_netname_neg_context *pneg_ctxt, char *hostname) pneg_ctxt->ContextType = SMB2_NETNAME_NEGOTIATE_CONTEXT_ID; /* copy up to max of first 100 bytes of server name to NetName field */ - pneg_ctxt->DataLength = cpu_to_le16(2 + - (2 * cifs_strtoUTF16(pneg_ctxt->NetName, hostname, 100, cp))); + pneg_ctxt->DataLength = cpu_to_le16(2 * cifs_strtoUTF16(pneg_ctxt->NetName, hostname, 100, cp)); /* context size is DataLength + minimal smb2_neg_context */ return DIV_ROUND_UP(le16_to_cpu(pneg_ctxt->DataLength) + sizeof(struct smb2_neg_context), 8) * 8; @@ -538,12 +560,12 @@ static void assemble_neg_contexts(struct smb2_negotiate_req *req, struct TCP_Server_Info *server, unsigned int *total_len) { - char *pneg_ctxt = (char *)req; + char *pneg_ctxt; unsigned int ctxt_len; if (*total_len > 200) { /* In case length corrupted don't want to overrun smb buffer */ - cifs_dbg(VFS, "Bad frame length assembling neg contexts\n"); + cifs_server_dbg(VFS, "Bad frame length assembling neg contexts\n"); return; } @@ -661,7 +683,7 @@ static int smb311_decode_neg_context(struct smb2_negotiate_rsp *rsp, cifs_dbg(FYI, "decoding %d negotiate contexts\n", ctxt_cnt); if (len_of_smb <= offset) { - cifs_dbg(VFS, "Invalid response: negotiate context offset\n"); + cifs_server_dbg(VFS, "Invalid response: negotiate context offset\n"); return -EINVAL; } @@ -693,7 +715,7 @@ static int smb311_decode_neg_context(struct smb2_negotiate_rsp *rsp, else if (pctx->ContextType == SMB2_POSIX_EXTENSIONS_AVAILABLE) server->posix_ext_supported = true; else - cifs_dbg(VFS, "unknown negcontext of type %d ignored\n", + cifs_server_dbg(VFS, "unknown negcontext of type %d ignored\n", le16_to_cpu(pctx->ContextType)); if (rc) @@ -752,6 +774,8 @@ add_posix_context(struct kvec *iov, unsigned int *num_iovec, umode_t mode) unsigned int num = *num_iovec; iov[num].iov_base = create_posix_buf(mode); + if (mode == ACL_NO_MODE) + cifs_dbg(FYI, "illegal mode\n"); if (iov[num].iov_base == NULL) return -ENOMEM; iov[num].iov_len = sizeof(struct create_posix); @@ -790,7 +814,7 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) struct kvec rsp_iov; int rc = 0; int resp_buftype; - struct TCP_Server_Info *server = ses->server; + struct TCP_Server_Info *server = cifs_ses_server(ses); int blob_offset, blob_length; char *security_blob; int flags = CIFS_NEG_OP; @@ -812,13 +836,13 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) memset(server->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE); memset(ses->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE); - if (strcmp(ses->server->vals->version_string, + if (strcmp(server->vals->version_string, SMB3ANY_VERSION_STRING) == 0) { req->Dialects[0] = cpu_to_le16(SMB30_PROT_ID); req->Dialects[1] = cpu_to_le16(SMB302_PROT_ID); req->DialectCount = cpu_to_le16(2); total_len += 4; - } else if (strcmp(ses->server->vals->version_string, + } else if (strcmp(server->vals->version_string, SMBDEFAULT_VERSION_STRING) == 0) { req->Dialects[0] = cpu_to_le16(SMB21_PROT_ID); req->Dialects[1] = cpu_to_le16(SMB30_PROT_ID); @@ -828,7 +852,7 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) total_len += 8; } else { /* otherwise send specific dialect */ - req->Dialects[0] = cpu_to_le16(ses->server->vals->protocol_id); + req->Dialects[0] = cpu_to_le16(server->vals->protocol_id); req->DialectCount = cpu_to_le16(1); total_len += 2; } @@ -841,16 +865,16 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) else req->SecurityMode = 0; - req->Capabilities = cpu_to_le32(ses->server->vals->req_capabilities); + req->Capabilities = cpu_to_le32(server->vals->req_capabilities); /* ClientGUID must be zero for SMB2.02 dialect */ - if (ses->server->vals->protocol_id == SMB20_PROT_ID) + if (server->vals->protocol_id == SMB20_PROT_ID) memset(req->ClientGUID, 0, SMB2_CLIENT_GUID_SIZE); else { memcpy(req->ClientGUID, server->client_guid, SMB2_CLIENT_GUID_SIZE); - if ((ses->server->vals->protocol_id == SMB311_PROT_ID) || - (strcmp(ses->server->vals->version_string, + if ((server->vals->protocol_id == SMB311_PROT_ID) || + (strcmp(server->vals->version_string, SMBDEFAULT_VERSION_STRING) == 0)) assemble_neg_contexts(req, server, &total_len); } @@ -869,42 +893,42 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]); */ if (rc == -EOPNOTSUPP) { - cifs_dbg(VFS, "Dialect not supported by server. Consider " + cifs_server_dbg(VFS, "Dialect not supported by server. Consider " "specifying vers=1.0 or vers=2.0 on mount for accessing" " older servers\n"); goto neg_exit; } else if (rc != 0) goto neg_exit; - if (strcmp(ses->server->vals->version_string, + if (strcmp(server->vals->version_string, SMB3ANY_VERSION_STRING) == 0) { if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) { - cifs_dbg(VFS, + cifs_server_dbg(VFS, "SMB2 dialect returned but not requested\n"); return -EIO; } else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) { - cifs_dbg(VFS, + cifs_server_dbg(VFS, "SMB2.1 dialect returned but not requested\n"); return -EIO; } - } else if (strcmp(ses->server->vals->version_string, + } else if (strcmp(server->vals->version_string, SMBDEFAULT_VERSION_STRING) == 0) { if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) { - cifs_dbg(VFS, + cifs_server_dbg(VFS, "SMB2 dialect returned but not requested\n"); return -EIO; } else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) { /* ops set to 3.0 by default for default so update */ - ses->server->ops = &smb21_operations; - ses->server->vals = &smb21_values; + server->ops = &smb21_operations; + server->vals = &smb21_values; } else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) { - ses->server->ops = &smb311_operations; - ses->server->vals = &smb311_values; + server->ops = &smb311_operations; + server->vals = &smb311_values; } } else if (le16_to_cpu(rsp->DialectRevision) != - ses->server->vals->protocol_id) { + server->vals->protocol_id) { /* if requested single dialect ensure returned dialect matched */ - cifs_dbg(VFS, "Illegal 0x%x dialect returned: not requested\n", + cifs_server_dbg(VFS, "Illegal 0x%x dialect returned: not requested\n", le16_to_cpu(rsp->DialectRevision)); return -EIO; } @@ -922,7 +946,7 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) cifs_dbg(FYI, "negotiated smb3.1.1 dialect\n"); else { - cifs_dbg(VFS, "Illegal dialect returned by server 0x%x\n", + cifs_server_dbg(VFS, "Illegal dialect returned by server 0x%x\n", le16_to_cpu(rsp->DialectRevision)); rc = -EIO; goto neg_exit; @@ -982,7 +1006,7 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) rc = smb311_decode_neg_context(rsp, server, rsp_iov.iov_len); else - cifs_dbg(VFS, "Missing expected negotiate contexts\n"); + cifs_server_dbg(VFS, "Missing expected negotiate contexts\n"); } neg_exit: free_rsp_buf(resp_buftype, rsp); @@ -996,11 +1020,12 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) struct validate_negotiate_info_rsp *pneg_rsp = NULL; u32 rsplen; u32 inbuflen; /* max of 4 dialects */ + struct TCP_Server_Info *server = tcon->ses->server; cifs_dbg(FYI, "validate negotiate\n"); /* In SMB3.11 preauth integrity supersedes validate negotiate */ - if (tcon->ses->server->dialect == SMB311_PROT_ID) + if (server->dialect == SMB311_PROT_ID) return 0; /* @@ -1019,15 +1044,15 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) } if (tcon->ses->session_flags & SMB2_SESSION_FLAG_IS_NULL) - cifs_dbg(VFS, "Unexpected null user (anonymous) auth flag sent by server\n"); + cifs_tcon_dbg(VFS, "Unexpected null user (anonymous) auth flag sent by server\n"); pneg_inbuf = kmalloc(sizeof(*pneg_inbuf), GFP_NOFS); if (!pneg_inbuf) return -ENOMEM; pneg_inbuf->Capabilities = - cpu_to_le32(tcon->ses->server->vals->req_capabilities); - memcpy(pneg_inbuf->Guid, tcon->ses->server->client_guid, + cpu_to_le32(server->vals->req_capabilities); + memcpy(pneg_inbuf->Guid, server->client_guid, SMB2_CLIENT_GUID_SIZE); if (tcon->ses->sign) @@ -1040,7 +1065,7 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) pneg_inbuf->SecurityMode = 0; - if (strcmp(tcon->ses->server->vals->version_string, + if (strcmp(server->vals->version_string, SMB3ANY_VERSION_STRING) == 0) { pneg_inbuf->Dialects[0] = cpu_to_le16(SMB30_PROT_ID); pneg_inbuf->Dialects[1] = cpu_to_le16(SMB302_PROT_ID); @@ -1048,7 +1073,7 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) /* structure is big enough for 3 dialects, sending only 2 */ inbuflen = sizeof(*pneg_inbuf) - (2 * sizeof(pneg_inbuf->Dialects[0])); - } else if (strcmp(tcon->ses->server->vals->version_string, + } else if (strcmp(server->vals->version_string, SMBDEFAULT_VERSION_STRING) == 0) { pneg_inbuf->Dialects[0] = cpu_to_le16(SMB21_PROT_ID); pneg_inbuf->Dialects[1] = cpu_to_le16(SMB30_PROT_ID); @@ -1060,7 +1085,7 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) } else { /* otherwise specific dialect was requested */ pneg_inbuf->Dialects[0] = - cpu_to_le16(tcon->ses->server->vals->protocol_id); + cpu_to_le16(server->vals->protocol_id); pneg_inbuf->DialectCount = cpu_to_le16(1); /* structure is big enough for 3 dialects, sending only 1 */ inbuflen = sizeof(*pneg_inbuf) - @@ -1076,18 +1101,18 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) * Old Windows versions or Netapp SMB server can return * not supported error. Client should accept it. */ - cifs_dbg(VFS, "Server does not support validate negotiate\n"); + cifs_tcon_dbg(VFS, "Server does not support validate negotiate\n"); rc = 0; goto out_free_inbuf; } else if (rc != 0) { - cifs_dbg(VFS, "validate protocol negotiate failed: %d\n", rc); + cifs_tcon_dbg(VFS, "validate protocol negotiate failed: %d\n", rc); rc = -EIO; goto out_free_inbuf; } rc = -EIO; if (rsplen != sizeof(*pneg_rsp)) { - cifs_dbg(VFS, "invalid protocol negotiate response size: %d\n", + cifs_tcon_dbg(VFS, "invalid protocol negotiate response size: %d\n", rsplen); /* relax check since Mac returns max bufsize allowed on ioctl */ @@ -1096,16 +1121,16 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) } /* check validate negotiate info response matches what we got earlier */ - if (pneg_rsp->Dialect != cpu_to_le16(tcon->ses->server->dialect)) + if (pneg_rsp->Dialect != cpu_to_le16(server->dialect)) goto vneg_out; - if (pneg_rsp->SecurityMode != cpu_to_le16(tcon->ses->server->sec_mode)) + if (pneg_rsp->SecurityMode != cpu_to_le16(server->sec_mode)) goto vneg_out; /* do not validate server guid because not saved at negprot time yet */ if ((le32_to_cpu(pneg_rsp->Capabilities) | SMB2_NT_FIND | - SMB2_LARGE_FILES) != tcon->ses->server->capabilities) + SMB2_LARGE_FILES) != server->capabilities) goto vneg_out; /* validate negotiate successful */ @@ -1114,7 +1139,7 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) goto out_free_rsp; vneg_out: - cifs_dbg(VFS, "protocol revalidation - security settings mismatch\n"); + cifs_tcon_dbg(VFS, "protocol revalidation - security settings mismatch\n"); out_free_rsp: kfree(pneg_rsp); out_free_inbuf: @@ -1169,7 +1194,7 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data) int rc; struct cifs_ses *ses = sess_data->ses; struct smb2_sess_setup_req *req; - struct TCP_Server_Info *server = ses->server; + struct TCP_Server_Info *server = cifs_ses_server(ses); unsigned int total_len; rc = smb2_plain_req_init(SMB2_SESSION_SETUP, NULL, (void **) &req, @@ -1177,13 +1202,21 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data) if (rc) return rc; - /* First session, not a reauthenticate */ - req->sync_hdr.SessionId = 0; - - /* if reconnect, we need to send previous sess id, otherwise it is 0 */ - req->PreviousSessionId = sess_data->previous_session; - - req->Flags = 0; /* MBZ */ + if (sess_data->ses->binding) { + req->sync_hdr.SessionId = sess_data->ses->Suid; + req->sync_hdr.Flags |= SMB2_FLAGS_SIGNED; + req->PreviousSessionId = 0; + req->Flags = SMB2_SESSION_REQ_FLAG_BINDING; + } else { + /* First session, not a reauthenticate */ + req->sync_hdr.SessionId = 0; + /* + * if reconnect, we need to send previous sess id + * otherwise it is 0 + */ + req->PreviousSessionId = sess_data->previous_session; + req->Flags = 0; /* MBZ */ + } /* enough to enable echos and oplocks and one max size write */ req->sync_hdr.CreditRequest = cpu_to_le16(130); @@ -1256,28 +1289,33 @@ SMB2_sess_establish_session(struct SMB2_sess_data *sess_data) { int rc = 0; struct cifs_ses *ses = sess_data->ses; + struct TCP_Server_Info *server = cifs_ses_server(ses); - mutex_lock(&ses->server->srv_mutex); - if (ses->server->ops->generate_signingkey) { - rc = ses->server->ops->generate_signingkey(ses); + mutex_lock(&server->srv_mutex); + if (server->ops->generate_signingkey) { + rc = server->ops->generate_signingkey(ses); if (rc) { cifs_dbg(FYI, "SMB3 session key generation failed\n"); - mutex_unlock(&ses->server->srv_mutex); + mutex_unlock(&server->srv_mutex); return rc; } } - if (!ses->server->session_estab) { - ses->server->sequence_number = 0x2; - ses->server->session_estab = true; + if (!server->session_estab) { + server->sequence_number = 0x2; + server->session_estab = true; } - mutex_unlock(&ses->server->srv_mutex); + mutex_unlock(&server->srv_mutex); cifs_dbg(FYI, "SMB2/3 session established successfully\n"); - spin_lock(&GlobalMid_Lock); - ses->status = CifsGood; - ses->need_reconnect = false; - spin_unlock(&GlobalMid_Lock); + /* keep existing ses state if binding */ + if (!ses->binding) { + spin_lock(&GlobalMid_Lock); + ses->status = CifsGood; + ses->need_reconnect = false; + spin_unlock(&GlobalMid_Lock); + } + return rc; } @@ -1315,16 +1353,19 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data) goto out_put_spnego_key; } - ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len, - GFP_KERNEL); - if (!ses->auth_key.response) { - cifs_dbg(VFS, - "Kerberos can't allocate (%u bytes) memory", - msg->sesskey_len); - rc = -ENOMEM; - goto out_put_spnego_key; + /* keep session key if binding */ + if (!ses->binding) { + ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len, + GFP_KERNEL); + if (!ses->auth_key.response) { + cifs_dbg(VFS, + "Kerberos can't allocate (%u bytes) memory", + msg->sesskey_len); + rc = -ENOMEM; + goto out_put_spnego_key; + } + ses->auth_key.len = msg->sesskey_len; } - ses->auth_key.len = msg->sesskey_len; sess_data->iov[1].iov_base = msg->data + msg->sesskey_len; sess_data->iov[1].iov_len = msg->secblob_len; @@ -1334,9 +1375,11 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data) goto out_put_spnego_key; rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; - ses->Suid = rsp->sync_hdr.SessionId; - - ses->session_flags = le16_to_cpu(rsp->SessionFlags); + /* keep session id and flags if binding */ + if (!ses->binding) { + ses->Suid = rsp->sync_hdr.SessionId; + ses->session_flags = le16_to_cpu(rsp->SessionFlags); + } rc = SMB2_sess_establish_session(sess_data); out_put_spnego_key: @@ -1430,9 +1473,11 @@ SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data) cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n"); - - ses->Suid = rsp->sync_hdr.SessionId; - ses->session_flags = le16_to_cpu(rsp->SessionFlags); + /* keep existing ses id and flags if binding */ + if (!ses->binding) { + ses->Suid = rsp->sync_hdr.SessionId; + ses->session_flags = le16_to_cpu(rsp->SessionFlags); + } out: kfree(ntlmssp_blob); @@ -1489,8 +1534,11 @@ SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data) rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; - ses->Suid = rsp->sync_hdr.SessionId; - ses->session_flags = le16_to_cpu(rsp->SessionFlags); + /* keep existing ses id and flags if binding */ + if (!ses->binding) { + ses->Suid = rsp->sync_hdr.SessionId; + ses->session_flags = le16_to_cpu(rsp->SessionFlags); + } rc = SMB2_sess_establish_session(sess_data); out: @@ -1507,7 +1555,7 @@ SMB2_select_sec(struct cifs_ses *ses, struct SMB2_sess_data *sess_data) { int type; - type = smb2_select_sectype(ses->server, ses->sectype); + type = smb2_select_sectype(cifs_ses_server(ses), ses->sectype); cifs_dbg(FYI, "sess setup type %d\n", type); if (type == Unspecified) { cifs_dbg(VFS, @@ -1535,7 +1583,7 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, const struct nls_table *nls_cp) { int rc = 0; - struct TCP_Server_Info *server = ses->server; + struct TCP_Server_Info *server = cifs_ses_server(ses); struct SMB2_sess_data *sess_data; cifs_dbg(FYI, "Session Setup\n"); @@ -1561,14 +1609,14 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, /* * Initialize the session hash with the server one. */ - memcpy(ses->preauth_sha_hash, ses->server->preauth_sha_hash, + memcpy(ses->preauth_sha_hash, server->preauth_sha_hash, SMB2_PREAUTH_HASH_SIZE); while (sess_data->func) sess_data->func(sess_data); if ((ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST) && (ses->sign)) - cifs_dbg(VFS, "signing requested but authenticated as guest\n"); + cifs_server_dbg(VFS, "signing requested but authenticated as guest\n"); rc = sess_data->result; out: kfree(sess_data); @@ -1661,10 +1709,11 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, __le16 *unc_path = NULL; int flags = 0; unsigned int total_len; + struct TCP_Server_Info *server = ses->server; cifs_dbg(FYI, "TCON\n"); - if (!(ses->server) || !tree) + if (!server || !tree) return -EIO; unc_path = kmalloc(MAX_SHARENAME_LENGTH * 2, GFP_KERNEL); @@ -1707,7 +1756,7 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, * unless it is guest or anonymous user. See MS-SMB2 3.2.5.3.1 * (Samba servers don't always set the flag so also check if null user) */ - if ((ses->server->dialect == SMB311_PROT_ID) && + if ((server->dialect == SMB311_PROT_ID) && !smb3_encryption_required(tcon) && !(ses->session_flags & (SMB2_SESSION_FLAG_IS_GUEST|SMB2_SESSION_FLAG_IS_NULL)) && @@ -1746,7 +1795,7 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, cifs_dbg(FYI, "connection to printer\n"); break; default: - cifs_dbg(VFS, "unknown share type %d\n", rsp->ShareType); + cifs_server_dbg(VFS, "unknown share type %d\n", rsp->ShareType); rc = -EOPNOTSUPP; goto tcon_error_exit; } @@ -1761,15 +1810,15 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, if ((rsp->Capabilities & SMB2_SHARE_CAP_DFS) && ((tcon->share_flags & SHI1005_FLAGS_DFS) == 0)) - cifs_dbg(VFS, "DFS capability contradicts DFS flag\n"); + cifs_tcon_dbg(VFS, "DFS capability contradicts DFS flag\n"); if (tcon->seal && - !(tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) - cifs_dbg(VFS, "Encryption is requested but not supported\n"); + !(server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) + cifs_tcon_dbg(VFS, "Encryption is requested but not supported\n"); init_copy_chunk_defaults(tcon); - if (tcon->ses->server->ops->validate_negotiate) - rc = tcon->ses->server->ops->validate_negotiate(xid, tcon); + if (server->ops->validate_negotiate) + rc = server->ops->validate_negotiate(xid, tcon); tcon_exit: free_rsp_buf(resp_buftype, rsp); @@ -1778,7 +1827,7 @@ tcon_exit: tcon_error_exit: if (rsp && rsp->sync_hdr.Status == STATUS_BAD_NETWORK_NAME) { - cifs_dbg(VFS, "BAD_NETWORK_NAME: %s\n", tree); + cifs_tcon_dbg(VFS, "BAD_NETWORK_NAME: %s\n", tree); } goto tcon_exit; } @@ -1804,6 +1853,8 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon) if ((tcon->need_reconnect) || (tcon->ses->need_reconnect)) return 0; + close_shroot_lease(&tcon->crfid); + rc = smb2_plain_req_init(SMB2_TREE_DISCONNECT, tcon, (void **) &req, &total_len); if (rc) @@ -1888,6 +1939,16 @@ parse_query_id_ctxt(struct create_context *cc, struct smb2_file_all_info *buf) buf->IndexNumber = pdisk_id->DiskFileId; } +static void +parse_posix_ctxt(struct create_context *cc, struct smb_posix_info *pposix_inf) +{ + /* struct smb_posix_info *ppinf = (struct smb_posix_info *)cc; */ + + /* TODO: Need to add parsing for the context and return */ + printk_once(KERN_WARNING + "SMB3 3.11 POSIX response context not completed yet\n"); +} + void smb2_parse_contexts(struct TCP_Server_Info *server, struct smb2_create_rsp *rsp, @@ -1899,6 +1960,9 @@ smb2_parse_contexts(struct TCP_Server_Info *server, unsigned int next; unsigned int remaining; char *name; + const char smb3_create_tag_posix[] = {0x93, 0xAD, 0x25, 0x50, 0x9C, + 0xB4, 0x11, 0xE7, 0xB4, 0x23, 0x83, + 0xDE, 0x96, 0x8B, 0xCD, 0x7C}; *oplock = 0; data_offset = (char *)rsp + le32_to_cpu(rsp->CreateContextsOffset); @@ -1918,6 +1982,15 @@ smb2_parse_contexts(struct TCP_Server_Info *server, else if (buf && (le16_to_cpu(cc->NameLength) == 4) && strncmp(name, SMB2_CREATE_QUERY_ON_DISK_ID, 4) == 0) parse_query_id_ctxt(cc, buf); + else if ((le16_to_cpu(cc->NameLength) == 16)) { + if (memcmp(name, smb3_create_tag_posix, 16) == 0) + parse_posix_ctxt(cc, NULL); + } + /* else { + cifs_dbg(FYI, "Context not matched with len %d\n", + le16_to_cpu(cc->NameLength)); + cifs_dump_mem("Cctxt name: ", name, 4); + } */ next = le32_to_cpu(cc->Next); if (!next) @@ -2146,6 +2219,77 @@ add_twarp_context(struct kvec *iov, unsigned int *num_iovec, __u64 timewarp) return 0; } +/* See MS-SMB2 2.2.13.2.2 and MS-DTYP 2.4.6 */ +static struct crt_sd_ctxt * +create_sd_buf(umode_t mode, unsigned int *len) +{ + struct crt_sd_ctxt *buf; + struct cifs_ace *pace; + unsigned int sdlen, acelen; + + *len = roundup(sizeof(struct crt_sd_ctxt) + sizeof(struct cifs_ace) * 2, + 8); + buf = kzalloc(*len, GFP_KERNEL); + if (buf == NULL) + return buf; + + sdlen = sizeof(struct smb3_sd) + sizeof(struct smb3_acl) + + 2 * sizeof(struct cifs_ace); + + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct crt_sd_ctxt, sd)); + buf->ccontext.DataLength = cpu_to_le32(sdlen); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct crt_sd_ctxt, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + /* SMB2_CREATE_SD_BUFFER_TOKEN is "SecD" */ + buf->Name[0] = 'S'; + buf->Name[1] = 'e'; + buf->Name[2] = 'c'; + buf->Name[3] = 'D'; + buf->sd.Revision = 1; /* Must be one see MS-DTYP 2.4.6 */ + /* + * ACL is "self relative" ie ACL is stored in contiguous block of memory + * and "DP" ie the DACL is present + */ + buf->sd.Control = cpu_to_le16(ACL_CONTROL_SR | ACL_CONTROL_DP); + + /* offset owner, group and Sbz1 and SACL are all zero */ + buf->sd.OffsetDacl = cpu_to_le32(sizeof(struct smb3_sd)); + buf->acl.AclRevision = ACL_REVISION; /* See 2.4.4.1 of MS-DTYP */ + + /* create one ACE to hold the mode embedded in reserved special SID */ + pace = (struct cifs_ace *)(sizeof(struct crt_sd_ctxt) + (char *)buf); + acelen = setup_special_mode_ACE(pace, (__u64)mode); + /* and one more ACE to allow access for authenticated users */ + pace = (struct cifs_ace *)(acelen + (sizeof(struct crt_sd_ctxt) + + (char *)buf)); + acelen += setup_authusers_ACE(pace); + buf->acl.AclSize = cpu_to_le16(sizeof(struct cifs_acl) + acelen); + buf->acl.AceCount = cpu_to_le16(2); + return buf; +} + +static int +add_sd_context(struct kvec *iov, unsigned int *num_iovec, umode_t mode) +{ + struct smb2_create_req *req = iov[0].iov_base; + unsigned int num = *num_iovec; + unsigned int len = 0; + + iov[num].iov_base = create_sd_buf(mode, &len); + if (iov[num].iov_base == NULL) + return -ENOMEM; + iov[num].iov_len = len; + if (!req->CreateContextsOffset) + req->CreateContextsOffset = cpu_to_le32( + sizeof(struct smb2_create_req) + + iov[num - 1].iov_len); + le32_add_cpu(&req->CreateContextsLength, len); + *num_iovec = num + 1; + return 0; +} + static struct crt_query_id_ctxt * create_query_id_buf(void) { @@ -2351,6 +2495,7 @@ int smb311_posix_mkdir(const unsigned int xid, struct inode *inode, rqst.rq_iov = iov; rqst.rq_nvec = n_iov; + /* no need to inc num_remote_opens because we close it just below */ trace_smb3_posix_mkdir_enter(xid, tcon->tid, ses->Suid, CREATE_NOT_FILE, FILE_WRITE_ATTRIBUTES); /* resource #4: response buffer */ @@ -2415,6 +2560,7 @@ SMB2_open_init(struct cifs_tcon *tcon, struct smb_rqst *rqst, __u8 *oplock, /* File attributes ignored on open (used in create though) */ req->FileAttributes = cpu_to_le32(file_attributes); req->ShareAccess = FILE_SHARE_ALL_LE; + req->CreateDisposition = cpu_to_le32(oparms->disposition); req->CreateOptions = cpu_to_le32(oparms->create_options & CREATE_OPTIONS_MASK); req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req)); @@ -2458,7 +2604,7 @@ SMB2_open_init(struct cifs_tcon *tcon, struct smb_rqst *rqst, __u8 *oplock, iov[1].iov_len = uni_path_len; iov[1].iov_base = path; - if (!server->oplocks) + if ((!server->oplocks) || (tcon->no_lease)) *oplock = SMB2_OPLOCK_LEVEL_NONE; if (!(server->capabilities & SMB2_GLOBAL_CAP_LEASING) || @@ -2516,6 +2662,23 @@ SMB2_open_init(struct cifs_tcon *tcon, struct smb_rqst *rqst, __u8 *oplock, return rc; } + if ((oparms->disposition != FILE_OPEN) && + (oparms->cifs_sb) && + (oparms->cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID) && + (oparms->mode != ACL_NO_MODE)) { + if (n_iov > 2) { + struct create_context *ccontext = + (struct create_context *)iov[n_iov-1].iov_base; + ccontext->Next = + cpu_to_le32(iov[n_iov-1].iov_len); + } + + cifs_dbg(FYI, "add sd with mode 0x%x\n", oparms->mode); + rc = add_sd_context(iov, &n_iov, oparms->mode); + if (rc) + return rc; + } + if (n_iov > 2) { struct create_context *ccontext = (struct create_context *)iov[n_iov-1].iov_base; @@ -2594,6 +2757,11 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, } trace_smb3_open_err(xid, tcon->tid, ses->Suid, oparms->create_options, oparms->desired_access, rc); + if (rc == -EREMCHG) { + printk_once(KERN_WARNING "server share %s deleted\n", + tcon->treeName); + tcon->need_reconnect = true; + } goto creat_exit; } else trace_smb3_open_done(xid, rsp->PersistentFileId, tcon->tid, @@ -2637,7 +2805,7 @@ SMB2_ioctl_init(struct cifs_tcon *tcon, struct smb_rqst *rqst, int rc; char *in_data_buf; - rc = smb2_plain_req_init(SMB2_IOCTL, tcon, (void **) &req, &total_len); + rc = smb2_ioctl_req_init(opcode, tcon, (void **) &req, &total_len); if (rc) return rc; @@ -2742,6 +2910,7 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, int resp_buftype = CIFS_NO_BUFFER; int rc = 0; int flags = 0; + struct TCP_Server_Info *server; cifs_dbg(FYI, "SMB2 IOCTL\n"); @@ -2757,7 +2926,10 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, else return -EIO; - if (!ses || !(ses->server)) + if (!ses) + return -EIO; + server = ses->server; + if (!server) return -EIO; if (smb3_encryption_required(tcon)) @@ -2807,14 +2979,14 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, if (*plen == 0) goto ioctl_exit; /* server returned no data */ else if (*plen > rsp_iov.iov_len || *plen > 0xFF00) { - cifs_dbg(VFS, "srv returned invalid ioctl length: %d\n", *plen); + cifs_tcon_dbg(VFS, "srv returned invalid ioctl length: %d\n", *plen); *plen = 0; rc = -EIO; goto ioctl_exit; } if (rsp_iov.iov_len - *plen < le32_to_cpu(rsp->OutputOffset)) { - cifs_dbg(VFS, "Malformed ioctl resp: len %d offset %d\n", *plen, + cifs_tcon_dbg(VFS, "Malformed ioctl resp: len %d offset %d\n", *plen, le32_to_cpu(rsp->OutputOffset)); *plen = 0; rc = -EIO; @@ -2862,7 +3034,7 @@ SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, int SMB2_close_init(struct cifs_tcon *tcon, struct smb_rqst *rqst, - u64 persistent_fid, u64 volatile_fid) + u64 persistent_fid, u64 volatile_fid, bool query_attrs) { struct smb2_close_req *req; struct kvec *iov = rqst->rq_iov; @@ -2875,6 +3047,10 @@ SMB2_close_init(struct cifs_tcon *tcon, struct smb_rqst *rqst, req->PersistentFileId = persistent_fid; req->VolatileFileId = volatile_fid; + if (query_attrs) + req->Flags = SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB; + else + req->Flags = 0; iov[0].iov_base = (char *)req; iov[0].iov_len = total_len; @@ -2889,8 +3065,9 @@ SMB2_close_free(struct smb_rqst *rqst) } int -SMB2_close_flags(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, int flags) +__SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, + struct smb2_file_network_open_info *pbuf) { struct smb_rqst rqst; struct smb2_close_rsp *rsp = NULL; @@ -2899,6 +3076,8 @@ SMB2_close_flags(const unsigned int xid, struct cifs_tcon *tcon, struct kvec rsp_iov; int resp_buftype = CIFS_NO_BUFFER; int rc = 0; + int flags = 0; + bool query_attrs = false; cifs_dbg(FYI, "Close\n"); @@ -2913,7 +3092,13 @@ SMB2_close_flags(const unsigned int xid, struct cifs_tcon *tcon, rqst.rq_iov = iov; rqst.rq_nvec = 1; - rc = SMB2_close_init(tcon, &rqst, persistent_fid, volatile_fid); + /* check if need to ask server to return timestamps in close response */ + if (pbuf) + query_attrs = true; + + trace_smb3_close_enter(xid, persistent_fid, tcon->tid, ses->Suid); + rc = SMB2_close_init(tcon, &rqst, persistent_fid, volatile_fid, + query_attrs); if (rc) goto close_exit; @@ -2925,23 +3110,40 @@ SMB2_close_flags(const unsigned int xid, struct cifs_tcon *tcon, trace_smb3_close_err(xid, persistent_fid, tcon->tid, ses->Suid, rc); goto close_exit; + } else { + trace_smb3_close_done(xid, persistent_fid, tcon->tid, + ses->Suid); + /* + * Note that have to subtract 4 since struct network_open_info + * has a final 4 byte pad that close response does not have + */ + if (pbuf) + memcpy(pbuf, (char *)&rsp->CreationTime, sizeof(*pbuf) - 4); } atomic_dec(&tcon->num_remote_opens); - - /* BB FIXME - decode close response, update inode for caching */ - close_exit: SMB2_close_free(&rqst); free_rsp_buf(resp_buftype, rsp); + + /* retry close in a worker thread if this one is interrupted */ + if (rc == -EINTR) { + int tmp_rc; + + tmp_rc = smb2_handle_cancelled_close(tcon, persistent_fid, + volatile_fid); + if (tmp_rc) + cifs_dbg(VFS, "handle cancelled close fid 0x%llx returned error %d\n", + persistent_fid, tmp_rc); + } return rc; } int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid) + u64 persistent_fid, u64 volatile_fid) { - return SMB2_close_flags(xid, tcon, persistent_fid, volatile_fid, 0); + return __SMB2_close(xid, tcon, persistent_fid, volatile_fid, NULL); } int @@ -3055,12 +3257,16 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon, int rc = 0; int resp_buftype = CIFS_NO_BUFFER; struct cifs_ses *ses = tcon->ses; + struct TCP_Server_Info *server; int flags = 0; bool allocated = false; cifs_dbg(FYI, "Query Info\n"); - if (!ses || !(ses->server)) + if (!ses) + return -EIO; + server = ses->server; + if (!server) return -EIO; if (smb3_encryption_required(tcon)) @@ -3098,7 +3304,7 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon, if (!*data) { *data = kmalloc(*dlen, GFP_KERNEL); if (!*data) { - cifs_dbg(VFS, + cifs_tcon_dbg(VFS, "Error %d allocating memory for acl\n", rc); *dlen = 0; @@ -3159,6 +3365,93 @@ SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon, } /* + * CHANGE_NOTIFY Request is sent to get notifications on changes to a directory + * See MS-SMB2 2.2.35 and 2.2.36 + */ + +static int +SMB2_notify_init(const unsigned int xid, struct smb_rqst *rqst, + struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, + u32 completion_filter, bool watch_tree) +{ + struct smb2_change_notify_req *req; + struct kvec *iov = rqst->rq_iov; + unsigned int total_len; + int rc; + + rc = smb2_plain_req_init(SMB2_CHANGE_NOTIFY, tcon, (void **) &req, &total_len); + if (rc) + return rc; + + req->PersistentFileId = persistent_fid; + req->VolatileFileId = volatile_fid; + /* See note 354 of MS-SMB2, 64K max */ + req->OutputBufferLength = + cpu_to_le32(SMB2_MAX_BUFFER_SIZE - MAX_SMB2_HDR_SIZE); + req->CompletionFilter = cpu_to_le32(completion_filter); + if (watch_tree) + req->Flags = cpu_to_le16(SMB2_WATCH_TREE); + else + req->Flags = 0; + + iov[0].iov_base = (char *)req; + iov[0].iov_len = total_len; + + return 0; +} + +int +SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, bool watch_tree, + u32 completion_filter) +{ + struct cifs_ses *ses = tcon->ses; + struct smb_rqst rqst; + struct kvec iov[1]; + struct kvec rsp_iov = {NULL, 0}; + int resp_buftype = CIFS_NO_BUFFER; + int flags = 0; + int rc = 0; + + cifs_dbg(FYI, "change notify\n"); + if (!ses || !(ses->server)) + return -EIO; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + memset(&iov, 0, sizeof(iov)); + rqst.rq_iov = iov; + rqst.rq_nvec = 1; + + rc = SMB2_notify_init(xid, &rqst, tcon, persistent_fid, volatile_fid, + completion_filter, watch_tree); + if (rc) + goto cnotify_exit; + + trace_smb3_notify_enter(xid, persistent_fid, tcon->tid, ses->Suid, + (u8)watch_tree, completion_filter); + rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov); + + if (rc != 0) { + cifs_stats_fail_inc(tcon, SMB2_CHANGE_NOTIFY_HE); + trace_smb3_notify_err(xid, persistent_fid, tcon->tid, ses->Suid, + (u8)watch_tree, completion_filter, rc); + } else + trace_smb3_notify_done(xid, persistent_fid, tcon->tid, + ses->Suid, (u8)watch_tree, completion_filter); + + cnotify_exit: + if (rqst.rq_iov) + cifs_small_buf_release(rqst.rq_iov[0].iov_base); /* request */ + free_rsp_buf(resp_buftype, rsp_iov.iov_base); + return rc; +} + + + +/* * This is a no-op for now. We're not really interested in the reply, but * rather in the fact that the server sent one and that server->lstrp * gets updated. @@ -3287,51 +3580,76 @@ SMB2_echo(struct TCP_Server_Info *server) return rc; } +void +SMB2_flush_free(struct smb_rqst *rqst) +{ + if (rqst && rqst->rq_iov) + cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ +} + int -SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, - u64 volatile_fid) +SMB2_flush_init(const unsigned int xid, struct smb_rqst *rqst, + struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid) { - struct smb_rqst rqst; struct smb2_flush_req *req; - struct cifs_ses *ses = tcon->ses; - struct kvec iov[1]; - struct kvec rsp_iov; - int resp_buftype; - int rc = 0; - int flags = 0; + struct kvec *iov = rqst->rq_iov; unsigned int total_len; - - cifs_dbg(FYI, "Flush\n"); - - if (!ses || !(ses->server)) - return -EIO; + int rc; rc = smb2_plain_req_init(SMB2_FLUSH, tcon, (void **) &req, &total_len); if (rc) return rc; - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - req->PersistentFileId = persistent_fid; req->VolatileFileId = volatile_fid; iov[0].iov_base = (char *)req; iov[0].iov_len = total_len; + return 0; +} + +int +SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, + u64 volatile_fid) +{ + struct cifs_ses *ses = tcon->ses; + struct smb_rqst rqst; + struct kvec iov[1]; + struct kvec rsp_iov = {NULL, 0}; + int resp_buftype = CIFS_NO_BUFFER; + int flags = 0; + int rc = 0; + + cifs_dbg(FYI, "flush\n"); + if (!ses || !(ses->server)) + return -EIO; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + memset(&rqst, 0, sizeof(struct smb_rqst)); + memset(&iov, 0, sizeof(iov)); rqst.rq_iov = iov; rqst.rq_nvec = 1; + rc = SMB2_flush_init(xid, &rqst, tcon, persistent_fid, volatile_fid); + if (rc) + goto flush_exit; + + trace_smb3_flush_enter(xid, persistent_fid, tcon->tid, ses->Suid); rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov); - cifs_small_buf_release(req); if (rc != 0) { cifs_stats_fail_inc(tcon, SMB2_FLUSH_HE); trace_smb3_flush_err(xid, persistent_fid, tcon->tid, ses->Suid, rc); - } + } else + trace_smb3_flush_done(xid, persistent_fid, tcon->tid, + ses->Suid); + flush_exit: + SMB2_flush_free(&rqst); free_rsp_buf(resp_buftype, rsp_iov.iov_base); return rc; } @@ -3446,8 +3764,8 @@ smb2_readv_callback(struct mid_q_entry *mid) struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)rdata->iov[0].iov_base; struct cifs_credits credits = { .value = 0, .instance = 0 }; - struct smb_rqst rqst = { .rq_iov = rdata->iov, - .rq_nvec = 2, + struct smb_rqst rqst = { .rq_iov = &rdata->iov[1], + .rq_nvec = 1, .rq_pages = rdata->pages, .rq_offset = rdata->page_offset, .rq_npages = rdata->nr_pages, @@ -3468,7 +3786,7 @@ smb2_readv_callback(struct mid_q_entry *mid) rc = smb2_verify_signature(&rqst, server); if (rc) - cifs_dbg(VFS, "SMB signature verification returned error = %d\n", + cifs_tcon_dbg(VFS, "SMB signature verification returned error = %d\n", rc); } /* FIXME: should this be counted toward the initiating task? */ @@ -3595,7 +3913,7 @@ SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms, unsigned int *nbytes, char **buf, int *buf_type) { struct smb_rqst rqst; - int resp_buftype, rc = -EACCES; + int resp_buftype, rc; struct smb2_read_plain_req *req = NULL; struct smb2_read_rsp *rsp = NULL; struct kvec iov[1]; @@ -3734,6 +4052,9 @@ smb2_writev_callback(struct mid_q_entry *mid) wdata->cfile->fid.persistent_fid, tcon->tid, tcon->ses->Suid, wdata->offset, wdata->bytes, wdata->result); + if (wdata->result == -ENOSPC) + printk_once(KERN_WARNING "Out of space writing to %s\n", + tcon->treeName); } else trace_smb3_write_done(0 /* no xid */, wdata->cfile->fid.persistent_fid, @@ -4012,56 +4333,38 @@ num_entries(char *bufstart, char *end_of_buf, char **lastentry, size_t size) /* * Readdir/FindFirst */ -int -SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, - u64 persistent_fid, u64 volatile_fid, int index, - struct cifs_search_info *srch_inf) +int SMB2_query_directory_init(const unsigned int xid, + struct cifs_tcon *tcon, struct smb_rqst *rqst, + u64 persistent_fid, u64 volatile_fid, + int index, int info_level) { - struct smb_rqst rqst; + struct TCP_Server_Info *server = tcon->ses->server; struct smb2_query_directory_req *req; - struct smb2_query_directory_rsp *rsp = NULL; - struct kvec iov[2]; - struct kvec rsp_iov; - int rc = 0; - int len; - int resp_buftype = CIFS_NO_BUFFER; unsigned char *bufptr; - struct TCP_Server_Info *server; - struct cifs_ses *ses = tcon->ses; __le16 asteriks = cpu_to_le16('*'); - char *end_of_smb; - unsigned int output_size = CIFSMaxBufSize; - size_t info_buf_size; - int flags = 0; + unsigned int output_size = CIFSMaxBufSize - + MAX_SMB2_CREATE_RESPONSE_SIZE - + MAX_SMB2_CLOSE_RESPONSE_SIZE; unsigned int total_len; - - if (ses && (ses->server)) - server = ses->server; - else - return -EIO; + struct kvec *iov = rqst->rq_iov; + int len, rc; rc = smb2_plain_req_init(SMB2_QUERY_DIRECTORY, tcon, (void **) &req, &total_len); if (rc) return rc; - if (smb3_encryption_required(tcon)) - flags |= CIFS_TRANSFORM_REQ; - - switch (srch_inf->info_level) { + switch (info_level) { case SMB_FIND_FILE_DIRECTORY_INFO: req->FileInformationClass = FILE_DIRECTORY_INFORMATION; - info_buf_size = sizeof(FILE_DIRECTORY_INFO) - 1; break; case SMB_FIND_FILE_ID_FULL_DIR_INFO: req->FileInformationClass = FILEID_FULL_DIRECTORY_INFORMATION; - info_buf_size = sizeof(SEARCH_ID_FULL_DIR_INFO) - 1; break; default: - cifs_dbg(VFS, "info level %u isn't supported\n", - srch_inf->info_level); - rc = -EINVAL; - goto qdir_exit; + cifs_tcon_dbg(VFS, "info level %u isn't supported\n", + info_level); + return -EINVAL; } req->FileIndex = cpu_to_le32(index); @@ -4090,40 +4393,50 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, iov[1].iov_base = (char *)(req->Buffer); iov[1].iov_len = len; - memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov = iov; - rqst.rq_nvec = 2; - trace_smb3_query_dir_enter(xid, persistent_fid, tcon->tid, tcon->ses->Suid, index, output_size); - rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov); - cifs_small_buf_release(req); - rsp = (struct smb2_query_directory_rsp *)rsp_iov.iov_base; + return 0; +} - if (rc) { - if (rc == -ENODATA && - rsp->sync_hdr.Status == STATUS_NO_MORE_FILES) { - trace_smb3_query_dir_done(xid, persistent_fid, - tcon->tid, tcon->ses->Suid, index, 0); - srch_inf->endOfSearch = true; - rc = 0; - } else { - trace_smb3_query_dir_err(xid, persistent_fid, tcon->tid, - tcon->ses->Suid, index, 0, rc); - cifs_stats_fail_inc(tcon, SMB2_QUERY_DIRECTORY_HE); - } - goto qdir_exit; +void SMB2_query_directory_free(struct smb_rqst *rqst) +{ + if (rqst && rqst->rq_iov) { + cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ + } +} + +int +smb2_parse_query_directory(struct cifs_tcon *tcon, + struct kvec *rsp_iov, + int resp_buftype, + struct cifs_search_info *srch_inf) +{ + struct smb2_query_directory_rsp *rsp; + size_t info_buf_size; + char *end_of_smb; + int rc; + + rsp = (struct smb2_query_directory_rsp *)rsp_iov->iov_base; + + switch (srch_inf->info_level) { + case SMB_FIND_FILE_DIRECTORY_INFO: + info_buf_size = sizeof(FILE_DIRECTORY_INFO) - 1; + break; + case SMB_FIND_FILE_ID_FULL_DIR_INFO: + info_buf_size = sizeof(SEARCH_ID_FULL_DIR_INFO) - 1; + break; + default: + cifs_tcon_dbg(VFS, "info level %u isn't supported\n", + srch_inf->info_level); + return -EINVAL; } rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), - le32_to_cpu(rsp->OutputBufferLength), &rsp_iov, + le32_to_cpu(rsp->OutputBufferLength), rsp_iov, info_buf_size); - if (rc) { - trace_smb3_query_dir_err(xid, persistent_fid, tcon->tid, - tcon->ses->Suid, index, 0, rc); - goto qdir_exit; - } + if (rc) + return rc; srch_inf->unicode = true; @@ -4136,7 +4449,7 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, srch_inf->ntwrk_buf_start = (char *)rsp; srch_inf->srch_entries_start = srch_inf->last_entry = (char *)rsp + le16_to_cpu(rsp->OutputBufferOffset); - end_of_smb = rsp_iov.iov_len + (char *)rsp; + end_of_smb = rsp_iov->iov_len + (char *)rsp; srch_inf->entries_in_buffer = num_entries(srch_inf->srch_entries_start, end_of_smb, &srch_inf->last_entry, info_buf_size); @@ -4149,13 +4462,74 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, else if (resp_buftype == CIFS_SMALL_BUFFER) srch_inf->smallBuf = true; else - cifs_dbg(VFS, "illegal search buffer type\n"); + cifs_tcon_dbg(VFS, "illegal search buffer type\n"); + + return 0; +} + +int +SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, int index, + struct cifs_search_info *srch_inf) +{ + struct smb_rqst rqst; + struct kvec iov[SMB2_QUERY_DIRECTORY_IOV_SIZE]; + struct smb2_query_directory_rsp *rsp = NULL; + int resp_buftype = CIFS_NO_BUFFER; + struct kvec rsp_iov; + int rc = 0; + struct cifs_ses *ses = tcon->ses; + int flags = 0; + + if (!ses || !(ses->server)) + return -EIO; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + memset(&iov, 0, sizeof(iov)); + rqst.rq_iov = iov; + rqst.rq_nvec = SMB2_QUERY_DIRECTORY_IOV_SIZE; + + rc = SMB2_query_directory_init(xid, tcon, &rqst, persistent_fid, + volatile_fid, index, + srch_inf->info_level); + if (rc) + goto qdir_exit; + + rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov); + rsp = (struct smb2_query_directory_rsp *)rsp_iov.iov_base; + + if (rc) { + if (rc == -ENODATA && + rsp->sync_hdr.Status == STATUS_NO_MORE_FILES) { + trace_smb3_query_dir_done(xid, persistent_fid, + tcon->tid, tcon->ses->Suid, index, 0); + srch_inf->endOfSearch = true; + rc = 0; + } else { + trace_smb3_query_dir_err(xid, persistent_fid, tcon->tid, + tcon->ses->Suid, index, 0, rc); + cifs_stats_fail_inc(tcon, SMB2_QUERY_DIRECTORY_HE); + } + goto qdir_exit; + } + + rc = smb2_parse_query_directory(tcon, &rsp_iov, resp_buftype, + srch_inf); + if (rc) { + trace_smb3_query_dir_err(xid, persistent_fid, tcon->tid, + tcon->ses->Suid, index, 0, rc); + goto qdir_exit; + } + resp_buftype = CIFS_NO_BUFFER; trace_smb3_query_dir_done(xid, persistent_fid, tcon->tid, tcon->ses->Suid, index, srch_inf->entries_in_buffer); - return rc; qdir_exit: + SMB2_query_directory_free(&rqst); free_rsp_buf(resp_buftype, rsp); return rc; } |