From ec2e4523fdba88317e06d0c7a88af3a0860447fc Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Tue, 27 Dec 2011 16:12:43 +0400 Subject: CIFS: Add capability to send SMB2 negotiate message and add negotiate request type to let set_credits know that we are only on negotiate stage and no need to make a decision about disabling echos and oplocks. Signed-off-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/smb2pdu.c | 330 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 330 insertions(+) create mode 100644 fs/cifs/smb2pdu.c (limited to 'fs/cifs/smb2pdu.c') diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c new file mode 100644 index 000000000000..719e4c4f0307 --- /dev/null +++ b/fs/cifs/smb2pdu.c @@ -0,0 +1,330 @@ +/* + * fs/cifs/smb2pdu.c + * + * Copyright (C) International Business Machines Corp., 2009, 2011 + * Etersoft, 2012 + * Author(s): Steve French (sfrench@us.ibm.com) + * Pavel Shilovsky (pshilovsky@samba.org) 2012 + * + * Contains the routines for constructing the SMB2 PDUs themselves + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + /* SMB2 PDU handling routines here - except for leftovers (eg session setup) */ + /* Note that there are handle based routines which must be */ + /* treated slightly differently for reconnection purposes since we never */ + /* want to reuse a stale file handle and only the caller knows the file info */ + +#include +#include +#include +#include +#include +#include "smb2pdu.h" +#include "cifsglob.h" +#include "cifsacl.h" +#include "cifsproto.h" +#include "smb2proto.h" +#include "cifs_unicode.h" +#include "cifs_debug.h" +#include "ntlmssp.h" +#include "smb2status.h" + +/* + * The following table defines the expected "StructureSize" of SMB2 requests + * in order by SMB2 command. This is similar to "wct" in SMB/CIFS requests. + * + * Note that commands are defined in smb2pdu.h in le16 but the array below is + * indexed by command in host byte order. + */ +static const int smb2_req_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = { + /* SMB2_NEGOTIATE */ 36, + /* SMB2_SESSION_SETUP */ 25, + /* SMB2_LOGOFF */ 4, + /* SMB2_TREE_CONNECT */ 9, + /* SMB2_TREE_DISCONNECT */ 4, + /* SMB2_CREATE */ 57, + /* SMB2_CLOSE */ 24, + /* SMB2_FLUSH */ 24, + /* SMB2_READ */ 49, + /* SMB2_WRITE */ 49, + /* SMB2_LOCK */ 48, + /* SMB2_IOCTL */ 57, + /* SMB2_CANCEL */ 4, + /* SMB2_ECHO */ 4, + /* SMB2_QUERY_DIRECTORY */ 33, + /* SMB2_CHANGE_NOTIFY */ 32, + /* SMB2_QUERY_INFO */ 41, + /* SMB2_SET_INFO */ 33, + /* SMB2_OPLOCK_BREAK */ 24 /* BB this is 36 for LEASE_BREAK variant */ +}; + + +static void +smb2_hdr_assemble(struct smb2_hdr *hdr, __le16 smb2_cmd /* command */ , + const struct cifs_tcon *tcon) +{ + struct smb2_pdu *pdu = (struct smb2_pdu *)hdr; + char *temp = (char *)hdr; + /* lookup word count ie StructureSize from table */ + __u16 parmsize = smb2_req_struct_sizes[le16_to_cpu(smb2_cmd)]; + + /* + * smaller than SMALL_BUFFER_SIZE but bigger than fixed area of + * largest operations (Create) + */ + memset(temp, 0, 256); + + /* Note this is only network field converted to big endian */ + hdr->smb2_buf_length = cpu_to_be32(parmsize + sizeof(struct smb2_hdr) + - 4 /* RFC 1001 length field itself not counted */); + + hdr->ProtocolId[0] = 0xFE; + hdr->ProtocolId[1] = 'S'; + hdr->ProtocolId[2] = 'M'; + hdr->ProtocolId[3] = 'B'; + hdr->StructureSize = cpu_to_le16(64); + hdr->Command = smb2_cmd; + hdr->CreditRequest = cpu_to_le16(2); /* BB make this dynamic */ + hdr->ProcessId = cpu_to_le32((__u16)current->tgid); + + if (!tcon) + goto out; + + hdr->TreeId = tcon->tid; + /* Uid is not converted */ + if (tcon->ses) + hdr->SessionId = tcon->ses->Suid; + /* BB check following DFS flags BB */ + /* BB do we have to add check for SHI1005_FLAGS_DFS_ROOT too? */ + /* if (tcon->share_flags & SHI1005_FLAGS_DFS) + hdr->Flags |= SMB2_FLAGS_DFS_OPERATIONS; */ + /* BB how does SMB2 do case sensitive? */ + /* if (tcon->nocase) + hdr->Flags |= SMBFLG_CASELESS; */ + /* if (tcon->ses && tcon->ses->server && + (tcon->ses->server->sec_mode & SECMODE_SIGN_REQUIRED)) + hdr->Flags |= SMB2_FLAGS_SIGNED; */ +out: + pdu->StructureSize2 = cpu_to_le16(parmsize); + return; +} + +static int +smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon) +{ + int rc = 0; + /* BB add missing code here */ + return rc; +} + +/* + * Allocate and return pointer to an SMB request hdr, and set basic + * SMB information in the SMB header. If the return code is zero, this + * function must have filled in request_buf pointer. + */ +static int +small_smb2_init(__le16 smb2_command, struct cifs_tcon *tcon, + void **request_buf) +{ + int rc = 0; + + rc = smb2_reconnect(smb2_command, tcon); + if (rc) + return rc; + + /* BB eventually switch this to SMB2 specific small buf size */ + *request_buf = cifs_small_buf_get(); + if (*request_buf == NULL) { + /* BB should we add a retry in here if not a writepage? */ + return -ENOMEM; + } + + smb2_hdr_assemble((struct smb2_hdr *) *request_buf, smb2_command, tcon); + + if (tcon != NULL) { +#ifdef CONFIG_CIFS_STATS2 + /* + uint16_t com_code = le16_to_cpu(smb2_command); + cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_sent[com_code]); + */ +#endif + cifs_stats_inc(&tcon->num_smbs_sent); + } + + return rc; +} + +static void +free_rsp_buf(int resp_buftype, void *rsp) +{ + if (resp_buftype == CIFS_SMALL_BUFFER) + cifs_small_buf_release(rsp); + else if (resp_buftype == CIFS_LARGE_BUFFER) + cifs_buf_release(rsp); +} + +#define SMB2_NUM_PROT 1 + +#define SMB2_PROT 0 +#define SMB21_PROT 1 +#define BAD_PROT 0xFFFF + +#define SMB2_PROT_ID 0x0202 +#define SMB21_PROT_ID 0x0210 +#define BAD_PROT_ID 0xFFFF + +static struct { + int index; + __le16 name; +} smb2protocols[] = { + {SMB2_PROT, cpu_to_le16(SMB2_PROT_ID)}, + {SMB21_PROT, cpu_to_le16(SMB21_PROT_ID)}, + {BAD_PROT, cpu_to_le16(BAD_PROT_ID)} +}; + +/* + * + * SMB2 Worker functions follow: + * + * The general structure of the worker functions is: + * 1) Call smb2_init (assembles SMB2 header) + * 2) Initialize SMB2 command specific fields in fixed length area of SMB + * 3) Call smb_sendrcv2 (sends request on socket and waits for response) + * 4) Decode SMB2 command specific fields in the fixed length area + * 5) Decode variable length data area (if any for this SMB2 command type) + * 6) Call free smb buffer + * 7) return + * + */ + +int +SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) +{ + struct smb2_negotiate_req *req; + struct smb2_negotiate_rsp *rsp; + struct kvec iov[1]; + int rc = 0; + int resp_buftype; + struct TCP_Server_Info *server; + unsigned int sec_flags; + u16 i; + u16 temp = 0; + int blob_offset, blob_length; + char *security_blob; + int flags = CIFS_NEG_OP; + + cFYI(1, "Negotiate protocol"); + + if (ses->server) + server = ses->server; + else { + rc = -EIO; + return rc; + } + + rc = small_smb2_init(SMB2_NEGOTIATE, NULL, (void **) &req); + if (rc) + return rc; + + /* if any of auth flags (ie not sign or seal) are overriden use them */ + if (ses->overrideSecFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL))) + sec_flags = ses->overrideSecFlg; /* BB FIXME fix sign flags?*/ + else /* if override flags set only sign/seal OR them with global auth */ + sec_flags = global_secflags | ses->overrideSecFlg; + + cFYI(1, "sec_flags 0x%x", sec_flags); + + req->hdr.SessionId = 0; + + for (i = 0; i < SMB2_NUM_PROT; i++) + req->Dialects[i] = smb2protocols[i].name; + + req->DialectCount = cpu_to_le16(i); + inc_rfc1001_len(req, i * 2); + + /* only one of SMB2 signing flags may be set in SMB2 request */ + if ((sec_flags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) + temp = SMB2_NEGOTIATE_SIGNING_REQUIRED; + else if (sec_flags & CIFSSEC_MAY_SIGN) /* MAY_SIGN is a single flag */ + temp = SMB2_NEGOTIATE_SIGNING_ENABLED; + + req->SecurityMode = cpu_to_le16(temp); + + req->Capabilities = cpu_to_le32(SMB2_GLOBAL_CAP_DFS); + + iov[0].iov_base = (char *)req; + /* 4 for rfc1002 length field */ + iov[0].iov_len = get_rfc1002_length(req) + 4; + + rc = SendReceive2(xid, ses, iov, 1, &resp_buftype, flags); + + rsp = (struct smb2_negotiate_rsp *)iov[0].iov_base; + /* + * No tcon so can't do + * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]); + */ + if (rc != 0) + goto neg_exit; + + if (rsp == NULL) { + rc = -EIO; + goto neg_exit; + } + + cFYI(1, "mode 0x%x", rsp->SecurityMode); + + if (rsp->DialectRevision == smb2protocols[SMB21_PROT].name) + cFYI(1, "negotiated smb2.1 dialect"); + else if (rsp->DialectRevision == smb2protocols[SMB2_PROT].name) + cFYI(1, "negotiated smb2 dialect"); + else { + cERROR(1, "Illegal dialect returned by server %d", + le16_to_cpu(rsp->DialectRevision)); + rc = -EIO; + goto neg_exit; + } + server->dialect = le16_to_cpu(rsp->DialectRevision); + + server->maxBuf = le32_to_cpu(rsp->MaxTransactSize); + server->max_read = le32_to_cpu(rsp->MaxReadSize); + server->max_write = le32_to_cpu(rsp->MaxWriteSize); + /* BB Do we need to validate the SecurityMode? */ + server->sec_mode = le16_to_cpu(rsp->SecurityMode); + server->capabilities = le32_to_cpu(rsp->Capabilities); + + security_blob = smb2_get_data_area_len(&blob_offset, &blob_length, + &rsp->hdr); + if (blob_length == 0) { + cERROR(1, "missing security blob on negprot"); + rc = -EIO; + goto neg_exit; + } +#ifdef CONFIG_SMB2_ASN1 /* BB REMOVEME when updated asn1.c ready */ + rc = decode_neg_token_init(security_blob, blob_length, + &server->sec_type); + if (rc == 1) + rc = 0; + else if (rc == 0) { + rc = -EIO; + goto neg_exit; + } +#endif + +neg_exit: + free_rsp_buf(resp_buftype, rsp); + return rc; +} -- cgit v1.2.3 From 5478f9ba9a34d660eb3227dcd16314689c51f946 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Tue, 27 Dec 2011 16:22:00 +0400 Subject: CIFS: Add session setup/logoff capability for SMB2 Signed-off-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 3 + fs/cifs/ntlmssp.h | 10 +++ fs/cifs/sess.c | 6 +- fs/cifs/smb2misc.c | 5 ++ fs/cifs/smb2ops.c | 2 + fs/cifs/smb2pdu.c | 221 ++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/cifs/smb2pdu.h | 37 +++++++++ fs/cifs/smb2proto.h | 3 + 8 files changed, 284 insertions(+), 3 deletions(-) (limited to 'fs/cifs/smb2pdu.c') diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 2d48f880b130..0d78bc410cb3 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -504,6 +504,9 @@ struct cifs_ses { struct session_key auth_key; struct ntlmssp_auth *ntlmssp; /* ciphertext, flags, server challenge */ bool need_reconnect:1; /* connection reset, uid now invalid */ +#ifdef CONFIG_CIFS_SMB2 + __u16 session_flags; +#endif /* CONFIG_CIFS_SMB2 */ }; /* no more than one of the following three session flags may be set */ #define CIFS_SES_NT4 1 diff --git a/fs/cifs/ntlmssp.h b/fs/cifs/ntlmssp.h index 5d52e4a3b1ed..848249fa120f 100644 --- a/fs/cifs/ntlmssp.h +++ b/fs/cifs/ntlmssp.h @@ -126,3 +126,13 @@ typedef struct _AUTHENTICATE_MESSAGE { do not set the version is present flag */ char UserString[0]; } __attribute__((packed)) AUTHENTICATE_MESSAGE, *PAUTHENTICATE_MESSAGE; + +/* + * Size of the session key (crypto key encrypted with the password + */ + +int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, struct cifs_ses *ses); +void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, struct cifs_ses *ses); +int build_ntlmssp_auth_blob(unsigned char *pbuffer, u16 *buflen, + struct cifs_ses *ses, + const struct nls_table *nls_cp); diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index 08efc3c8efef..382c06d01b38 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -364,7 +364,7 @@ static int decode_ascii_ssetup(char **pbcc_area, __u16 bleft, return rc; } -static int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, +int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, struct cifs_ses *ses) { unsigned int tioffset; /* challenge message target info area */ @@ -415,7 +415,7 @@ static int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, /* We do not malloc the blob, it is passed in pbuffer, because it is fixed size, and small, making this approach cleaner */ -static void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, +void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, struct cifs_ses *ses) { NEGOTIATE_MESSAGE *sec_blob = (NEGOTIATE_MESSAGE *)pbuffer; @@ -451,7 +451,7 @@ static void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, /* We do not malloc the blob, it is passed in pbuffer, because its maximum possible size is fixed and small, making this approach cleaner. This function returns the length of the data in the blob */ -static int build_ntlmssp_auth_blob(unsigned char *pbuffer, +int build_ntlmssp_auth_blob(unsigned char *pbuffer, u16 *buflen, struct cifs_ses *ses, const struct nls_table *nls_cp) diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c index e4dede4ae058..10729a74da27 100644 --- a/fs/cifs/smb2misc.c +++ b/fs/cifs/smb2misc.c @@ -224,6 +224,11 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr) ((struct smb2_negotiate_rsp *)hdr)->SecurityBufferLength); break; case SMB2_SESSION_SETUP: + *off = le16_to_cpu( + ((struct smb2_sess_setup_rsp *)hdr)->SecurityBufferOffset); + *len = le16_to_cpu( + ((struct smb2_sess_setup_rsp *)hdr)->SecurityBufferLength); + break; case SMB2_CREATE: case SMB2_READ: case SMB2_QUERY_INFO: diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 2b5232b4f7e7..0057861ce19d 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -170,6 +170,8 @@ struct smb_version_operations smb21_operations = { .dump_detail = smb2_dump_detail, .need_neg = smb2_need_neg, .negotiate = smb2_negotiate, + .sess_setup = SMB2_sess_setup, + .logoff = SMB2_logoff, }; struct smb_version_values smb21_values = { diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 719e4c4f0307..2165f0d15963 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -328,3 +328,224 @@ neg_exit: free_rsp_buf(resp_buftype, rsp); return rc; } + +int +SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, + const struct nls_table *nls_cp) +{ + struct smb2_sess_setup_req *req; + struct smb2_sess_setup_rsp *rsp = NULL; + struct kvec iov[2]; + int rc = 0; + int resp_buftype; + __le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */ + struct TCP_Server_Info *server; + unsigned int sec_flags; + u8 temp = 0; + u16 blob_length = 0; + char *security_blob; + char *ntlmssp_blob = NULL; + bool use_spnego = false; /* else use raw ntlmssp */ + + cFYI(1, "Session Setup"); + + if (ses->server) + server = ses->server; + else { + rc = -EIO; + return rc; + } + + /* + * If memory allocation is successful, caller of this function + * frees it. + */ + ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL); + if (!ses->ntlmssp) + return -ENOMEM; + + ses->server->secType = RawNTLMSSP; + +ssetup_ntlmssp_authenticate: + if (phase == NtLmChallenge) + phase = NtLmAuthenticate; /* if ntlmssp, now final phase */ + + rc = small_smb2_init(SMB2_SESSION_SETUP, NULL, (void **) &req); + if (rc) + return rc; + + /* if any of auth flags (ie not sign or seal) are overriden use them */ + if (ses->overrideSecFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL))) + sec_flags = ses->overrideSecFlg; /* BB FIXME fix sign flags?*/ + else /* if override flags set only sign/seal OR them with global auth */ + sec_flags = global_secflags | ses->overrideSecFlg; + + cFYI(1, "sec_flags 0x%x", sec_flags); + + req->hdr.SessionId = 0; /* First session, not a reauthenticate */ + req->VcNumber = 0; /* MBZ */ + /* to enable echos and oplocks */ + req->hdr.CreditRequest = cpu_to_le16(3); + + /* only one of SMB2 signing flags may be set in SMB2 request */ + if ((sec_flags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) + temp = SMB2_NEGOTIATE_SIGNING_REQUIRED; + else if (ses->server->sec_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) + temp = SMB2_NEGOTIATE_SIGNING_REQUIRED; + else if (sec_flags & CIFSSEC_MAY_SIGN) /* MAY_SIGN is a single flag */ + temp = SMB2_NEGOTIATE_SIGNING_ENABLED; + + req->SecurityMode = temp; + req->Capabilities = 0; + req->Channel = 0; /* MBZ */ + + iov[0].iov_base = (char *)req; + /* 4 for rfc1002 length field and 1 for pad */ + iov[0].iov_len = get_rfc1002_length(req) + 4 - 1; + if (phase == NtLmNegotiate) { + ntlmssp_blob = kmalloc(sizeof(struct _NEGOTIATE_MESSAGE), + GFP_KERNEL); + if (ntlmssp_blob == NULL) { + rc = -ENOMEM; + goto ssetup_exit; + } + build_ntlmssp_negotiate_blob(ntlmssp_blob, ses); + if (use_spnego) { + /* blob_length = build_spnego_ntlmssp_blob( + &security_blob, + sizeof(struct _NEGOTIATE_MESSAGE), + ntlmssp_blob); */ + /* BB eventually need to add this */ + cERROR(1, "spnego not supported for SMB2 yet"); + rc = -EOPNOTSUPP; + kfree(ntlmssp_blob); + goto ssetup_exit; + } else { + blob_length = sizeof(struct _NEGOTIATE_MESSAGE); + /* with raw NTLMSSP we don't encapsulate in SPNEGO */ + security_blob = ntlmssp_blob; + } + } else if (phase == NtLmAuthenticate) { + req->hdr.SessionId = ses->Suid; + ntlmssp_blob = kzalloc(sizeof(struct _NEGOTIATE_MESSAGE) + 500, + GFP_KERNEL); + if (ntlmssp_blob == NULL) { + cERROR(1, "failed to malloc ntlmssp blob"); + rc = -ENOMEM; + goto ssetup_exit; + } + rc = build_ntlmssp_auth_blob(ntlmssp_blob, &blob_length, ses, + nls_cp); + if (rc) { + cFYI(1, "build_ntlmssp_auth_blob failed %d", rc); + goto ssetup_exit; /* BB double check error handling */ + } + if (use_spnego) { + /* blob_length = build_spnego_ntlmssp_blob( + &security_blob, + blob_length, + ntlmssp_blob); */ + cERROR(1, "spnego not supported for SMB2 yet"); + rc = -EOPNOTSUPP; + kfree(ntlmssp_blob); + goto ssetup_exit; + } else { + security_blob = ntlmssp_blob; + } + } else { + cERROR(1, "illegal ntlmssp phase"); + rc = -EIO; + goto ssetup_exit; + } + + /* Testing shows that buffer offset must be at location of Buffer[0] */ + req->SecurityBufferOffset = + cpu_to_le16(sizeof(struct smb2_sess_setup_req) - + 1 /* pad */ - 4 /* rfc1001 len */); + req->SecurityBufferLength = cpu_to_le16(blob_length); + iov[1].iov_base = security_blob; + iov[1].iov_len = blob_length; + + inc_rfc1001_len(req, blob_length - 1 /* pad */); + + /* BB add code to build os and lm fields */ + + rc = SendReceive2(xid, ses, iov, 2, &resp_buftype, CIFS_LOG_ERROR); + + kfree(security_blob); + rsp = (struct smb2_sess_setup_rsp *)iov[0].iov_base; + if (rsp->hdr.Status == STATUS_MORE_PROCESSING_REQUIRED) { + if (phase != NtLmNegotiate) { + cERROR(1, "Unexpected more processing error"); + goto ssetup_exit; + } + if (offsetof(struct smb2_sess_setup_rsp, Buffer) - 4 != + le16_to_cpu(rsp->SecurityBufferOffset)) { + cERROR(1, "Invalid security buffer offset %d", + le16_to_cpu(rsp->SecurityBufferOffset)); + rc = -EIO; + goto ssetup_exit; + } + + /* NTLMSSP Negotiate sent now processing challenge (response) */ + phase = NtLmChallenge; /* process ntlmssp challenge */ + rc = 0; /* MORE_PROCESSING is not an error here but expected */ + ses->Suid = rsp->hdr.SessionId; + rc = decode_ntlmssp_challenge(rsp->Buffer, + le16_to_cpu(rsp->SecurityBufferLength), ses); + } + + /* + * BB eventually add code for SPNEGO decoding of NtlmChallenge blob, + * but at least the raw NTLMSSP case works. + */ + /* + * No tcon so can't do + * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]); + */ + if (rc != 0) + goto ssetup_exit; + + if (rsp == NULL) { + rc = -EIO; + goto ssetup_exit; + } + + ses->session_flags = le16_to_cpu(rsp->SessionFlags); +ssetup_exit: + free_rsp_buf(resp_buftype, rsp); + + /* if ntlmssp, and negotiate succeeded, proceed to authenticate phase */ + if ((phase == NtLmChallenge) && (rc == 0)) + goto ssetup_ntlmssp_authenticate; + return rc; +} + +int +SMB2_logoff(const unsigned int xid, struct cifs_ses *ses) +{ + struct smb2_logoff_req *req; /* response is also trivial struct */ + int rc = 0; + struct TCP_Server_Info *server; + + cFYI(1, "disconnect session %p", ses); + + if (ses && (ses->server)) + server = ses->server; + else + return -EIO; + + rc = small_smb2_init(SMB2_LOGOFF, NULL, (void **) &req); + if (rc) + return rc; + + /* since no tcon, smb2_init can not do this, so do here */ + req->hdr.SessionId = ses->Suid; + + rc = SendReceiveNoRsp(xid, ses, (char *) &req->hdr, 0); + /* + * No tcon so can't do + * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]); + */ + return rc; +} diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index ef8dae213f60..26af68b2955a 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h @@ -187,4 +187,41 @@ struct smb2_negotiate_rsp { __u8 Buffer[1]; /* variable length GSS security buffer */ } __packed; +struct smb2_sess_setup_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 25 */ + __u8 VcNumber; + __u8 SecurityMode; + __le32 Capabilities; + __le32 Channel; + __le16 SecurityBufferOffset; + __le16 SecurityBufferLength; + __le64 PreviousSessionId; + __u8 Buffer[1]; /* variable length GSS security buffer */ +} __packed; + +/* Currently defined SessionFlags */ +#define SMB2_SESSION_FLAG_IS_GUEST 0x0001 +#define SMB2_SESSION_FLAG_IS_NULL 0x0002 +struct smb2_sess_setup_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 9 */ + __le16 SessionFlags; + __le16 SecurityBufferOffset; + __le16 SecurityBufferLength; + __u8 Buffer[1]; /* variable length GSS security buffer */ +} __packed; + +struct smb2_logoff_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __le16 Reserved; +} __packed; + +struct smb2_logoff_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __le16 Reserved; +} __packed; + #endif /* _SMB2PDU_H */ diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index 881767002807..9364fbcb90c6 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h @@ -47,5 +47,8 @@ extern int smb2_setup_request(struct cifs_ses *ses, struct kvec *iov, * are contained within these calls. */ extern int SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses); +extern int SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, + const struct nls_table *nls_cp); +extern int SMB2_logoff(const unsigned int xid, struct cifs_ses *ses); #endif /* _SMB2PROTO_H */ -- cgit v1.2.3 From faaf946a7d5b79194358437150f34ab4c66bfe21 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Tue, 27 Dec 2011 16:04:00 +0400 Subject: CIFS: Add tree connect/disconnect capability for SMB2 Signed-off-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/cifs_unicode.c | 1 - fs/cifs/cifs_unicode.h | 1 - fs/cifs/cifsglob.h | 11 +++- fs/cifs/smb2ops.c | 2 + fs/cifs/smb2pdu.c | 159 ++++++++++++++++++++++++++++++++++++++++++++++++- fs/cifs/smb2pdu.h | 57 ++++++++++++++++++ fs/cifs/smb2proto.h | 4 ++ 7 files changed, 230 insertions(+), 5 deletions(-) (limited to 'fs/cifs/smb2pdu.c') diff --git a/fs/cifs/cifs_unicode.c b/fs/cifs/cifs_unicode.c index fbb9da951843..97c1d4210869 100644 --- a/fs/cifs/cifs_unicode.c +++ b/fs/cifs/cifs_unicode.c @@ -330,4 +330,3 @@ cifsConvertToUTF16(__le16 *target, const char *source, int srclen, ctoUTF16_out: return i; } - diff --git a/fs/cifs/cifs_unicode.h b/fs/cifs/cifs_unicode.h index a513a546700b..a44c6eb8a4d7 100644 --- a/fs/cifs/cifs_unicode.h +++ b/fs/cifs/cifs_unicode.h @@ -84,7 +84,6 @@ char *cifs_strndup_from_utf16(const char *src, const int maxlen, const struct nls_table *codepage); extern int cifsConvertToUTF16(__le16 *target, const char *source, int maxlen, const struct nls_table *cp, int mapChars); - #endif /* diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 0d78bc410cb3..ef4e0a0bc826 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -528,7 +528,7 @@ struct cifs_tcon { char treeName[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */ char *nativeFileSystem; char *password; /* for share-level security */ - __u16 tid; /* The 2 byte tree id */ + __u32 tid; /* The 4 byte tree id */ __u16 Flags; /* optional support bits */ enum statusEnum tidStatus; #ifdef CONFIG_CIFS_STATS @@ -584,6 +584,15 @@ struct cifs_tcon { bool local_lease:1; /* check leases (only) on local system not remote */ bool broken_posix_open; /* e.g. Samba server versions < 3.3.2, 3.2.9 */ bool need_reconnect:1; /* connection reset, tid now invalid */ +#ifdef CONFIG_CIFS_SMB2 + bool print:1; /* set if connection to printer share */ + bool bad_network_name:1; /* set if ret status STATUS_BAD_NETWORK_NAME */ + __u32 capabilities; + __u32 share_flags; + __u32 maximal_access; + __u32 vol_serial_number; + __le64 vol_create_time; +#endif /* CONFIG_CIFS_SMB2 */ #ifdef CONFIG_CIFS_FSCACHE u64 resource_id; /* server resource id */ struct fscache_cookie *fscache; /* cookie for share */ diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 0057861ce19d..0e33ca32abf9 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -172,6 +172,8 @@ struct smb_version_operations smb21_operations = { .negotiate = smb2_negotiate, .sess_setup = SMB2_sess_setup, .logoff = SMB2_logoff, + .tree_connect = SMB2_tcon, + .tree_disconnect = SMB2_tdis, }; struct smb_version_values smb21_values = { diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 2165f0d15963..1bf037ec5a9d 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -110,8 +110,8 @@ smb2_hdr_assemble(struct smb2_hdr *hdr, __le16 smb2_cmd /* command */ , hdr->SessionId = tcon->ses->Suid; /* BB check following DFS flags BB */ /* BB do we have to add check for SHI1005_FLAGS_DFS_ROOT too? */ - /* if (tcon->share_flags & SHI1005_FLAGS_DFS) - hdr->Flags |= SMB2_FLAGS_DFS_OPERATIONS; */ + if (tcon->share_flags & SHI1005_FLAGS_DFS) + hdr->Flags |= SMB2_FLAGS_DFS_OPERATIONS; /* BB how does SMB2 do case sensitive? */ /* if (tcon->nocase) hdr->Flags |= SMBFLG_CASELESS; */ @@ -549,3 +549,158 @@ SMB2_logoff(const unsigned int xid, struct cifs_ses *ses) */ return rc; } + +static inline void cifs_stats_fail_inc(struct cifs_tcon *tcon, uint16_t code) +{ + /* cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[code]); */ +} + +#define MAX_SHARENAME_LENGTH (255 /* server */ + 80 /* share */ + 1 /* NULL */) + +int +SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, + struct cifs_tcon *tcon, const struct nls_table *cp) +{ + struct smb2_tree_connect_req *req; + struct smb2_tree_connect_rsp *rsp = NULL; + struct kvec iov[2]; + int rc = 0; + int resp_buftype; + int unc_path_len; + struct TCP_Server_Info *server; + __le16 *unc_path = NULL; + + cFYI(1, "TCON"); + + if ((ses->server) && tree) + server = ses->server; + else + return -EIO; + + if (tcon && tcon->bad_network_name) + return -ENOENT; + + unc_path = kmalloc(MAX_SHARENAME_LENGTH * 2, GFP_KERNEL); + if (unc_path == NULL) + return -ENOMEM; + + unc_path_len = cifs_strtoUTF16(unc_path, tree, strlen(tree), cp) + 1; + unc_path_len *= 2; + if (unc_path_len < 2) { + kfree(unc_path); + return -EINVAL; + } + + rc = small_smb2_init(SMB2_TREE_CONNECT, tcon, (void **) &req); + if (rc) { + kfree(unc_path); + return rc; + } + + if (tcon == NULL) { + /* since no tcon, smb2_init can not do this, so do here */ + req->hdr.SessionId = ses->Suid; + /* if (ses->server->sec_mode & SECMODE_SIGN_REQUIRED) + req->hdr.Flags |= SMB2_FLAGS_SIGNED; */ + } + + iov[0].iov_base = (char *)req; + /* 4 for rfc1002 length field and 1 for pad */ + iov[0].iov_len = get_rfc1002_length(req) + 4 - 1; + + /* Testing shows that buffer offset must be at location of Buffer[0] */ + req->PathOffset = cpu_to_le16(sizeof(struct smb2_tree_connect_req) + - 1 /* pad */ - 4 /* do not count rfc1001 len field */); + req->PathLength = cpu_to_le16(unc_path_len - 2); + iov[1].iov_base = unc_path; + iov[1].iov_len = unc_path_len; + + inc_rfc1001_len(req, unc_path_len - 1 /* pad */); + + rc = SendReceive2(xid, ses, iov, 2, &resp_buftype, 0); + rsp = (struct smb2_tree_connect_rsp *)iov[0].iov_base; + + if (rc != 0) { + if (tcon) { + cifs_stats_fail_inc(tcon, SMB2_TREE_CONNECT_HE); + tcon->need_reconnect = true; + } + goto tcon_error_exit; + } + + if (rsp == NULL) { + rc = -EIO; + goto tcon_exit; + } + + if (tcon == NULL) { + ses->ipc_tid = rsp->hdr.TreeId; + goto tcon_exit; + } + + if (rsp->ShareType & SMB2_SHARE_TYPE_DISK) + cFYI(1, "connection to disk share"); + else if (rsp->ShareType & SMB2_SHARE_TYPE_PIPE) { + tcon->ipc = true; + cFYI(1, "connection to pipe share"); + } else if (rsp->ShareType & SMB2_SHARE_TYPE_PRINT) { + tcon->print = true; + cFYI(1, "connection to printer"); + } else { + cERROR(1, "unknown share type %d", rsp->ShareType); + rc = -EOPNOTSUPP; + goto tcon_error_exit; + } + + tcon->share_flags = le32_to_cpu(rsp->ShareFlags); + tcon->maximal_access = le32_to_cpu(rsp->MaximalAccess); + tcon->tidStatus = CifsGood; + tcon->need_reconnect = false; + tcon->tid = rsp->hdr.TreeId; + strncpy(tcon->treeName, tree, MAX_TREE_SIZE); + + if ((rsp->Capabilities & SMB2_SHARE_CAP_DFS) && + ((tcon->share_flags & SHI1005_FLAGS_DFS) == 0)) + cERROR(1, "DFS capability contradicts DFS flag"); + +tcon_exit: + free_rsp_buf(resp_buftype, rsp); + kfree(unc_path); + return rc; + +tcon_error_exit: + if (rsp->hdr.Status == STATUS_BAD_NETWORK_NAME) { + cERROR(1, "BAD_NETWORK_NAME: %s", tree); + tcon->bad_network_name = true; + } + goto tcon_exit; +} + +int +SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon) +{ + struct smb2_tree_disconnect_req *req; /* response is trivial */ + int rc = 0; + struct TCP_Server_Info *server; + struct cifs_ses *ses = tcon->ses; + + cFYI(1, "Tree Disconnect"); + + if (ses && (ses->server)) + server = ses->server; + else + return -EIO; + + if ((tcon->need_reconnect) || (tcon->ses->need_reconnect)) + return 0; + + rc = small_smb2_init(SMB2_TREE_DISCONNECT, tcon, (void **) &req); + if (rc) + return rc; + + rc = SendReceiveNoRsp(xid, ses, (char *)&req->hdr, 0); + if (rc) + cifs_stats_fail_inc(tcon, SMB2_TREE_DISCONNECT_HE); + + return rc; +} diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index 26af68b2955a..aa77bf3a7a69 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h @@ -224,4 +224,61 @@ struct smb2_logoff_rsp { __le16 Reserved; } __packed; +struct smb2_tree_connect_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 9 */ + __le16 Reserved; + __le16 PathOffset; + __le16 PathLength; + __u8 Buffer[1]; /* variable length */ +} __packed; + +struct smb2_tree_connect_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 16 */ + __u8 ShareType; /* see below */ + __u8 Reserved; + __le32 ShareFlags; /* see below */ + __le32 Capabilities; /* see below */ + __le32 MaximalAccess; +} __packed; + +/* Possible ShareType values */ +#define SMB2_SHARE_TYPE_DISK 0x01 +#define SMB2_SHARE_TYPE_PIPE 0x02 +#define SMB2_SHARE_TYPE_PRINT 0x03 + +/* + * Possible ShareFlags - exactly one and only one of the first 4 caching flags + * must be set (any of the remaining, SHI1005, flags may be set individually + * or in combination. + */ +#define SMB2_SHAREFLAG_MANUAL_CACHING 0x00000000 +#define SMB2_SHAREFLAG_AUTO_CACHING 0x00000010 +#define SMB2_SHAREFLAG_VDO_CACHING 0x00000020 +#define SMB2_SHAREFLAG_NO_CACHING 0x00000030 +#define SHI1005_FLAGS_DFS 0x00000001 +#define SHI1005_FLAGS_DFS_ROOT 0x00000002 +#define SHI1005_FLAGS_RESTRICT_EXCLUSIVE_OPENS 0x00000100 +#define SHI1005_FLAGS_FORCE_SHARED_DELETE 0x00000200 +#define SHI1005_FLAGS_ALLOW_NAMESPACE_CACHING 0x00000400 +#define SHI1005_FLAGS_ACCESS_BASED_DIRECTORY_ENUM 0x00000800 +#define SHI1005_FLAGS_FORCE_LEVELII_OPLOCK 0x00001000 +#define SHI1005_FLAGS_ENABLE_HASH 0x00002000 + +/* Possible share capabilities */ +#define SMB2_SHARE_CAP_DFS cpu_to_le32(0x00000008) + +struct smb2_tree_disconnect_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __le16 Reserved; +} __packed; + +struct smb2_tree_disconnect_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __le16 Reserved; +} __packed; + #endif /* _SMB2PDU_H */ diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index 9364fbcb90c6..bc7299349dbf 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h @@ -50,5 +50,9 @@ extern int SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses); extern int SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, const struct nls_table *nls_cp); extern int SMB2_logoff(const unsigned int xid, struct cifs_ses *ses); +extern int SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, + const char *tree, struct cifs_tcon *tcon, + const struct nls_table *); +extern int SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon); #endif /* _SMB2PROTO_H */ -- cgit v1.2.3 From aa24d1e9692411e605084938ced6b160f92df454 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Tue, 27 Dec 2011 16:23:34 +0400 Subject: CIFS: Process reconnects for SMB2 shares Signed-off-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/cifsproto.h | 1 + fs/cifs/cifssmb.c | 21 +++++---- fs/cifs/connect.c | 3 ++ fs/cifs/smb2pdu.c | 127 +++++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 143 insertions(+), 9 deletions(-) (limited to 'fs/cifs/smb2pdu.c') diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 3b4d41f9ceeb..61baaa3330fb 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -171,6 +171,7 @@ extern struct smb_vol *cifs_get_volume_info(char *mount_data, const char *devname); extern int cifs_mount(struct cifs_sb_info *, struct smb_vol *); extern void cifs_umount(struct cifs_sb_info *); +extern void cifs_mark_open_files_invalid(struct cifs_tcon *tcon); #if IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) extern void cifs_dfs_release_automount_timer(void); diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index dcb0ad87e173..f1dfc7844f1b 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -112,24 +112,29 @@ cifs_kmap_unlock(void) #define cifs_kmap_unlock() do { ; } while(0) #endif /* CONFIG_HIGHMEM */ -/* Mark as invalid, all open files on tree connections since they - were closed when session to server was lost */ -static void mark_open_files_invalid(struct cifs_tcon *pTcon) +/* + * Mark as invalid, all open files on tree connections since they + * were closed when session to server was lost. + */ +void +cifs_mark_open_files_invalid(struct cifs_tcon *tcon) { struct cifsFileInfo *open_file = NULL; struct list_head *tmp; struct list_head *tmp1; -/* list all files open on tree connection and mark them invalid */ + /* list all files open on tree connection and mark them invalid */ spin_lock(&cifs_file_list_lock); - list_for_each_safe(tmp, tmp1, &pTcon->openFileList) { + list_for_each_safe(tmp, tmp1, &tcon->openFileList) { open_file = list_entry(tmp, struct cifsFileInfo, tlist); open_file->invalidHandle = true; open_file->oplock_break_cancelled = true; } spin_unlock(&cifs_file_list_lock); - /* BB Add call to invalidate_inodes(sb) for all superblocks mounted - to this tcon */ + /* + * BB Add call to invalidate_inodes(sb) for all superblocks mounted + * to this tcon. + */ } /* reconnect the socket, tcon, and smb session if needed */ @@ -209,7 +214,7 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command) goto out; } - mark_open_files_invalid(tcon); + cifs_mark_open_files_invalid(tcon); rc = CIFSTCon(0, ses, tcon->treeName, tcon, nls_codepage); mutex_unlock(&ses->session_mutex); cFYI(1, "reconnect tcon rc = %d", rc); diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index a6197224b102..7cf8b1632242 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -317,6 +317,9 @@ cifs_reconnect(struct TCP_Server_Info *server) server->tcpStatus = CifsNeedReconnect; spin_unlock(&GlobalMid_Lock); server->maxBuf = 0; +#ifdef CONFIG_CIFS_SMB2 + server->max_read = 0; +#endif cFYI(1, "Reconnecting tcp session"); diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 1bf037ec5a9d..48c04b2832e2 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -127,7 +127,132 @@ static int smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon) { int rc = 0; - /* BB add missing code here */ + struct nls_table *nls_codepage; + struct cifs_ses *ses; + struct TCP_Server_Info *server; + + /* + * SMB2s NegProt, SessSetup, Logoff do not have tcon yet so + * check for tcp and smb session status done differently + * for those three - in the calling routine. + */ + if (tcon == NULL) + return rc; + + if (smb2_command == SMB2_TREE_CONNECT) + return rc; + + if (tcon->tidStatus == CifsExiting) { + /* + * only tree disconnect, open, and write, + * (and ulogoff which does not have tcon) + * are allowed as we start force umount. + */ + if ((smb2_command != SMB2_WRITE) && + (smb2_command != SMB2_CREATE) && + (smb2_command != SMB2_TREE_DISCONNECT)) { + cFYI(1, "can not send cmd %d while umounting", + smb2_command); + return -ENODEV; + } + } + if ((!tcon->ses) || (tcon->ses->status == CifsExiting) || + (!tcon->ses->server)) + return -EIO; + + ses = tcon->ses; + server = ses->server; + + /* + * Give demultiplex thread up to 10 seconds to reconnect, should be + * greater than cifs socket timeout which is 7 seconds + */ + while (server->tcpStatus == CifsNeedReconnect) { + /* + * Return to caller for TREE_DISCONNECT and LOGOFF and CLOSE + * here since they are implicitly done when session drops. + */ + switch (smb2_command) { + /* + * BB Should we keep oplock break and add flush to exceptions? + */ + case SMB2_TREE_DISCONNECT: + case SMB2_CANCEL: + case SMB2_CLOSE: + case SMB2_OPLOCK_BREAK: + return -EAGAIN; + } + + wait_event_interruptible_timeout(server->response_q, + (server->tcpStatus != CifsNeedReconnect), 10 * HZ); + + /* are we still trying to reconnect? */ + if (server->tcpStatus != CifsNeedReconnect) + break; + + /* + * on "soft" mounts we wait once. Hard mounts keep + * retrying until process is killed or server comes + * back on-line + */ + if (!tcon->retry) { + cFYI(1, "gave up waiting on reconnect in smb_init"); + return -EHOSTDOWN; + } + } + + if (!tcon->ses->need_reconnect && !tcon->need_reconnect) + return rc; + + nls_codepage = load_nls_default(); + + /* + * need to prevent multiple threads trying to simultaneously reconnect + * the same SMB session + */ + mutex_lock(&tcon->ses->session_mutex); + rc = cifs_negotiate_protocol(0, tcon->ses); + if (!rc && tcon->ses->need_reconnect) + rc = cifs_setup_session(0, tcon->ses, nls_codepage); + + if (rc || !tcon->need_reconnect) { + mutex_unlock(&tcon->ses->session_mutex); + goto out; + } + + cifs_mark_open_files_invalid(tcon); + rc = SMB2_tcon(0, tcon->ses, tcon->treeName, tcon, nls_codepage); + mutex_unlock(&tcon->ses->session_mutex); + cFYI(1, "reconnect tcon rc = %d", rc); + if (rc) + goto out; + atomic_inc(&tconInfoReconnectCount); + /* + * BB FIXME add code to check if wsize needs update due to negotiated + * smb buffer size shrinking. + */ +out: + /* + * Check if handle based operation so we know whether we can continue + * or not without returning to caller to reset file handle. + */ + /* + * BB Is flush done by server on drop of tcp session? Should we special + * case it and skip above? + */ + switch (smb2_command) { + case SMB2_FLUSH: + case SMB2_READ: + case SMB2_WRITE: + case SMB2_LOCK: + case SMB2_IOCTL: + case SMB2_QUERY_DIRECTORY: + case SMB2_CHANGE_NOTIFY: + case SMB2_QUERY_INFO: + case SMB2_SET_INFO: + return -EAGAIN; + } + unload_nls(nls_codepage); return rc; } -- cgit v1.2.3 From 2503a0dba989486c59523a947a1dcb50ad90fee9 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Mon, 26 Dec 2011 22:58:46 +0400 Subject: CIFS: Add SMB2 support for is_path_accessible that needs for a successful mount through SMB2 protocol. Signed-off-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/cifs_unicode.c | 61 ++++++++++++++++++ fs/cifs/cifs_unicode.h | 5 ++ fs/cifs/smb2misc.c | 25 ++++++++ fs/cifs/smb2ops.c | 25 ++++++++ fs/cifs/smb2pdu.c | 132 ++++++++++++++++++++++++++++++++++++++ fs/cifs/smb2pdu.h | 167 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/cifs/smb2proto.h | 8 +++ 7 files changed, 423 insertions(+) (limited to 'fs/cifs/smb2pdu.c') diff --git a/fs/cifs/cifs_unicode.c b/fs/cifs/cifs_unicode.c index 97c1d4210869..7dab9c04ad52 100644 --- a/fs/cifs/cifs_unicode.c +++ b/fs/cifs/cifs_unicode.c @@ -330,3 +330,64 @@ cifsConvertToUTF16(__le16 *target, const char *source, int srclen, ctoUTF16_out: return i; } + +#ifdef CONFIG_CIFS_SMB2 +/* + * cifs_local_to_utf16_bytes - how long will a string be after conversion? + * @from - pointer to input string + * @maxbytes - don't go past this many bytes of input string + * @codepage - source codepage + * + * Walk a string and return the number of bytes that the string will + * be after being converted to the given charset, not including any null + * termination required. Don't walk past maxbytes in the source buffer. + */ + +static int +cifs_local_to_utf16_bytes(const char *from, int len, + const struct nls_table *codepage) +{ + int charlen; + int i; + wchar_t wchar_to; + + for (i = 0; len && *from; i++, from += charlen, len -= charlen) { + charlen = codepage->char2uni(from, len, &wchar_to); + /* Failed conversion defaults to a question mark */ + if (charlen < 1) + charlen = 1; + } + return 2 * i; /* UTF16 characters are two bytes */ +} + +/* + * cifs_strndup_to_utf16 - copy a string to wire format from the local codepage + * @src - source string + * @maxlen - don't walk past this many bytes in the source string + * @utf16_len - the length of the allocated string in bytes (including null) + * @cp - source codepage + * @remap - map special chars + * + * Take a string convert it from the local codepage to UTF16 and + * put it in a new buffer. Returns a pointer to the new string or NULL on + * error. + */ +__le16 * +cifs_strndup_to_utf16(const char *src, const int maxlen, int *utf16_len, + const struct nls_table *cp, int remap) +{ + int len; + __le16 *dst; + + len = cifs_local_to_utf16_bytes(src, maxlen, cp); + len += 2; /* NULL */ + dst = kmalloc(len, GFP_KERNEL); + if (!dst) { + *utf16_len = 0; + return NULL; + } + cifsConvertToUTF16(dst, src, strlen(src), cp, remap); + *utf16_len = len; + return dst; +} +#endif /* CONFIG_CIFS_SMB2 */ diff --git a/fs/cifs/cifs_unicode.h b/fs/cifs/cifs_unicode.h index a44c6eb8a4d7..4fb097468e21 100644 --- a/fs/cifs/cifs_unicode.h +++ b/fs/cifs/cifs_unicode.h @@ -84,6 +84,11 @@ char *cifs_strndup_from_utf16(const char *src, const int maxlen, const struct nls_table *codepage); extern int cifsConvertToUTF16(__le16 *target, const char *source, int maxlen, const struct nls_table *cp, int mapChars); +#ifdef CONFIG_CIFS_SMB2 +extern __le16 *cifs_strndup_to_utf16(const char *src, const int maxlen, + int *utf16_len, const struct nls_table *cp, + int remap); +#endif /* CONFIG_CIFS_SMB2 */ #endif /* diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c index 10729a74da27..eb73a136641c 100644 --- a/fs/cifs/smb2misc.c +++ b/fs/cifs/smb2misc.c @@ -230,6 +230,11 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr) ((struct smb2_sess_setup_rsp *)hdr)->SecurityBufferLength); break; case SMB2_CREATE: + *off = le32_to_cpu( + ((struct smb2_create_rsp *)hdr)->CreateContextsOffset); + *len = le32_to_cpu( + ((struct smb2_create_rsp *)hdr)->CreateContextsLength); + break; case SMB2_READ: case SMB2_QUERY_INFO: case SMB2_QUERY_DIRECTORY: @@ -315,3 +320,23 @@ calc_size_exit: cFYI(1, "SMB2 len %d", len); return len; } + +/* Note: caller must free return buffer */ +__le16 * +cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb) +{ + int len; + const char *start_of_path; + __le16 *to; + + /* Windows doesn't allow paths beginning with \ */ + if (from[0] == '\\') + start_of_path = from + 1; + else + start_of_path = from; + to = cifs_strndup_to_utf16(start_of_path, PATH_MAX, &len, + cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & + CIFS_MOUNT_MAP_SPECIAL_CHR); + return to; +} diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 0e33ca32abf9..1266137406fa 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -157,6 +157,30 @@ smb2_negotiate(const unsigned int xid, struct cifs_ses *ses) return rc; } +static int +smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path) +{ + int rc; + __u64 persistent_fid, volatile_fid; + __le16 *utf16_path; + + utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); + if (!utf16_path) + return -ENOMEM; + + rc = SMB2_open(xid, tcon, utf16_path, &persistent_fid, &volatile_fid, + FILE_READ_ATTRIBUTES, FILE_OPEN, 0, 0); + if (rc) { + kfree(utf16_path); + return rc; + } + + rc = SMB2_close(xid, tcon, persistent_fid, volatile_fid); + kfree(utf16_path); + return rc; +} + struct smb_version_operations smb21_operations = { .setup_request = smb2_setup_request, .check_receive = smb2_check_receive, @@ -174,6 +198,7 @@ struct smb_version_operations smb21_operations = { .logoff = SMB2_logoff, .tree_connect = SMB2_tcon, .tree_disconnect = SMB2_tdis, + .is_path_accessible = smb2_is_path_accessible, }; struct smb_version_values smb21_values = { diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 48c04b2832e2..ef0769c398a5 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -829,3 +829,135 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon) return rc; } + +int +SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path, + u64 *persistent_fid, u64 *volatile_fid, __u32 desired_access, + __u32 create_disposition, __u32 file_attributes, __u32 create_options) +{ + struct smb2_create_req *req; + struct smb2_create_rsp *rsp; + struct TCP_Server_Info *server; + struct cifs_ses *ses = tcon->ses; + struct kvec iov[2]; + int resp_buftype; + int uni_path_len; + int rc = 0; + int num_iovecs = 2; + + cFYI(1, "create/open"); + + if (ses && (ses->server)) + server = ses->server; + else + return -EIO; + + rc = small_smb2_init(SMB2_CREATE, tcon, (void **) &req); + if (rc) + return rc; + + if (enable_oplocks) + req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_BATCH; + else + req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_NONE; + req->ImpersonationLevel = IL_IMPERSONATION; + req->DesiredAccess = cpu_to_le32(desired_access); + /* 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(create_disposition); + req->CreateOptions = cpu_to_le32(create_options); + uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2; + req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req) + - 1 /* pad */ - 4 /* do not count rfc1001 len field */); + + iov[0].iov_base = (char *)req; + /* 4 for rfc1002 length field */ + iov[0].iov_len = get_rfc1002_length(req) + 4; + + /* MUST set path len (NameLength) to 0 opening root of share */ + if (uni_path_len >= 4) { + req->NameLength = cpu_to_le16(uni_path_len - 2); + /* -1 since last byte is buf[0] which is sent below (path) */ + iov[0].iov_len--; + iov[1].iov_len = uni_path_len; + iov[1].iov_base = path; + /* + * -1 since last byte is buf[0] which was counted in + * smb2_buf_len. + */ + inc_rfc1001_len(req, uni_path_len - 1); + } else { + num_iovecs = 1; + req->NameLength = 0; + } + + rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0); + rsp = (struct smb2_create_rsp *)iov[0].iov_base; + + if (rc != 0) { + cifs_stats_fail_inc(tcon, SMB2_CREATE_HE); + goto creat_exit; + } + + if (rsp == NULL) { + rc = -EIO; + goto creat_exit; + } + *persistent_fid = rsp->PersistentFileId; + *volatile_fid = rsp->VolatileFileId; +creat_exit: + free_rsp_buf(resp_buftype, rsp); + return rc; +} + +int +SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid) +{ + struct smb2_close_req *req; + struct smb2_close_rsp *rsp; + struct TCP_Server_Info *server; + struct cifs_ses *ses = tcon->ses; + struct kvec iov[1]; + int resp_buftype; + int rc = 0; + + cFYI(1, "Close"); + + if (ses && (ses->server)) + server = ses->server; + else + return -EIO; + + rc = small_smb2_init(SMB2_CLOSE, tcon, (void **) &req); + if (rc) + return rc; + + req->PersistentFileId = persistent_fid; + req->VolatileFileId = volatile_fid; + + iov[0].iov_base = (char *)req; + /* 4 for rfc1002 length field */ + iov[0].iov_len = get_rfc1002_length(req) + 4; + + rc = SendReceive2(xid, ses, iov, 1, &resp_buftype, 0); + rsp = (struct smb2_close_rsp *)iov[0].iov_base; + + if (rc != 0) { + if (tcon) + cifs_stats_fail_inc(tcon, SMB2_CLOSE_HE); + goto close_exit; + } + + if (rsp == NULL) { + rc = -EIO; + goto close_exit; + } + + /* BB FIXME - decode close response, update inode for caching */ + +close_exit: + free_rsp_buf(resp_buftype, rsp); + return rc; +} diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index aa77bf3a7a69..5cd358ef312e 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h @@ -281,4 +281,171 @@ struct smb2_tree_disconnect_rsp { __le16 Reserved; } __packed; +/* File Attrubutes */ +#define FILE_ATTRIBUTE_READONLY 0x00000001 +#define FILE_ATTRIBUTE_HIDDEN 0x00000002 +#define FILE_ATTRIBUTE_SYSTEM 0x00000004 +#define FILE_ATTRIBUTE_DIRECTORY 0x00000010 +#define FILE_ATTRIBUTE_ARCHIVE 0x00000020 +#define FILE_ATTRIBUTE_NORMAL 0x00000080 +#define FILE_ATTRIBUTE_TEMPORARY 0x00000100 +#define FILE_ATTRIBUTE_SPARSE_FILE 0x00000200 +#define FILE_ATTRIBUTE_REPARSE_POINT 0x00000400 +#define FILE_ATTRIBUTE_COMPRESSED 0x00000800 +#define FILE_ATTRIBUTE_OFFLINE 0x00001000 +#define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED 0x00002000 +#define FILE_ATTRIBUTE_ENCRYPTED 0x00004000 + +/* Oplock levels */ +#define SMB2_OPLOCK_LEVEL_NONE 0x00 +#define SMB2_OPLOCK_LEVEL_II 0x01 +#define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08 +#define SMB2_OPLOCK_LEVEL_BATCH 0x09 +#define SMB2_OPLOCK_LEVEL_LEASE 0xFF + +/* Desired Access Flags */ +#define FILE_READ_DATA_LE cpu_to_le32(0x00000001) +#define FILE_WRITE_DATA_LE cpu_to_le32(0x00000002) +#define FILE_APPEND_DATA_LE cpu_to_le32(0x00000004) +#define FILE_READ_EA_LE cpu_to_le32(0x00000008) +#define FILE_WRITE_EA_LE cpu_to_le32(0x00000010) +#define FILE_EXECUTE_LE cpu_to_le32(0x00000020) +#define FILE_READ_ATTRIBUTES_LE cpu_to_le32(0x00000080) +#define FILE_WRITE_ATTRIBUTES_LE cpu_to_le32(0x00000100) +#define FILE_DELETE_LE cpu_to_le32(0x00010000) +#define FILE_READ_CONTROL_LE cpu_to_le32(0x00020000) +#define FILE_WRITE_DAC_LE cpu_to_le32(0x00040000) +#define FILE_WRITE_OWNER_LE cpu_to_le32(0x00080000) +#define FILE_SYNCHRONIZE_LE cpu_to_le32(0x00100000) +#define FILE_ACCESS_SYSTEM_SECURITY_LE cpu_to_le32(0x01000000) +#define FILE_MAXIMAL_ACCESS_LE cpu_to_le32(0x02000000) +#define FILE_GENERIC_ALL_LE cpu_to_le32(0x10000000) +#define FILE_GENERIC_EXECUTE_LE cpu_to_le32(0x20000000) +#define FILE_GENERIC_WRITE_LE cpu_to_le32(0x40000000) +#define FILE_GENERIC_READ_LE cpu_to_le32(0x80000000) + +/* ShareAccess Flags */ +#define FILE_SHARE_READ_LE cpu_to_le32(0x00000001) +#define FILE_SHARE_WRITE_LE cpu_to_le32(0x00000002) +#define FILE_SHARE_DELETE_LE cpu_to_le32(0x00000004) +#define FILE_SHARE_ALL_LE cpu_to_le32(0x00000007) + +/* CreateDisposition Flags */ +#define FILE_SUPERSEDE_LE cpu_to_le32(0x00000000) +#define FILE_OPEN_LE cpu_to_le32(0x00000001) +#define FILE_CREATE_LE cpu_to_le32(0x00000002) +#define FILE_OPEN_IF_LE cpu_to_le32(0x00000003) +#define FILE_OVERWRITE_LE cpu_to_le32(0x00000004) +#define FILE_OVERWRITE_IF_LE cpu_to_le32(0x00000005) + +/* CreateOptions Flags */ +#define FILE_DIRECTORY_FILE_LE cpu_to_le32(0x00000001) +/* same as #define CREATE_NOT_FILE_LE cpu_to_le32(0x00000001) */ +#define FILE_WRITE_THROUGH_LE cpu_to_le32(0x00000002) +#define FILE_SEQUENTIAL_ONLY_LE cpu_to_le32(0x00000004) +#define FILE_NO_INTERMEDIATE_BUFFERRING_LE cpu_to_le32(0x00000008) +#define FILE_SYNCHRONOUS_IO_ALERT_LE cpu_to_le32(0x00000010) +#define FILE_SYNCHRONOUS_IO_NON_ALERT_LE cpu_to_le32(0x00000020) +#define FILE_NON_DIRECTORY_FILE_LE cpu_to_le32(0x00000040) +#define FILE_COMPLETE_IF_OPLOCKED_LE cpu_to_le32(0x00000100) +#define FILE_NO_EA_KNOWLEDGE_LE cpu_to_le32(0x00000200) +#define FILE_RANDOM_ACCESS_LE cpu_to_le32(0x00000800) +#define FILE_DELETE_ON_CLOSE_LE cpu_to_le32(0x00001000) +#define FILE_OPEN_BY_FILE_ID_LE cpu_to_le32(0x00002000) +#define FILE_OPEN_FOR_BACKUP_INTENT_LE cpu_to_le32(0x00004000) +#define FILE_NO_COMPRESSION_LE cpu_to_le32(0x00008000) +#define FILE_RESERVE_OPFILTER_LE cpu_to_le32(0x00100000) +#define FILE_OPEN_REPARSE_POINT_LE cpu_to_le32(0x00200000) +#define FILE_OPEN_NO_RECALL_LE cpu_to_le32(0x00400000) +#define FILE_OPEN_FOR_FREE_SPACE_QUERY_LE cpu_to_le32(0x00800000) + +#define FILE_READ_RIGHTS_LE (FILE_READ_DATA_LE | FILE_READ_EA_LE \ + | FILE_READ_ATTRIBUTES_LE) +#define FILE_WRITE_RIGHTS_LE (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE \ + | FILE_WRITE_EA_LE | FILE_WRITE_ATTRIBUTES_LE) +#define FILE_EXEC_RIGHTS_LE (FILE_EXECUTE_LE) + +/* Impersonation Levels */ +#define IL_ANONYMOUS cpu_to_le32(0x00000000) +#define IL_IDENTIFICATION cpu_to_le32(0x00000001) +#define IL_IMPERSONATION cpu_to_le32(0x00000002) +#define IL_DELEGATE cpu_to_le32(0x00000003) + +/* Create Context Values */ +#define SMB2_CREATE_EA_BUFFER "ExtA" /* extended attributes */ +#define SMB2_CREATE_SD_BUFFER "SecD" /* security descriptor */ +#define SMB2_CREATE_DURABLE_HANDLE_REQUEST "DHnQ" +#define SMB2_CREATE_DURABLE_HANDLE_RECONNECT "DHnC" +#define SMB2_CREATE_ALLOCATION_SIZE "AlSi" +#define SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST "MxAc" +#define SMB2_CREATE_TIMEWARP_REQUEST "TWrp" +#define SMB2_CREATE_QUERY_ON_DISK_ID "QFid" +#define SMB2_CREATE_REQUEST_LEASE "RqLs" + +struct smb2_create_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 57 */ + __u8 SecurityFlags; + __u8 RequestedOplockLevel; + __le32 ImpersonationLevel; + __le64 SmbCreateFlags; + __le64 Reserved; + __le32 DesiredAccess; + __le32 FileAttributes; + __le32 ShareAccess; + __le32 CreateDisposition; + __le32 CreateOptions; + __le16 NameOffset; + __le16 NameLength; + __le32 CreateContextsOffset; + __le32 CreateContextsLength; + __u8 Buffer[1]; +} __packed; + +struct smb2_create_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 89 */ + __u8 OplockLevel; + __u8 Reserved; + __le32 CreateAction; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 AllocationSize; + __le64 EndofFile; + __le32 FileAttributes; + __le32 Reserved2; + __u64 PersistentFileId; /* opaque endianness */ + __u64 VolatileFileId; /* opaque endianness */ + __le32 CreateContextsOffset; + __le32 CreateContextsLength; + __u8 Buffer[1]; +} __packed; + +/* Currently defined values for close flags */ +#define SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB cpu_to_le16(0x0001) +struct smb2_close_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 24 */ + __le16 Flags; + __le32 Reserved; + __u64 PersistentFileId; /* opaque endianness */ + __u64 VolatileFileId; /* opaque endianness */ +} __packed; + +struct smb2_close_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* 60 */ + __le16 Flags; + __le32 Reserved; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 AllocationSize; /* Beginning of FILE_STANDARD_INFO equivalent */ + __le64 EndOfFile; + __le32 Attributes; +} __packed; + #endif /* _SMB2PDU_H */ diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index bc7299349dbf..85aa8d5ea41a 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h @@ -36,6 +36,8 @@ extern int map_smb2_to_linux_error(char *buf, bool log_err); extern int smb2_check_message(char *buf, unsigned int length); extern unsigned int smb2_calc_size(struct smb2_hdr *hdr); extern char *smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr); +extern __le16 *cifs_convert_path_to_utf16(const char *from, + struct cifs_sb_info *cifs_sb); extern int smb2_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, bool log_error); @@ -54,5 +56,11 @@ extern int SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, struct cifs_tcon *tcon, const struct nls_table *); extern int SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon); +extern int SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, + __le16 *path, u64 *persistent_fid, u64 *volatile_fid, + __u32 desired_access, __u32 create_disposition, + __u32 file_attributes, __u32 create_options); +extern int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_file_id, u64 volatile_file_id); #endif /* _SMB2PROTO_H */ -- cgit v1.2.3 From be4cb9e3d4ef7af1aaf66cebab1391ff91b48beb Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Thu, 29 Dec 2011 17:06:33 +0400 Subject: CIFS: Query SMB2 inode info Signed-off-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/Makefile | 2 +- fs/cifs/smb2glob.h | 44 +++++++++++++++++++ fs/cifs/smb2inode.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/cifs/smb2misc.c | 7 ++- fs/cifs/smb2ops.c | 11 +++++ fs/cifs/smb2pdu.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++ fs/cifs/smb2pdu.h | 111 ++++++++++++++++++++++++++++++++++++++++++++++ fs/cifs/smb2proto.h | 7 +++ 8 files changed, 417 insertions(+), 2 deletions(-) create mode 100644 fs/cifs/smb2glob.h create mode 100644 fs/cifs/smb2inode.c (limited to 'fs/cifs/smb2pdu.c') diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile index daf6837d9e0e..feee94309271 100644 --- a/fs/cifs/Makefile +++ b/fs/cifs/Makefile @@ -17,4 +17,4 @@ cifs-$(CONFIG_CIFS_DFS_UPCALL) += dns_resolve.o cifs_dfs_ref.o cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o cache.o cifs-$(CONFIG_CIFS_SMB2) += smb2ops.o smb2maperror.o smb2transport.o \ - smb2misc.o smb2pdu.o + smb2misc.o smb2pdu.o smb2inode.o diff --git a/fs/cifs/smb2glob.h b/fs/cifs/smb2glob.h new file mode 100644 index 000000000000..33c1d89090c0 --- /dev/null +++ b/fs/cifs/smb2glob.h @@ -0,0 +1,44 @@ +/* + * fs/cifs/smb2glob.h + * + * Definitions for various global variables and structures + * + * Copyright (C) International Business Machines Corp., 2002, 2011 + * Etersoft, 2012 + * Author(s): Steve French (sfrench@us.ibm.com) + * Jeremy Allison (jra@samba.org) + * Pavel Shilovsky (pshilovsky@samba.org) 2012 + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU Lesser General Public License for more details. + * + */ +#ifndef _SMB2_GLOB_H +#define _SMB2_GLOB_H + +/* + ***************************************************************** + * Constants go here + ***************************************************************** + */ + +/* + * Identifiers for functions that use the open, operation, close pattern + * in smb2inode.c:smb2_open_op_close() + */ +#define SMB2_OP_SET_DELETE 1 +#define SMB2_OP_SET_INFO 2 +#define SMB2_OP_QUERY_INFO 3 +#define SMB2_OP_QUERY_DIR 4 +#define SMB2_OP_MKDIR 5 +#define SMB2_OP_RENAME 6 +#define SMB2_OP_DELETE 7 + +#endif /* _SMB2_GLOB_H */ diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c new file mode 100644 index 000000000000..1ba5c405315c --- /dev/null +++ b/fs/cifs/smb2inode.c @@ -0,0 +1,124 @@ +/* + * fs/cifs/smb2inode.c + * + * Copyright (C) International Business Machines Corp., 2002, 2011 + * Etersoft, 2012 + * Author(s): Pavel Shilovsky (pshilovsky@samba.org), + * Steve French (sfrench@us.ibm.com) + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include "cifsfs.h" +#include "cifspdu.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_debug.h" +#include "cifs_fs_sb.h" +#include "cifs_unicode.h" +#include "fscache.h" +#include "smb2glob.h" +#include "smb2pdu.h" +#include "smb2proto.h" + +static int +smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path, + __u32 desired_access, __u32 create_disposition, + __u32 file_attributes, __u32 create_options, + void *data, int command) +{ + int rc, tmprc = 0; + u64 persistent_fid, volatile_fid; + __le16 *utf16_path; + + utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); + if (!utf16_path) + return -ENOMEM; + + rc = SMB2_open(xid, tcon, utf16_path, &persistent_fid, &volatile_fid, + desired_access, create_disposition, file_attributes, + create_options); + if (rc) { + kfree(utf16_path); + return rc; + } + + switch (command) { + case SMB2_OP_DELETE: + break; + case SMB2_OP_QUERY_INFO: + tmprc = SMB2_query_info(xid, tcon, persistent_fid, + volatile_fid, + (struct smb2_file_all_info *)data); + break; + case SMB2_OP_MKDIR: + /* + * Directories are created through parameters in the + * SMB2_open() call. + */ + break; + default: + cERROR(1, "Invalid command"); + break; + } + + rc = SMB2_close(xid, tcon, persistent_fid, volatile_fid); + if (tmprc) + rc = tmprc; + kfree(utf16_path); + return rc; +} + +static void +move_smb2_info_to_cifs(FILE_ALL_INFO *dst, struct smb2_file_all_info *src) +{ + memcpy(dst, src, (size_t)(&src->CurrentByteOffset) - (size_t)src); + dst->CurrentByteOffset = src->CurrentByteOffset; + dst->Mode = src->Mode; + dst->AlignmentRequirement = src->AlignmentRequirement; + dst->IndexNumber1 = 0; /* we don't use it */ +} + +int +smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path, + FILE_ALL_INFO *data, bool *adjust_tz) +{ + int rc; + struct smb2_file_all_info *smb2_data; + + *adjust_tz = false; + + smb2_data = kzalloc(sizeof(struct smb2_file_all_info) + MAX_NAME * 2, + GFP_KERNEL); + if (smb2_data == NULL) + return -ENOMEM; + + rc = smb2_open_op_close(xid, tcon, cifs_sb, full_path, + FILE_READ_ATTRIBUTES, FILE_OPEN, 0, 0, + smb2_data, SMB2_OP_QUERY_INFO); + if (rc) + goto out; + + move_smb2_info_to_cifs(data, smb2_data); +out: + kfree(smb2_data); + return rc; +} diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c index eb73a136641c..a4ff5d547554 100644 --- a/fs/cifs/smb2misc.c +++ b/fs/cifs/smb2misc.c @@ -235,8 +235,13 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr) *len = le32_to_cpu( ((struct smb2_create_rsp *)hdr)->CreateContextsLength); break; - case SMB2_READ: case SMB2_QUERY_INFO: + *off = le16_to_cpu( + ((struct smb2_query_info_rsp *)hdr)->OutputBufferOffset); + *len = le32_to_cpu( + ((struct smb2_query_info_rsp *)hdr)->OutputBufferLength); + break; + case SMB2_READ: case SMB2_QUERY_DIRECTORY: case SMB2_IOCTL: case SMB2_CHANGE_NOTIFY: diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 1266137406fa..bcf310c8b784 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -181,6 +181,15 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon, return rc; } +static int +smb2_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, const char *full_path, + u64 *uniqueid, FILE_ALL_INFO *data) +{ + *uniqueid = le64_to_cpu(data->IndexNumber); + return 0; +} + struct smb_version_operations smb21_operations = { .setup_request = smb2_setup_request, .check_receive = smb2_check_receive, @@ -199,6 +208,8 @@ struct smb_version_operations smb21_operations = { .tree_connect = SMB2_tcon, .tree_disconnect = SMB2_tdis, .is_path_accessible = smb2_is_path_accessible, + .query_path_info = smb2_query_path_info, + .get_srv_inum = smb2_get_srv_inum, }; struct smb_version_values smb21_values = { diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index ef0769c398a5..7ef5324786a6 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -961,3 +961,116 @@ close_exit: free_rsp_buf(resp_buftype, rsp); return rc; } + +static int +validate_buf(unsigned int offset, unsigned int buffer_length, + struct smb2_hdr *hdr, unsigned int min_buf_size) + +{ + unsigned int smb_len = be32_to_cpu(hdr->smb2_buf_length); + char *end_of_smb = smb_len + 4 /* RFC1001 length field */ + (char *)hdr; + char *begin_of_buf = 4 /* RFC1001 len field */ + offset + (char *)hdr; + char *end_of_buf = begin_of_buf + buffer_length; + + + if (buffer_length < min_buf_size) { + cERROR(1, "buffer length %d smaller than minimum size %d", + buffer_length, min_buf_size); + return -EINVAL; + } + + /* check if beyond RFC1001 maximum length */ + if ((smb_len > 0x7FFFFF) || (buffer_length > 0x7FFFFF)) { + cERROR(1, "buffer length %d or smb length %d too large", + buffer_length, smb_len); + return -EINVAL; + } + + if ((begin_of_buf > end_of_smb) || (end_of_buf > end_of_smb)) { + cERROR(1, "illegal server response, bad offset to data"); + return -EINVAL; + } + + return 0; +} + +/* + * If SMB buffer fields are valid, copy into temporary buffer to hold result. + * Caller must free buffer. + */ +static int +validate_and_copy_buf(unsigned int offset, unsigned int buffer_length, + struct smb2_hdr *hdr, unsigned int minbufsize, + char *data) + +{ + char *begin_of_buf = 4 /* RFC1001 len field */ + offset + (char *)hdr; + int rc; + + if (!data) + return -EINVAL; + + rc = validate_buf(offset, buffer_length, hdr, minbufsize); + if (rc) + return rc; + + memcpy(data, begin_of_buf, buffer_length); + + return 0; +} + +int +SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, + struct smb2_file_all_info *data) +{ + struct smb2_query_info_req *req; + struct smb2_query_info_rsp *rsp = NULL; + struct kvec iov[2]; + int rc = 0; + int resp_buftype; + struct TCP_Server_Info *server; + struct cifs_ses *ses = tcon->ses; + + cFYI(1, "Query Info"); + + if (ses && (ses->server)) + server = ses->server; + else + return -EIO; + + rc = small_smb2_init(SMB2_QUERY_INFO, tcon, (void **) &req); + if (rc) + return rc; + + req->InfoType = SMB2_O_INFO_FILE; + req->FileInfoClass = FILE_ALL_INFORMATION; + req->PersistentFileId = persistent_fid; + req->VolatileFileId = volatile_fid; + /* 4 for rfc1002 length field and 1 for Buffer */ + req->InputBufferOffset = + cpu_to_le16(sizeof(struct smb2_query_info_req) - 1 - 4); + req->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_all_info) + MAX_NAME * 2); + + iov[0].iov_base = (char *)req; + /* 4 for rfc1002 length field */ + iov[0].iov_len = get_rfc1002_length(req) + 4; + + rc = SendReceive2(xid, ses, iov, 1, &resp_buftype, 0); + if (rc) { + cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); + goto qinf_exit; + } + + rsp = (struct smb2_query_info_rsp *)iov[0].iov_base; + + rc = validate_and_copy_buf(le16_to_cpu(rsp->OutputBufferOffset), + le32_to_cpu(rsp->OutputBufferLength), + &rsp->hdr, sizeof(struct smb2_file_all_info), + (char *)data); + +qinf_exit: + free_rsp_buf(resp_buftype, rsp); + return rc; +} diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index 5cd358ef312e..9151e9040b02 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h @@ -448,4 +448,115 @@ struct smb2_close_rsp { __le32 Attributes; } __packed; +/* Possible InfoType values */ +#define SMB2_O_INFO_FILE 0x01 +#define SMB2_O_INFO_FILESYSTEM 0x02 +#define SMB2_O_INFO_SECURITY 0x03 +#define SMB2_O_INFO_QUOTA 0x04 + +struct smb2_query_info_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 41 */ + __u8 InfoType; + __u8 FileInfoClass; + __le32 OutputBufferLength; + __le16 InputBufferOffset; + __u16 Reserved; + __le32 InputBufferLength; + __le32 AdditionalInformation; + __le32 Flags; + __u64 PersistentFileId; /* opaque endianness */ + __u64 VolatileFileId; /* opaque endianness */ + __u8 Buffer[1]; +} __packed; + +struct smb2_query_info_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 9 */ + __le16 OutputBufferOffset; + __le32 OutputBufferLength; + __u8 Buffer[1]; +} __packed; + +/* + * PDU infolevel structure definitions + * BB consider moving to a different header + */ + +/* partial list of QUERY INFO levels */ +#define FILE_DIRECTORY_INFORMATION 1 +#define FILE_FULL_DIRECTORY_INFORMATION 2 +#define FILE_BOTH_DIRECTORY_INFORMATION 3 +#define FILE_BASIC_INFORMATION 4 +#define FILE_STANDARD_INFORMATION 5 +#define FILE_INTERNAL_INFORMATION 6 +#define FILE_EA_INFORMATION 7 +#define FILE_ACCESS_INFORMATION 8 +#define FILE_NAME_INFORMATION 9 +#define FILE_RENAME_INFORMATION 10 +#define FILE_LINK_INFORMATION 11 +#define FILE_NAMES_INFORMATION 12 +#define FILE_DISPOSITION_INFORMATION 13 +#define FILE_POSITION_INFORMATION 14 +#define FILE_FULL_EA_INFORMATION 15 +#define FILE_MODE_INFORMATION 16 +#define FILE_ALIGNMENT_INFORMATION 17 +#define FILE_ALL_INFORMATION 18 +#define FILE_ALLOCATION_INFORMATION 19 +#define FILE_END_OF_FILE_INFORMATION 20 +#define FILE_ALTERNATE_NAME_INFORMATION 21 +#define FILE_STREAM_INFORMATION 22 +#define FILE_PIPE_INFORMATION 23 +#define FILE_PIPE_LOCAL_INFORMATION 24 +#define FILE_PIPE_REMOTE_INFORMATION 25 +#define FILE_MAILSLOT_QUERY_INFORMATION 26 +#define FILE_MAILSLOT_SET_INFORMATION 27 +#define FILE_COMPRESSION_INFORMATION 28 +#define FILE_OBJECT_ID_INFORMATION 29 +/* Number 30 not defined in documents */ +#define FILE_MOVE_CLUSTER_INFORMATION 31 +#define FILE_QUOTA_INFORMATION 32 +#define FILE_REPARSE_POINT_INFORMATION 33 +#define FILE_NETWORK_OPEN_INFORMATION 34 +#define FILE_ATTRIBUTE_TAG_INFORMATION 35 +#define FILE_TRACKING_INFORMATION 36 +#define FILEID_BOTH_DIRECTORY_INFORMATION 37 +#define FILEID_FULL_DIRECTORY_INFORMATION 38 +#define FILE_VALID_DATA_LENGTH_INFORMATION 39 +#define FILE_SHORT_NAME_INFORMATION 40 +#define FILE_SFIO_RESERVE_INFORMATION 44 +#define FILE_SFIO_VOLUME_INFORMATION 45 +#define FILE_HARD_LINK_INFORMATION 46 +#define FILE_NORMALIZED_NAME_INFORMATION 48 +#define FILEID_GLOBAL_TX_DIRECTORY_INFORMATION 50 +#define FILE_STANDARD_LINK_INFORMATION 54 + +/* + * This level 18, although with struct with same name is different from cifs + * level 0x107. Level 0x107 has an extra u64 between AccessFlags and + * CurrentByteOffset. + */ +struct smb2_file_all_info { /* data block encoding of response to level 18 */ + __le64 CreationTime; /* Beginning of FILE_BASIC_INFO equivalent */ + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le32 Attributes; + __u32 Pad1; /* End of FILE_BASIC_INFO_INFO equivalent */ + __le64 AllocationSize; /* Beginning of FILE_STANDARD_INFO equivalent */ + __le64 EndOfFile; /* size ie offset to first free byte in file */ + __le32 NumberOfLinks; /* hard links */ + __u8 DeletePending; + __u8 Directory; + __u16 Pad2; /* End of FILE_STANDARD_INFO equivalent */ + __le64 IndexNumber; + __le32 EASize; + __le32 AccessFlags; + __le64 CurrentByteOffset; + __le32 Mode; + __le32 AlignmentRequirement; + __le32 FileNameLength; + char FileName[1]; +} __packed; /* level 18 Query */ + #endif /* _SMB2PDU_H */ diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index 85aa8d5ea41a..1517b4c03c90 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h @@ -44,6 +44,10 @@ extern int smb2_check_receive(struct mid_q_entry *mid, extern int smb2_setup_request(struct cifs_ses *ses, struct kvec *iov, unsigned int nvec, struct mid_q_entry **ret_mid); +extern int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, + const char *full_path, FILE_ALL_INFO *data, + bool *adjust_tz); /* * SMB2 Worker functions - most of protocol specific implementation details * are contained within these calls. @@ -62,5 +66,8 @@ extern int SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __u32 file_attributes, __u32 create_options); extern int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_file_id, u64 volatile_file_id); +extern int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_file_id, u64 volatile_file_id, + struct smb2_file_all_info *data); #endif /* _SMB2PROTO_H */ -- cgit v1.2.3 From 9094fad1ed90caebd25b1bdec3c8982d079356ee Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Thu, 12 Jul 2012 18:30:44 +0400 Subject: CIFS: Add echo request support for SMB2 Signed-off-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 3 +++ fs/cifs/connect.c | 3 --- fs/cifs/smb2ops.c | 8 ++++++++ fs/cifs/smb2pdu.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/cifs/smb2pdu.h | 12 ++++++++++++ fs/cifs/smb2proto.h | 1 + 6 files changed, 73 insertions(+), 3 deletions(-) (limited to 'fs/cifs/smb2pdu.c') diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 0c53a8339253..ae9a1e900c15 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -73,6 +73,9 @@ /* (max path length + 1 for null) * 2 for unicode */ #define MAX_NAME 514 +/* SMB echo "timeout" -- FIXME: tunable? */ +#define SMB_ECHO_INTERVAL (60 * HZ) + #include "cifspdu.h" #ifndef XATTR_DOS_ATTRIB diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index a83ed766aa94..5ab173fd6339 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -56,9 +56,6 @@ #define CIFS_PORT 445 #define RFC1001_PORT 139 -/* SMB echo "timeout" -- FIXME: tunable? */ -#define SMB_ECHO_INTERVAL (60 * HZ) - extern mempool_t *cifs_req_poolp; /* FIXME: should these be tunable? */ diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 8672e49d1c4c..483bd0ba2ecb 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -207,6 +207,12 @@ smb2_build_path_to_root(struct smb_vol *vol, struct cifs_sb_info *cifs_sb, return NULL; } +static bool +smb2_can_echo(struct TCP_Server_Info *server) +{ + return server->echoes; +} + struct smb_version_operations smb21_operations = { .setup_request = smb2_setup_request, .setup_async_request = smb2_setup_async_request, @@ -226,6 +232,8 @@ struct smb_version_operations smb21_operations = { .tree_connect = SMB2_tcon, .tree_disconnect = SMB2_tdis, .is_path_accessible = smb2_is_path_accessible, + .can_echo = smb2_can_echo, + .echo = SMB2_echo, .query_path_info = smb2_query_path_info, .get_srv_inum = smb2_get_srv_inum, .build_path_to_root = smb2_build_path_to_root, diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 7ef5324786a6..373b6945161f 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -1074,3 +1074,52 @@ qinf_exit: free_rsp_buf(resp_buftype, rsp); 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. + * + * FIXME: maybe we should consider checking that the reply matches request? + */ +static void +smb2_echo_callback(struct mid_q_entry *mid) +{ + struct TCP_Server_Info *server = mid->callback_data; + struct smb2_echo_rsp *smb2 = (struct smb2_echo_rsp *)mid->resp_buf; + unsigned int credits_received = 1; + + if (mid->mid_state == MID_RESPONSE_RECEIVED) + credits_received = le16_to_cpu(smb2->hdr.CreditRequest); + + DeleteMidQEntry(mid); + add_credits(server, credits_received, CIFS_ECHO_OP); +} + +int +SMB2_echo(struct TCP_Server_Info *server) +{ + struct smb2_echo_req *req; + int rc = 0; + struct kvec iov; + + cFYI(1, "In echo request"); + + rc = small_smb2_init(SMB2_ECHO, NULL, (void **)&req); + if (rc) + return rc; + + req->hdr.CreditRequest = cpu_to_le16(1); + + iov.iov_base = (char *)req; + /* 4 for rfc1002 length field */ + iov.iov_len = get_rfc1002_length(req) + 4; + + rc = cifs_call_async(server, &iov, 1, NULL, smb2_echo_callback, server, + CIFS_ECHO_OP); + if (rc) + cFYI(1, "Echo request failed: %d", rc); + + cifs_small_buf_release(req); + return rc; +} diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index 9151e9040b02..59aae608d366 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h @@ -448,6 +448,18 @@ struct smb2_close_rsp { __le32 Attributes; } __packed; +struct smb2_echo_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __u16 Reserved; +} __packed; + +struct smb2_echo_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __u16 Reserved; +} __packed; + /* Possible InfoType values */ #define SMB2_O_INFO_FILE 0x01 #define SMB2_O_INFO_FILESYSTEM 0x02 diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index 1a17955c35c9..902bbe2b5ad3 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h @@ -73,5 +73,6 @@ extern int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, extern int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_file_id, u64 volatile_file_id, struct smb2_file_all_info *data); +extern int SMB2_echo(struct TCP_Server_Info *server); #endif /* _SMB2PROTO_H */ -- cgit v1.2.3 From d60622eb5a23904facf4a4efac60f5bfa810d7d4 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Mon, 28 May 2012 15:19:39 +0400 Subject: CIFS: Allow SMB2 statistics to be tracked Since there are only 19 command codes, it also is easier to track by exact command code than it was for cifs. Signed-off-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 9 ++++++ fs/cifs/smb2ops.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/cifs/smb2pdu.c | 4 +-- 3 files changed, 91 insertions(+), 3 deletions(-) (limited to 'fs/cifs/smb2pdu.c') diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 0896328418aa..12b1176b87b0 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -28,6 +28,9 @@ #include "cifsacl.h" #include #include +#ifdef CONFIG_CIFS_SMB2 +#include "smb2pdu.h" +#endif /* * The sizes of various internal tables and strings @@ -592,6 +595,12 @@ struct cifs_tcon { atomic_t num_acl_get; atomic_t num_acl_set; } cifs_stats; +#ifdef CONFIG_CIFS_SMB2 + struct { + atomic_t smb2_com_sent[NUMBER_OF_SMB2_COMMANDS]; + atomic_t smb2_com_failed[NUMBER_OF_SMB2_COMMANDS]; + } smb2_stats; +#endif /* CONFIG_CIFS_SMB2 */ } stats; #ifdef CONFIG_CIFS_STATS2 unsigned long long time_writes; diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 483bd0ba2ecb..1018c5c6b5be 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -213,6 +213,85 @@ smb2_can_echo(struct TCP_Server_Info *server) return server->echoes; } +static void +smb2_clear_stats(struct cifs_tcon *tcon) +{ +#ifdef CONFIG_CIFS_STATS + int i; + for (i = 0; i < NUMBER_OF_SMB2_COMMANDS; i++) { + atomic_set(&tcon->stats.smb2_stats.smb2_com_sent[i], 0); + atomic_set(&tcon->stats.smb2_stats.smb2_com_failed[i], 0); + } +#endif +} + +static void +smb2_print_stats(struct seq_file *m, struct cifs_tcon *tcon) +{ +#ifdef CONFIG_CIFS_STATS + atomic_t *sent = tcon->stats.smb2_stats.smb2_com_sent; + atomic_t *failed = tcon->stats.smb2_stats.smb2_com_failed; + seq_printf(m, "\nNegotiates: %d sent %d failed", + atomic_read(&sent[SMB2_NEGOTIATE_HE]), + atomic_read(&failed[SMB2_NEGOTIATE_HE])); + seq_printf(m, "\nSessionSetups: %d sent %d failed", + atomic_read(&sent[SMB2_SESSION_SETUP_HE]), + atomic_read(&failed[SMB2_SESSION_SETUP_HE])); +#define SMB2LOGOFF 0x0002 /* trivial request/resp */ + seq_printf(m, "\nLogoffs: %d sent %d failed", + atomic_read(&sent[SMB2_LOGOFF_HE]), + atomic_read(&failed[SMB2_LOGOFF_HE])); + seq_printf(m, "\nTreeConnects: %d sent %d failed", + atomic_read(&sent[SMB2_TREE_CONNECT_HE]), + atomic_read(&failed[SMB2_TREE_CONNECT_HE])); + seq_printf(m, "\nTreeDisconnects: %d sent %d failed", + atomic_read(&sent[SMB2_TREE_DISCONNECT_HE]), + atomic_read(&failed[SMB2_TREE_DISCONNECT_HE])); + seq_printf(m, "\nCreates: %d sent %d failed", + atomic_read(&sent[SMB2_CREATE_HE]), + atomic_read(&failed[SMB2_CREATE_HE])); + seq_printf(m, "\nCloses: %d sent %d failed", + atomic_read(&sent[SMB2_CLOSE_HE]), + atomic_read(&failed[SMB2_CLOSE_HE])); + seq_printf(m, "\nFlushes: %d sent %d failed", + atomic_read(&sent[SMB2_FLUSH_HE]), + atomic_read(&failed[SMB2_FLUSH_HE])); + seq_printf(m, "\nReads: %d sent %d failed", + atomic_read(&sent[SMB2_READ_HE]), + atomic_read(&failed[SMB2_READ_HE])); + seq_printf(m, "\nWrites: %d sent %d failed", + atomic_read(&sent[SMB2_WRITE_HE]), + atomic_read(&failed[SMB2_WRITE_HE])); + seq_printf(m, "\nLocks: %d sent %d failed", + atomic_read(&sent[SMB2_LOCK_HE]), + atomic_read(&failed[SMB2_LOCK_HE])); + seq_printf(m, "\nIOCTLs: %d sent %d failed", + atomic_read(&sent[SMB2_IOCTL_HE]), + atomic_read(&failed[SMB2_IOCTL_HE])); + seq_printf(m, "\nCancels: %d sent %d failed", + atomic_read(&sent[SMB2_CANCEL_HE]), + atomic_read(&failed[SMB2_CANCEL_HE])); + seq_printf(m, "\nEchos: %d sent %d failed", + atomic_read(&sent[SMB2_ECHO_HE]), + atomic_read(&failed[SMB2_ECHO_HE])); + seq_printf(m, "\nQueryDirectories: %d sent %d failed", + atomic_read(&sent[SMB2_QUERY_DIRECTORY_HE]), + atomic_read(&failed[SMB2_QUERY_DIRECTORY_HE])); + seq_printf(m, "\nChangeNotifies: %d sent %d failed", + atomic_read(&sent[SMB2_CHANGE_NOTIFY_HE]), + atomic_read(&failed[SMB2_CHANGE_NOTIFY_HE])); + seq_printf(m, "\nQueryInfos: %d sent %d failed", + atomic_read(&sent[SMB2_QUERY_INFO_HE]), + atomic_read(&failed[SMB2_QUERY_INFO_HE])); + seq_printf(m, "\nSetInfos: %d sent %d failed", + atomic_read(&sent[SMB2_SET_INFO_HE]), + atomic_read(&failed[SMB2_SET_INFO_HE])); + seq_printf(m, "\nOplockBreaks: %d sent %d failed", + atomic_read(&sent[SMB2_OPLOCK_BREAK_HE]), + atomic_read(&failed[SMB2_OPLOCK_BREAK_HE])); +#endif +} + struct smb_version_operations smb21_operations = { .setup_request = smb2_setup_request, .setup_async_request = smb2_setup_async_request, @@ -225,6 +304,8 @@ struct smb_version_operations smb21_operations = { .find_mid = smb2_find_mid, .check_message = smb2_check_message, .dump_detail = smb2_dump_detail, + .clear_stats = smb2_clear_stats, + .print_stats = smb2_print_stats, .need_neg = smb2_need_neg, .negotiate = smb2_negotiate, .sess_setup = SMB2_sess_setup, diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 373b6945161f..e4eb1d3fb7d9 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -282,10 +282,8 @@ small_smb2_init(__le16 smb2_command, struct cifs_tcon *tcon, if (tcon != NULL) { #ifdef CONFIG_CIFS_STATS2 - /* uint16_t com_code = le16_to_cpu(smb2_command); cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_sent[com_code]); - */ #endif cifs_stats_inc(&tcon->num_smbs_sent); } @@ -677,7 +675,7 @@ SMB2_logoff(const unsigned int xid, struct cifs_ses *ses) static inline void cifs_stats_fail_inc(struct cifs_tcon *tcon, uint16_t code) { - /* cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[code]); */ + cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_failed[code]); } #define MAX_SHARENAME_LENGTH (255 /* server */ + 80 /* share */ + 1 /* NULL */) -- cgit v1.2.3 From 29e20f9c65fae245d6fd4fce31cc5d01cde3d93f Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Fri, 13 Jul 2012 13:58:14 +0400 Subject: CIFS: Make CAP_* checks protocol independent Since both CIFS and SMB2 use ses->capabilities (server->capabilities) field but flags are different we should make such checks protocol independent. Reviewed-by: Jeff Layton Signed-off-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 14 ++++++++++++-- fs/cifs/connect.c | 6 +++--- fs/cifs/dir.c | 3 +-- fs/cifs/file.c | 33 ++++++++++++++++----------------- fs/cifs/inode.c | 26 ++++++++++++-------------- fs/cifs/link.c | 6 +++--- fs/cifs/readdir.c | 16 ++++++++-------- fs/cifs/smb1ops.c | 3 +++ fs/cifs/smb2ops.c | 3 +++ fs/cifs/smb2pdu.c | 2 ++ fs/cifs/smb2pdu.h | 3 +++ 11 files changed, 66 insertions(+), 49 deletions(-) (limited to 'fs/cifs/smb2pdu.c') diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 12b1176b87b0..bcdf4d4420f1 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -258,6 +258,9 @@ struct smb_version_values { size_t max_header_size; size_t read_rsp_size; __le16 lock_cmd; + unsigned int cap_unix; + unsigned int cap_nt_find; + unsigned int cap_large_files; }; #define HEADER_SIZE(server) (server->vals->header_size) @@ -408,7 +411,7 @@ struct TCP_Server_Info { unsigned int max_vcs; /* maximum number of smb sessions, at least those that can be specified uniquely with vcnumbers */ - int capabilities; /* allow selective disabling of caps by smb sess */ + unsigned int capabilities; /* selective disabling of caps by smb sess */ int timeAdj; /* Adjust for difference in server time zone in sec */ __u64 CurrentMid; /* multiplex id - rotating counter */ char cryptkey[CIFS_CRYPTO_KEY_SIZE]; /* used by ntlm, ntlmv2 etc */ @@ -532,7 +535,7 @@ struct cifs_ses { __u64 Suid; /* remote smb uid */ uid_t linux_uid; /* overriding owner of files on the mount */ uid_t cred_uid; /* owner of credentials */ - int capabilities; + unsigned int capabilities; char serverName[SERVER_NAME_LEN_WITH_NULL * 2]; /* BB make bigger for TCP names - will ipv6 and sctp addresses fit? */ char *user_name; /* must not be null except during init of sess @@ -554,6 +557,13 @@ struct cifs_ses { which do not negotiate NTLM or POSIX dialects, but instead negotiate one of the older LANMAN dialects */ #define CIFS_SES_LANMAN 8 + +static inline bool +cap_unix(struct cifs_ses *ses) +{ + return ses->server->vals->cap_unix & ses->capabilities; +} + /* * there is one of these for each connection to a resource on a particular * session diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 5ab173fd6339..6df6fa14cba8 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -3634,7 +3634,7 @@ try_mount_again: } /* tell server which Unix caps we support */ - if (tcon->ses->capabilities & CAP_UNIX) { + if (cap_unix(tcon->ses)) { /* reset of caps checks mount to see if unix extensions disabled for just this mount */ reset_cifs_unix_caps(xid, tcon, cifs_sb, volume_info); @@ -3993,7 +3993,7 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, ses->flags = 0; ses->capabilities = server->capabilities; if (linuxExtEnabled == 0) - ses->capabilities &= (~CAP_UNIX); + ses->capabilities &= (~server->vals->cap_unix); cFYI(1, "Security Mode: 0x%x Capabilities: 0x%x TimeAdjust: %d", server->sec_mode, server->capabilities, server->timeAdj); @@ -4100,7 +4100,7 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, uid_t fsuid) goto out; } - if (ses->capabilities & CAP_UNIX) + if (cap_unix(ses)) reset_cifs_unix_caps(0, tcon, NULL, vol_info); out: kfree(vol_info->username); diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 2caba0b54acb..cbe709ad6663 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -182,8 +182,7 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid, goto out; } - if (tcon->unix_ext && (tcon->ses->capabilities & CAP_UNIX) && - !tcon->broken_posix_open && + if (tcon->unix_ext && cap_unix(tcon->ses) && !tcon->broken_posix_open && (CIFS_UNIX_POSIX_PATH_OPS_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability))) { rc = cifs_posix_open(full_path, &newinode, diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 93b3b1358409..07e9d41cade7 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -385,9 +385,8 @@ int cifs_open(struct inode *inode, struct file *file) oplock = 0; if (!tcon->broken_posix_open && tcon->unix_ext && - (tcon->ses->capabilities & CAP_UNIX) && - (CIFS_UNIX_POSIX_PATH_OPS_CAP & - le64_to_cpu(tcon->fsUnixInfo.Capability))) { + cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & + le64_to_cpu(tcon->fsUnixInfo.Capability))) { /* can not refresh inode info since size could be stale */ rc = cifs_posix_open(full_path, &inode, inode->i_sb, cifs_sb->mnt_file_mode /* ignored */, @@ -509,10 +508,9 @@ static int cifs_reopen_file(struct cifsFileInfo *pCifsFile, bool can_flush) else oplock = 0; - if (tcon->unix_ext && (tcon->ses->capabilities & CAP_UNIX) && + if (tcon->unix_ext && cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & - le64_to_cpu(tcon->fsUnixInfo.Capability))) { - + le64_to_cpu(tcon->fsUnixInfo.Capability))) { /* * O_CREAT, O_EXCL and O_TRUNC already had their effect on the * original open. Must mask them off for a reopen. @@ -1071,7 +1069,7 @@ cifs_push_locks(struct cifsFileInfo *cfile) struct cifs_sb_info *cifs_sb = CIFS_SB(cfile->dentry->d_sb); struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); - if ((tcon->ses->capabilities & CAP_UNIX) && + if (cap_unix(tcon->ses) && (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) return cifs_push_posix_locks(cfile); @@ -1419,7 +1417,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *flock) netfid = cfile->netfid; cinode = CIFS_I(file->f_path.dentry->d_inode); - if ((tcon->ses->capabilities & CAP_UNIX) && + if (cap_unix(tcon->ses) && (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) posix_lck = true; @@ -2745,7 +2743,7 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, unsigned int current_read_size; unsigned int rsize; struct cifs_sb_info *cifs_sb; - struct cifs_tcon *pTcon; + struct cifs_tcon *tcon; unsigned int xid; char *current_offset; struct cifsFileInfo *open_file; @@ -2765,7 +2763,7 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, return rc; } open_file = file->private_data; - pTcon = tlink_tcon(open_file->tlink); + tcon = tlink_tcon(open_file->tlink); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) pid = open_file->pid; @@ -2779,11 +2777,12 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, read_size > total_read; total_read += bytes_read, current_offset += bytes_read) { current_read_size = min_t(uint, read_size - total_read, rsize); - - /* For windows me and 9x we do not want to request more - than it negotiated since it will refuse the read then */ - if ((pTcon->ses) && - !(pTcon->ses->capabilities & CAP_LARGE_FILES)) { + /* + * For windows me and 9x we do not want to request more than it + * negotiated since it will refuse the read then. + */ + if ((tcon->ses) && !(tcon->ses->capabilities & + tcon->ses->server->vals->cap_large_files)) { current_read_size = min_t(uint, current_read_size, CIFSMaxBufSize); } @@ -2796,7 +2795,7 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, } io_parms.netfid = open_file->netfid; io_parms.pid = pid; - io_parms.tcon = pTcon; + io_parms.tcon = tcon; io_parms.offset = *poffset; io_parms.length = current_read_size; rc = CIFSSMBRead(xid, &io_parms, &bytes_read, @@ -2810,7 +2809,7 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, return rc; } } else { - cifs_stats_bytes_read(pTcon, total_read); + cifs_stats_bytes_read(tcon, total_read); *poffset += bytes_read; } } diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index def10064fe9d..35cb6a374a45 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -1149,9 +1149,8 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry) goto unlink_out; } - if ((tcon->ses->capabilities & CAP_UNIX) && - (CIFS_UNIX_POSIX_PATH_OPS_CAP & - le64_to_cpu(tcon->fsUnixInfo.Capability))) { + if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & + le64_to_cpu(tcon->fsUnixInfo.Capability))) { rc = CIFSPOSIXDelFile(xid, tcon, full_path, SMB_POSIX_UNLINK_FILE_TARGET, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); @@ -1226,7 +1225,7 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, umode_t mode) unsigned int xid; struct cifs_sb_info *cifs_sb; struct tcon_link *tlink; - struct cifs_tcon *pTcon; + struct cifs_tcon *tcon; char *full_path = NULL; struct inode *newinode = NULL; struct cifs_fattr fattr; @@ -1237,7 +1236,7 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, umode_t mode) tlink = cifs_sb_tlink(cifs_sb); if (IS_ERR(tlink)) return PTR_ERR(tlink); - pTcon = tlink_tcon(tlink); + tcon = tlink_tcon(tlink); xid = get_xid(); @@ -1247,9 +1246,8 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, umode_t mode) goto mkdir_out; } - if ((pTcon->ses->capabilities & CAP_UNIX) && - (CIFS_UNIX_POSIX_PATH_OPS_CAP & - le64_to_cpu(pTcon->fsUnixInfo.Capability))) { + if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & + le64_to_cpu(tcon->fsUnixInfo.Capability))) { u32 oplock = 0; FILE_UNIX_BASIC_INFO *pInfo = kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL); @@ -1259,7 +1257,7 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, umode_t mode) } mode &= ~current_umask(); - rc = CIFSPOSIXCreate(xid, pTcon, SMB_O_DIRECTORY | SMB_O_CREAT, + rc = CIFSPOSIXCreate(xid, tcon, SMB_O_DIRECTORY | SMB_O_CREAT, mode, NULL /* netfid */, pInfo, &oplock, full_path, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & @@ -1303,14 +1301,14 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, umode_t mode) } mkdir_retry_old: /* BB add setting the equivalent of mode via CreateX w/ACLs */ - rc = CIFSSMBMkDir(xid, pTcon, full_path, cifs_sb->local_nls, + rc = CIFSSMBMkDir(xid, tcon, full_path, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if (rc) { cFYI(1, "cifs_mkdir returned 0x%x", rc); d_drop(direntry); } else { mkdir_get_info: - if (pTcon->unix_ext) + if (tcon->unix_ext) rc = cifs_get_inode_info_unix(&newinode, full_path, inode->i_sb, xid); else @@ -1328,7 +1326,7 @@ mkdir_get_info: if (inode->i_mode & S_ISGID) mode |= S_ISGID; - if (pTcon->unix_ext) { + if (tcon->unix_ext) { struct cifs_unix_set_info_args args = { .mode = mode, .ctime = NO_CHANGE_64, @@ -1346,7 +1344,7 @@ mkdir_get_info: args.uid = NO_CHANGE_64; args.gid = NO_CHANGE_64; } - CIFSSMBUnixSetPathInfo(xid, pTcon, full_path, &args, + CIFSSMBUnixSetPathInfo(xid, tcon, full_path, &args, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); @@ -1361,7 +1359,7 @@ mkdir_get_info: cifsInode = CIFS_I(newinode); dosattrs = cifsInode->cifsAttrs|ATTR_READONLY; pInfo.Attributes = cpu_to_le32(dosattrs); - tmprc = CIFSSMBSetPathInfo(xid, pTcon, + tmprc = CIFSSMBSetPathInfo(xid, tcon, full_path, &pInfo, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & diff --git a/fs/cifs/link.c b/fs/cifs/link.c index f78971511f57..09e4b3ae4564 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -495,8 +495,8 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) * but there doesn't seem to be any harm in allowing the client to * read them. */ - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) - && !(tcon->ses->capabilities & CAP_UNIX)) { + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) && + !cap_unix(tcon->ses)) { rc = -EACCES; goto out; } @@ -518,7 +518,7 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); - if ((rc != 0) && (tcon->ses->capabilities & CAP_UNIX)) + if ((rc != 0) && cap_unix(tcon->ses)) rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, &target_path, cifs_sb->local_nls); diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index da30d96a7495..d87f82678bc7 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -228,7 +228,7 @@ static int initiate_cifs_search(const unsigned int xid, struct file *file) struct cifsFileInfo *cifsFile; struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); struct tcon_link *tlink = NULL; - struct cifs_tcon *pTcon; + struct cifs_tcon *tcon; if (file->private_data == NULL) { tlink = cifs_sb_tlink(cifs_sb); @@ -242,10 +242,10 @@ static int initiate_cifs_search(const unsigned int xid, struct file *file) } file->private_data = cifsFile; cifsFile->tlink = cifs_get_tlink(tlink); - pTcon = tlink_tcon(tlink); + tcon = tlink_tcon(tlink); } else { cifsFile = file->private_data; - pTcon = tlink_tcon(cifsFile->tlink); + tcon = tlink_tcon(cifsFile->tlink); } cifsFile->invalidHandle = true; @@ -262,11 +262,11 @@ static int initiate_cifs_search(const unsigned int xid, struct file *file) ffirst_retry: /* test for Unix extensions */ /* but now check for them on the share/mount not on the SMB session */ -/* if (pTcon->ses->capabilities & CAP_UNIX) { */ - if (pTcon->unix_ext) + /* if (cap_unix(tcon->ses) { */ + if (tcon->unix_ext) cifsFile->srch_inf.info_level = SMB_FIND_FILE_UNIX; - else if ((pTcon->ses->capabilities & - (CAP_NT_SMBS | CAP_NT_FIND)) == 0) { + else if ((tcon->ses->capabilities & + tcon->ses->server->vals->cap_nt_find) == 0) { cifsFile->srch_inf.info_level = SMB_FIND_FILE_INFO_STANDARD; } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { cifsFile->srch_inf.info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO; @@ -278,7 +278,7 @@ ffirst_retry: if (backup_cred(cifs_sb)) search_flags |= CIFS_SEARCH_BACKUP_SEARCH; - rc = CIFSFindFirst(xid, pTcon, full_path, cifs_sb->local_nls, + rc = CIFSFindFirst(xid, tcon, full_path, cifs_sb->local_nls, &cifsFile->netfid, search_flags, &cifsFile->srch_inf, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR, CIFS_DIR_SEP(cifs_sb)); diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c index 581740998735..c40356d24c5c 100644 --- a/fs/cifs/smb1ops.c +++ b/fs/cifs/smb1ops.c @@ -632,4 +632,7 @@ struct smb_version_values smb1_values = { .max_header_size = MAX_CIFS_HDR_SIZE, .read_rsp_size = sizeof(READ_RSP), .lock_cmd = cpu_to_le16(SMB_COM_LOCKING_ANDX), + .cap_unix = CAP_UNIX, + .cap_nt_find = CAP_NT_SMBS | CAP_NT_FIND, + .cap_large_files = CAP_LARGE_FILES, }; diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 1018c5c6b5be..410cf925ea26 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -325,4 +325,7 @@ struct smb_version_values smb21_values = { .header_size = sizeof(struct smb2_hdr), .max_header_size = MAX_SMB2_HDR_SIZE, .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, }; diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index e4eb1d3fb7d9..62b3f17d0613 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -428,6 +428,8 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) /* BB Do we need to validate the SecurityMode? */ server->sec_mode = le16_to_cpu(rsp->SecurityMode); server->capabilities = le32_to_cpu(rsp->Capabilities); + /* Internal types */ + server->capabilities |= SMB2_NT_FIND | SMB2_LARGE_FILES; security_blob = smb2_get_data_area_len(&blob_offset, &blob_length, &rsp->hdr); diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index 59aae608d366..f37a1b41b402 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h @@ -167,6 +167,9 @@ struct smb2_negotiate_req { #define SMB2_GLOBAL_CAP_DFS 0x00000001 #define SMB2_GLOBAL_CAP_LEASING 0x00000002 /* Resp only New to SMB2.1 */ #define SMB2_GLOBAL_CAP_LARGE_MTU 0X00000004 /* Resp only New to SMB2.1 */ +/* Internal types */ +#define SMB2_NT_FIND 0x00100000 +#define SMB2_LARGE_FILES 0x00200000 struct smb2_negotiate_rsp { struct smb2_hdr hdr; -- cgit v1.2.3