mirror of
https://github.com/torvalds/linux
synced 2024-11-05 18:23:50 +00:00
CIFS: Add tree connect/disconnect capability for SMB2
Signed-off-by: Pavel Shilovsky <piastry@etersoft.ru> Signed-off-by: Steve French <smfrench@gmail.com>
This commit is contained in:
parent
5478f9ba9a
commit
faaf946a7d
7 changed files with 230 additions and 5 deletions
|
@ -330,4 +330,3 @@ cifsConvertToUTF16(__le16 *target, const char *source, int srclen,
|
|||
ctoUTF16_out:
|
||||
return i;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
/*
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in a new issue