diff options
author | Pavel Shilovsky <piastry@etersoft.ru> | 2011-12-27 07:04:00 -0500 |
---|---|---|
committer | Pavel Shilovsky <pshilovsky@samba.org> | 2012-07-24 13:54:58 -0400 |
commit | faaf946a7d5b79194358437150f34ab4c66bfe21 (patch) | |
tree | 1bf4b4cab6abc39443a63e90cac771f9aaad0f52 /fs/cifs | |
parent | 5478f9ba9a34d660eb3227dcd16314689c51f946 (diff) |
CIFS: Add tree connect/disconnect capability for SMB2
Signed-off-by: Pavel Shilovsky <piastry@etersoft.ru>
Signed-off-by: Steve French <smfrench@gmail.com>
Diffstat (limited to 'fs/cifs')
-rw-r--r-- | fs/cifs/cifs_unicode.c | 1 | ||||
-rw-r--r-- | fs/cifs/cifs_unicode.h | 1 | ||||
-rw-r--r-- | fs/cifs/cifsglob.h | 11 | ||||
-rw-r--r-- | fs/cifs/smb2ops.c | 2 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.c | 159 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.h | 57 | ||||
-rw-r--r-- | fs/cifs/smb2proto.h | 4 |
7 files changed, 230 insertions, 5 deletions
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, | |||
330 | ctoUTF16_out: | 330 | ctoUTF16_out: |
331 | return i; | 331 | return i; |
332 | } | 332 | } |
333 | |||
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, | |||
84 | const struct nls_table *codepage); | 84 | const struct nls_table *codepage); |
85 | extern int cifsConvertToUTF16(__le16 *target, const char *source, int maxlen, | 85 | extern int cifsConvertToUTF16(__le16 *target, const char *source, int maxlen, |
86 | const struct nls_table *cp, int mapChars); | 86 | const struct nls_table *cp, int mapChars); |
87 | |||
88 | #endif | 87 | #endif |
89 | 88 | ||
90 | /* | 89 | /* |
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 { | |||
528 | char treeName[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */ | 528 | char treeName[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */ |
529 | char *nativeFileSystem; | 529 | char *nativeFileSystem; |
530 | char *password; /* for share-level security */ | 530 | char *password; /* for share-level security */ |
531 | __u16 tid; /* The 2 byte tree id */ | 531 | __u32 tid; /* The 4 byte tree id */ |
532 | __u16 Flags; /* optional support bits */ | 532 | __u16 Flags; /* optional support bits */ |
533 | enum statusEnum tidStatus; | 533 | enum statusEnum tidStatus; |
534 | #ifdef CONFIG_CIFS_STATS | 534 | #ifdef CONFIG_CIFS_STATS |
@@ -584,6 +584,15 @@ struct cifs_tcon { | |||
584 | bool local_lease:1; /* check leases (only) on local system not remote */ | 584 | bool local_lease:1; /* check leases (only) on local system not remote */ |
585 | bool broken_posix_open; /* e.g. Samba server versions < 3.3.2, 3.2.9 */ | 585 | bool broken_posix_open; /* e.g. Samba server versions < 3.3.2, 3.2.9 */ |
586 | bool need_reconnect:1; /* connection reset, tid now invalid */ | 586 | bool need_reconnect:1; /* connection reset, tid now invalid */ |
587 | #ifdef CONFIG_CIFS_SMB2 | ||
588 | bool print:1; /* set if connection to printer share */ | ||
589 | bool bad_network_name:1; /* set if ret status STATUS_BAD_NETWORK_NAME */ | ||
590 | __u32 capabilities; | ||
591 | __u32 share_flags; | ||
592 | __u32 maximal_access; | ||
593 | __u32 vol_serial_number; | ||
594 | __le64 vol_create_time; | ||
595 | #endif /* CONFIG_CIFS_SMB2 */ | ||
587 | #ifdef CONFIG_CIFS_FSCACHE | 596 | #ifdef CONFIG_CIFS_FSCACHE |
588 | u64 resource_id; /* server resource id */ | 597 | u64 resource_id; /* server resource id */ |
589 | struct fscache_cookie *fscache; /* cookie for share */ | 598 | 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 = { | |||
172 | .negotiate = smb2_negotiate, | 172 | .negotiate = smb2_negotiate, |
173 | .sess_setup = SMB2_sess_setup, | 173 | .sess_setup = SMB2_sess_setup, |
174 | .logoff = SMB2_logoff, | 174 | .logoff = SMB2_logoff, |
175 | .tree_connect = SMB2_tcon, | ||
176 | .tree_disconnect = SMB2_tdis, | ||
175 | }; | 177 | }; |
176 | 178 | ||
177 | struct smb_version_values smb21_values = { | 179 | 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 */ , | |||
110 | hdr->SessionId = tcon->ses->Suid; | 110 | hdr->SessionId = tcon->ses->Suid; |
111 | /* BB check following DFS flags BB */ | 111 | /* BB check following DFS flags BB */ |
112 | /* BB do we have to add check for SHI1005_FLAGS_DFS_ROOT too? */ | 112 | /* BB do we have to add check for SHI1005_FLAGS_DFS_ROOT too? */ |
113 | /* if (tcon->share_flags & SHI1005_FLAGS_DFS) | 113 | if (tcon->share_flags & SHI1005_FLAGS_DFS) |
114 | hdr->Flags |= SMB2_FLAGS_DFS_OPERATIONS; */ | 114 | hdr->Flags |= SMB2_FLAGS_DFS_OPERATIONS; |
115 | /* BB how does SMB2 do case sensitive? */ | 115 | /* BB how does SMB2 do case sensitive? */ |
116 | /* if (tcon->nocase) | 116 | /* if (tcon->nocase) |
117 | hdr->Flags |= SMBFLG_CASELESS; */ | 117 | hdr->Flags |= SMBFLG_CASELESS; */ |
@@ -549,3 +549,158 @@ SMB2_logoff(const unsigned int xid, struct cifs_ses *ses) | |||
549 | */ | 549 | */ |
550 | return rc; | 550 | return rc; |
551 | } | 551 | } |
552 | |||
553 | static inline void cifs_stats_fail_inc(struct cifs_tcon *tcon, uint16_t code) | ||
554 | { | ||
555 | /* cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[code]); */ | ||
556 | } | ||
557 | |||
558 | #define MAX_SHARENAME_LENGTH (255 /* server */ + 80 /* share */ + 1 /* NULL */) | ||
559 | |||
560 | int | ||
561 | SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, | ||
562 | struct cifs_tcon *tcon, const struct nls_table *cp) | ||
563 | { | ||
564 | struct smb2_tree_connect_req *req; | ||
565 | struct smb2_tree_connect_rsp *rsp = NULL; | ||
566 | struct kvec iov[2]; | ||
567 | int rc = 0; | ||
568 | int resp_buftype; | ||
569 | int unc_path_len; | ||
570 | struct TCP_Server_Info *server; | ||
571 | __le16 *unc_path = NULL; | ||
572 | |||
573 | cFYI(1, "TCON"); | ||
574 | |||
575 | if ((ses->server) && tree) | ||
576 | server = ses->server; | ||
577 | else | ||
578 | return -EIO; | ||
579 | |||
580 | if (tcon && tcon->bad_network_name) | ||
581 | return -ENOENT; | ||
582 | |||
583 | unc_path = kmalloc(MAX_SHARENAME_LENGTH * 2, GFP_KERNEL); | ||
584 | if (unc_path == NULL) | ||
585 | return -ENOMEM; | ||
586 | |||
587 | unc_path_len = cifs_strtoUTF16(unc_path, tree, strlen(tree), cp) + 1; | ||
588 | unc_path_len *= 2; | ||
589 | if (unc_path_len < 2) { | ||
590 | kfree(unc_path); | ||
591 | return -EINVAL; | ||
592 | } | ||
593 | |||
594 | rc = small_smb2_init(SMB2_TREE_CONNECT, tcon, (void **) &req); | ||
595 | if (rc) { | ||
596 | kfree(unc_path); | ||
597 | return rc; | ||
598 | } | ||
599 | |||
600 | if (tcon == NULL) { | ||
601 | /* since no tcon, smb2_init can not do this, so do here */ | ||
602 | req->hdr.SessionId = ses->Suid; | ||
603 | /* if (ses->server->sec_mode & SECMODE_SIGN_REQUIRED) | ||
604 | req->hdr.Flags |= SMB2_FLAGS_SIGNED; */ | ||
605 | } | ||
606 | |||
607 | iov[0].iov_base = (char *)req; | ||
608 | /* 4 for rfc1002 length field and 1 for pad */ | ||
609 | iov[0].iov_len = get_rfc1002_length(req) + 4 - 1; | ||
610 | |||
611 | /* Testing shows that buffer offset must be at location of Buffer[0] */ | ||
612 | req->PathOffset = cpu_to_le16(sizeof(struct smb2_tree_connect_req) | ||
613 | - 1 /* pad */ - 4 /* do not count rfc1001 len field */); | ||
614 | req->PathLength = cpu_to_le16(unc_path_len - 2); | ||
615 | iov[1].iov_base = unc_path; | ||
616 | iov[1].iov_len = unc_path_len; | ||
617 | |||
618 | inc_rfc1001_len(req, unc_path_len - 1 /* pad */); | ||
619 | |||
620 | rc = SendReceive2(xid, ses, iov, 2, &resp_buftype, 0); | ||
621 | rsp = (struct smb2_tree_connect_rsp *)iov[0].iov_base; | ||
622 | |||
623 | if (rc != 0) { | ||
624 | if (tcon) { | ||
625 | cifs_stats_fail_inc(tcon, SMB2_TREE_CONNECT_HE); | ||
626 | tcon->need_reconnect = true; | ||
627 | } | ||
628 | goto tcon_error_exit; | ||
629 | } | ||
630 | |||
631 | if (rsp == NULL) { | ||
632 | rc = -EIO; | ||
633 | goto tcon_exit; | ||
634 | } | ||
635 | |||
636 | if (tcon == NULL) { | ||
637 | ses->ipc_tid = rsp->hdr.TreeId; | ||
638 | goto tcon_exit; | ||
639 | } | ||
640 | |||
641 | if (rsp->ShareType & SMB2_SHARE_TYPE_DISK) | ||
642 | cFYI(1, "connection to disk share"); | ||
643 | else if (rsp->ShareType & SMB2_SHARE_TYPE_PIPE) { | ||
644 | tcon->ipc = true; | ||
645 | cFYI(1, "connection to pipe share"); | ||
646 | } else if (rsp->ShareType & SMB2_SHARE_TYPE_PRINT) { | ||
647 | tcon->print = true; | ||
648 | cFYI(1, "connection to printer"); | ||
649 | } else { | ||
650 | cERROR(1, "unknown share type %d", rsp->ShareType); | ||
651 | rc = -EOPNOTSUPP; | ||
652 | goto tcon_error_exit; | ||
653 | } | ||
654 | |||
655 | tcon->share_flags = le32_to_cpu(rsp->ShareFlags); | ||
656 | tcon->maximal_access = le32_to_cpu(rsp->MaximalAccess); | ||
657 | tcon->tidStatus = CifsGood; | ||
658 | tcon->need_reconnect = false; | ||
659 | tcon->tid = rsp->hdr.TreeId; | ||
660 | strncpy(tcon->treeName, tree, MAX_TREE_SIZE); | ||
661 | |||
662 | if ((rsp->Capabilities & SMB2_SHARE_CAP_DFS) && | ||
663 | ((tcon->share_flags & SHI1005_FLAGS_DFS) == 0)) | ||
664 | cERROR(1, "DFS capability contradicts DFS flag"); | ||
665 | |||
666 | tcon_exit: | ||
667 | free_rsp_buf(resp_buftype, rsp); | ||
668 | kfree(unc_path); | ||
669 | return rc; | ||
670 | |||
671 | tcon_error_exit: | ||
672 | if (rsp->hdr.Status == STATUS_BAD_NETWORK_NAME) { | ||
673 | cERROR(1, "BAD_NETWORK_NAME: %s", tree); | ||
674 | tcon->bad_network_name = true; | ||
675 | } | ||
676 | goto tcon_exit; | ||
677 | } | ||
678 | |||
679 | int | ||
680 | SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon) | ||
681 | { | ||
682 | struct smb2_tree_disconnect_req *req; /* response is trivial */ | ||
683 | int rc = 0; | ||
684 | struct TCP_Server_Info *server; | ||
685 | struct cifs_ses *ses = tcon->ses; | ||
686 | |||
687 | cFYI(1, "Tree Disconnect"); | ||
688 | |||
689 | if (ses && (ses->server)) | ||
690 | server = ses->server; | ||
691 | else | ||
692 | return -EIO; | ||
693 | |||
694 | if ((tcon->need_reconnect) || (tcon->ses->need_reconnect)) | ||
695 | return 0; | ||
696 | |||
697 | rc = small_smb2_init(SMB2_TREE_DISCONNECT, tcon, (void **) &req); | ||
698 | if (rc) | ||
699 | return rc; | ||
700 | |||
701 | rc = SendReceiveNoRsp(xid, ses, (char *)&req->hdr, 0); | ||
702 | if (rc) | ||
703 | cifs_stats_fail_inc(tcon, SMB2_TREE_DISCONNECT_HE); | ||
704 | |||
705 | return rc; | ||
706 | } | ||
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 { | |||
224 | __le16 Reserved; | 224 | __le16 Reserved; |
225 | } __packed; | 225 | } __packed; |
226 | 226 | ||
227 | struct smb2_tree_connect_req { | ||
228 | struct smb2_hdr hdr; | ||
229 | __le16 StructureSize; /* Must be 9 */ | ||
230 | __le16 Reserved; | ||
231 | __le16 PathOffset; | ||
232 | __le16 PathLength; | ||
233 | __u8 Buffer[1]; /* variable length */ | ||
234 | } __packed; | ||
235 | |||
236 | struct smb2_tree_connect_rsp { | ||
237 | struct smb2_hdr hdr; | ||
238 | __le16 StructureSize; /* Must be 16 */ | ||
239 | __u8 ShareType; /* see below */ | ||
240 | __u8 Reserved; | ||
241 | __le32 ShareFlags; /* see below */ | ||
242 | __le32 Capabilities; /* see below */ | ||
243 | __le32 MaximalAccess; | ||
244 | } __packed; | ||
245 | |||
246 | /* Possible ShareType values */ | ||
247 | #define SMB2_SHARE_TYPE_DISK 0x01 | ||
248 | #define SMB2_SHARE_TYPE_PIPE 0x02 | ||
249 | #define SMB2_SHARE_TYPE_PRINT 0x03 | ||
250 | |||
251 | /* | ||
252 | * Possible ShareFlags - exactly one and only one of the first 4 caching flags | ||
253 | * must be set (any of the remaining, SHI1005, flags may be set individually | ||
254 | * or in combination. | ||
255 | */ | ||
256 | #define SMB2_SHAREFLAG_MANUAL_CACHING 0x00000000 | ||
257 | #define SMB2_SHAREFLAG_AUTO_CACHING 0x00000010 | ||
258 | #define SMB2_SHAREFLAG_VDO_CACHING 0x00000020 | ||
259 | #define SMB2_SHAREFLAG_NO_CACHING 0x00000030 | ||
260 | #define SHI1005_FLAGS_DFS 0x00000001 | ||
261 | #define SHI1005_FLAGS_DFS_ROOT 0x00000002 | ||
262 | #define SHI1005_FLAGS_RESTRICT_EXCLUSIVE_OPENS 0x00000100 | ||
263 | #define SHI1005_FLAGS_FORCE_SHARED_DELETE 0x00000200 | ||
264 | #define SHI1005_FLAGS_ALLOW_NAMESPACE_CACHING 0x00000400 | ||
265 | #define SHI1005_FLAGS_ACCESS_BASED_DIRECTORY_ENUM 0x00000800 | ||
266 | #define SHI1005_FLAGS_FORCE_LEVELII_OPLOCK 0x00001000 | ||
267 | #define SHI1005_FLAGS_ENABLE_HASH 0x00002000 | ||
268 | |||
269 | /* Possible share capabilities */ | ||
270 | #define SMB2_SHARE_CAP_DFS cpu_to_le32(0x00000008) | ||
271 | |||
272 | struct smb2_tree_disconnect_req { | ||
273 | struct smb2_hdr hdr; | ||
274 | __le16 StructureSize; /* Must be 4 */ | ||
275 | __le16 Reserved; | ||
276 | } __packed; | ||
277 | |||
278 | struct smb2_tree_disconnect_rsp { | ||
279 | struct smb2_hdr hdr; | ||
280 | __le16 StructureSize; /* Must be 4 */ | ||
281 | __le16 Reserved; | ||
282 | } __packed; | ||
283 | |||
227 | #endif /* _SMB2PDU_H */ | 284 | #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); | |||
50 | extern int SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, | 50 | extern int SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, |
51 | const struct nls_table *nls_cp); | 51 | const struct nls_table *nls_cp); |
52 | extern int SMB2_logoff(const unsigned int xid, struct cifs_ses *ses); | 52 | extern int SMB2_logoff(const unsigned int xid, struct cifs_ses *ses); |
53 | extern int SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, | ||
54 | const char *tree, struct cifs_tcon *tcon, | ||
55 | const struct nls_table *); | ||
56 | extern int SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon); | ||
53 | 57 | ||
54 | #endif /* _SMB2PROTO_H */ | 58 | #endif /* _SMB2PROTO_H */ |