CIFS: Make transport routines work with SMB2
Reviewed-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: Pavel Shilovsky <piastry@etersoft.ru> Signed-off-by: Steve French <smfrench@gmail.com>
This commit is contained in:
parent
ddfbefbd39
commit
2dc7e1c033
@ -16,4 +16,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
|
||||
cifs-$(CONFIG_CIFS_SMB2) += smb2ops.o smb2maperror.o smb2transport.o
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <linux/in.h>
|
||||
#include <linux/in6.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mempool.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include "cifs_fs_sb.h"
|
||||
#include "cifsacl.h"
|
||||
@ -218,6 +219,7 @@ struct smb_version_values {
|
||||
size_t header_size;
|
||||
size_t max_header_size;
|
||||
size_t read_rsp_size;
|
||||
__le16 lock_cmd;
|
||||
};
|
||||
|
||||
#define HEADER_SIZE(server) (server->vals->header_size)
|
||||
@ -812,6 +814,7 @@ typedef void (mid_callback_t)(struct mid_q_entry *mid);
|
||||
/* one of these for every pending CIFS request to the server */
|
||||
struct mid_q_entry {
|
||||
struct list_head qhead; /* mids waiting on reply from this server */
|
||||
struct TCP_Server_Info *server; /* server corresponding to this mid */
|
||||
__u64 mid; /* multiplex id */
|
||||
__u32 pid; /* process id */
|
||||
__u32 sequence_number; /* for CIFS signing */
|
||||
@ -1153,6 +1156,8 @@ void cifs_oplock_break(struct work_struct *work);
|
||||
extern const struct slow_work_ops cifs_oplock_break_ops;
|
||||
extern struct workqueue_struct *cifsiod_wq;
|
||||
|
||||
extern mempool_t *cifs_mid_poolp;
|
||||
|
||||
/* Operations for different SMB versions */
|
||||
#define SMB1_VERSION_STRING "1.0"
|
||||
extern struct smb_version_operations smb1_operations;
|
||||
|
@ -68,6 +68,7 @@ extern char *cifs_compose_mount_options(const char *sb_mountdata,
|
||||
extern struct mid_q_entry *AllocMidQEntry(const struct smb_hdr *smb_buffer,
|
||||
struct TCP_Server_Info *server);
|
||||
extern void DeleteMidQEntry(struct mid_q_entry *midEntry);
|
||||
extern void cifs_wake_up_task(struct mid_q_entry *mid);
|
||||
extern int cifs_call_async(struct TCP_Server_Info *server, struct kvec *iov,
|
||||
unsigned int nvec, mid_receive_t *receive,
|
||||
mid_callback_t *callback, void *cbdata,
|
||||
|
@ -445,4 +445,5 @@ struct smb_version_values smb1_values = {
|
||||
.header_size = sizeof(struct smb_hdr),
|
||||
.max_header_size = MAX_CIFS_HDR_SIZE,
|
||||
.read_rsp_size = sizeof(READ_RSP),
|
||||
.lock_cmd = cpu_to_le16(SMB_COM_LOCKING_ANDX),
|
||||
};
|
||||
|
@ -18,10 +18,27 @@
|
||||
*/
|
||||
|
||||
#include "cifsglob.h"
|
||||
#include "smb2pdu.h"
|
||||
#include "smb2proto.h"
|
||||
|
||||
static __u64
|
||||
smb2_get_next_mid(struct TCP_Server_Info *server)
|
||||
{
|
||||
__u64 mid;
|
||||
/* for SMB2 we need the current value */
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
mid = server->CurrentMid++;
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
return mid;
|
||||
}
|
||||
|
||||
struct smb_version_operations smb21_operations = {
|
||||
.setup_request = smb2_setup_request,
|
||||
.check_receive = smb2_check_receive,
|
||||
.get_next_mid = smb2_get_next_mid,
|
||||
};
|
||||
|
||||
struct smb_version_values smb21_values = {
|
||||
.version_string = SMB21_VERSION_STRING,
|
||||
.lock_cmd = SMB2_LOCK,
|
||||
};
|
||||
|
@ -26,6 +26,65 @@
|
||||
|
||||
#include <net/sock.h>
|
||||
|
||||
/*
|
||||
* Note that, due to trying to use names similar to the protocol specifications,
|
||||
* there are many mixed case field names in the structures below. Although
|
||||
* this does not match typical Linux kernel style, it is necessary to be
|
||||
* be able to match against the protocol specfication.
|
||||
*
|
||||
* SMB2 commands
|
||||
* Some commands have minimal (wct=0,bcc=0), or uninteresting, responses
|
||||
* (ie no useful data other than the SMB error code itself) and are marked such.
|
||||
* Knowing this helps avoid response buffer allocations and copy in some cases.
|
||||
*/
|
||||
|
||||
/* List of commands in host endian */
|
||||
#define SMB2_NEGOTIATE_HE 0x0000
|
||||
#define SMB2_SESSION_SETUP_HE 0x0001
|
||||
#define SMB2_LOGOFF_HE 0x0002 /* trivial request/resp */
|
||||
#define SMB2_TREE_CONNECT_HE 0x0003
|
||||
#define SMB2_TREE_DISCONNECT_HE 0x0004 /* trivial req/resp */
|
||||
#define SMB2_CREATE_HE 0x0005
|
||||
#define SMB2_CLOSE_HE 0x0006
|
||||
#define SMB2_FLUSH_HE 0x0007 /* trivial resp */
|
||||
#define SMB2_READ_HE 0x0008
|
||||
#define SMB2_WRITE_HE 0x0009
|
||||
#define SMB2_LOCK_HE 0x000A
|
||||
#define SMB2_IOCTL_HE 0x000B
|
||||
#define SMB2_CANCEL_HE 0x000C
|
||||
#define SMB2_ECHO_HE 0x000D
|
||||
#define SMB2_QUERY_DIRECTORY_HE 0x000E
|
||||
#define SMB2_CHANGE_NOTIFY_HE 0x000F
|
||||
#define SMB2_QUERY_INFO_HE 0x0010
|
||||
#define SMB2_SET_INFO_HE 0x0011
|
||||
#define SMB2_OPLOCK_BREAK_HE 0x0012
|
||||
|
||||
/* The same list in little endian */
|
||||
#define SMB2_NEGOTIATE cpu_to_le16(SMB2_NEGOTIATE_HE)
|
||||
#define SMB2_SESSION_SETUP cpu_to_le16(SMB2_SESSION_SETUP_HE)
|
||||
#define SMB2_LOGOFF cpu_to_le16(SMB2_LOGOFF_HE)
|
||||
#define SMB2_TREE_CONNECT cpu_to_le16(SMB2_TREE_CONNECT_HE)
|
||||
#define SMB2_TREE_DISCONNECT cpu_to_le16(SMB2_TREE_DISCONNECT_HE)
|
||||
#define SMB2_CREATE cpu_to_le16(SMB2_CREATE_HE)
|
||||
#define SMB2_CLOSE cpu_to_le16(SMB2_CLOSE_HE)
|
||||
#define SMB2_FLUSH cpu_to_le16(SMB2_FLUSH_HE)
|
||||
#define SMB2_READ cpu_to_le16(SMB2_READ_HE)
|
||||
#define SMB2_WRITE cpu_to_le16(SMB2_WRITE_HE)
|
||||
#define SMB2_LOCK cpu_to_le16(SMB2_LOCK_HE)
|
||||
#define SMB2_IOCTL cpu_to_le16(SMB2_IOCTL_HE)
|
||||
#define SMB2_CANCEL cpu_to_le16(SMB2_CANCEL_HE)
|
||||
#define SMB2_ECHO cpu_to_le16(SMB2_ECHO_HE)
|
||||
#define SMB2_QUERY_DIRECTORY cpu_to_le16(SMB2_QUERY_DIRECTORY_HE)
|
||||
#define SMB2_CHANGE_NOTIFY cpu_to_le16(SMB2_CHANGE_NOTIFY_HE)
|
||||
#define SMB2_QUERY_INFO cpu_to_le16(SMB2_QUERY_INFO_HE)
|
||||
#define SMB2_SET_INFO cpu_to_le16(SMB2_SET_INFO_HE)
|
||||
#define SMB2_OPLOCK_BREAK cpu_to_le16(SMB2_OPLOCK_BREAK_HE)
|
||||
|
||||
#define NUMBER_OF_SMB2_COMMANDS 0x0013
|
||||
|
||||
/* BB FIXME - analyze following length BB */
|
||||
#define MAX_SMB2_HDR_SIZE 0x78 /* 4 len + 64 hdr + (2*24 wct) + 2 bct + 2 pad */
|
||||
|
||||
/*
|
||||
* SMB2 Header Definition
|
||||
*
|
||||
|
@ -34,4 +34,9 @@ struct statfs;
|
||||
*/
|
||||
extern int map_smb2_to_linux_error(char *buf, bool log_err);
|
||||
|
||||
extern int smb2_check_receive(struct mid_q_entry *mid,
|
||||
struct TCP_Server_Info *server, bool log_error);
|
||||
extern int smb2_setup_request(struct cifs_ses *ses, struct kvec *iov,
|
||||
unsigned int nvec, struct mid_q_entry **ret_mid);
|
||||
|
||||
#endif /* _SMB2PROTO_H */
|
||||
|
151
fs/cifs/smb2transport.c
Normal file
151
fs/cifs/smb2transport.c
Normal file
@ -0,0 +1,151 @@
|
||||
/*
|
||||
* fs/cifs/smb2transport.c
|
||||
*
|
||||
* Copyright (C) International Business Machines Corp., 2002, 2011
|
||||
* Etersoft, 2012
|
||||
* Author(s): Steve French (sfrench@us.ibm.com)
|
||||
* Jeremy Allison (jra@samba.org) 2006
|
||||
* 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.
|
||||
*
|
||||
* 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 <linux/fs.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <asm/processor.h>
|
||||
#include <linux/mempool.h>
|
||||
#include "smb2pdu.h"
|
||||
#include "cifsglob.h"
|
||||
#include "cifsproto.h"
|
||||
#include "smb2proto.h"
|
||||
#include "cifs_debug.h"
|
||||
#include "smb2status.h"
|
||||
|
||||
/*
|
||||
* Set message id for the request. Should be called after wait_for_free_request
|
||||
* and when srv_mutex is held.
|
||||
*/
|
||||
static inline void
|
||||
smb2_seq_num_into_buf(struct TCP_Server_Info *server, struct smb2_hdr *hdr)
|
||||
{
|
||||
hdr->MessageId = get_next_mid(server);
|
||||
}
|
||||
|
||||
static struct mid_q_entry *
|
||||
smb2_mid_entry_alloc(const struct smb2_hdr *smb_buffer,
|
||||
struct TCP_Server_Info *server)
|
||||
{
|
||||
struct mid_q_entry *temp;
|
||||
|
||||
if (server == NULL) {
|
||||
cERROR(1, "Null TCP session in smb2_mid_entry_alloc");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS);
|
||||
if (temp == NULL)
|
||||
return temp;
|
||||
else {
|
||||
memset(temp, 0, sizeof(struct mid_q_entry));
|
||||
temp->mid = smb_buffer->MessageId; /* always LE */
|
||||
temp->pid = current->pid;
|
||||
temp->command = smb_buffer->Command; /* Always LE */
|
||||
temp->when_alloc = jiffies;
|
||||
temp->server = server;
|
||||
|
||||
/*
|
||||
* The default is for the mid to be synchronous, so the
|
||||
* default callback just wakes up the current task.
|
||||
*/
|
||||
temp->callback = cifs_wake_up_task;
|
||||
temp->callback_data = current;
|
||||
}
|
||||
|
||||
atomic_inc(&midCount);
|
||||
temp->mid_state = MID_REQUEST_ALLOCATED;
|
||||
return temp;
|
||||
}
|
||||
|
||||
static int
|
||||
smb2_get_mid_entry(struct cifs_ses *ses, struct smb2_hdr *buf,
|
||||
struct mid_q_entry **mid)
|
||||
{
|
||||
if (ses->server->tcpStatus == CifsExiting)
|
||||
return -ENOENT;
|
||||
|
||||
if (ses->server->tcpStatus == CifsNeedReconnect) {
|
||||
cFYI(1, "tcp session dead - return to caller to retry");
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (ses->status != CifsGood) {
|
||||
/* check if SMB2 session is bad because we are setting it up */
|
||||
if ((buf->Command != SMB2_SESSION_SETUP) &&
|
||||
(buf->Command != SMB2_NEGOTIATE))
|
||||
return -EAGAIN;
|
||||
/* else ok - we are setting up session */
|
||||
}
|
||||
*mid = smb2_mid_entry_alloc(buf, ses->server);
|
||||
if (*mid == NULL)
|
||||
return -ENOMEM;
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
list_add_tail(&(*mid)->qhead, &ses->server->pending_mid_q);
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
smb2_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
|
||||
bool log_error)
|
||||
{
|
||||
unsigned int len = get_rfc1002_length(mid->resp_buf);
|
||||
|
||||
dump_smb(mid->resp_buf, min_t(u32, 80, len));
|
||||
/* convert the length into a more usable form */
|
||||
/* BB - uncomment with SMB2 signing implementation */
|
||||
/* if ((len > 24) &&
|
||||
(server->sec_mode & (SECMODE_SIGN_REQUIRED|SECMODE_SIGN_ENABLED))) {
|
||||
if (smb2_verify_signature(mid->resp_buf, server))
|
||||
cERROR(1, "Unexpected SMB signature");
|
||||
} */
|
||||
|
||||
return map_smb2_to_linux_error(mid->resp_buf, log_error);
|
||||
}
|
||||
|
||||
int
|
||||
smb2_setup_request(struct cifs_ses *ses, struct kvec *iov,
|
||||
unsigned int nvec, struct mid_q_entry **ret_mid)
|
||||
{
|
||||
int rc;
|
||||
struct smb2_hdr *hdr = (struct smb2_hdr *)iov[0].iov_base;
|
||||
struct mid_q_entry *mid;
|
||||
|
||||
smb2_seq_num_into_buf(ses->server, hdr);
|
||||
|
||||
rc = smb2_get_mid_entry(ses, hdr, &mid);
|
||||
if (rc)
|
||||
return rc;
|
||||
/* rc = smb2_sign_smb2(iov, nvec, ses->server);
|
||||
if (rc)
|
||||
delete_mid(mid); */
|
||||
*ret_mid = mid;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* BB add missing functions here */
|
@ -35,10 +35,8 @@
|
||||
#include "cifsproto.h"
|
||||
#include "cifs_debug.h"
|
||||
|
||||
extern mempool_t *cifs_mid_poolp;
|
||||
|
||||
static void
|
||||
wake_up_task(struct mid_q_entry *mid)
|
||||
void
|
||||
cifs_wake_up_task(struct mid_q_entry *mid)
|
||||
{
|
||||
wake_up_process(mid->callback_data);
|
||||
}
|
||||
@ -65,12 +63,13 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server)
|
||||
/* do_gettimeofday(&temp->when_sent);*/ /* easier to use jiffies */
|
||||
/* when mid allocated can be before when sent */
|
||||
temp->when_alloc = jiffies;
|
||||
temp->server = server;
|
||||
|
||||
/*
|
||||
* The default is for the mid to be synchronous, so the
|
||||
* default callback just wakes up the current task.
|
||||
*/
|
||||
temp->callback = wake_up_task;
|
||||
temp->callback = cifs_wake_up_task;
|
||||
temp->callback_data = current;
|
||||
}
|
||||
|
||||
@ -83,6 +82,7 @@ void
|
||||
DeleteMidQEntry(struct mid_q_entry *midEntry)
|
||||
{
|
||||
#ifdef CONFIG_CIFS_STATS2
|
||||
__le16 command = midEntry->server->vals->lock_cmd;
|
||||
unsigned long now;
|
||||
#endif
|
||||
midEntry->mid_state = MID_FREE;
|
||||
@ -96,8 +96,7 @@ DeleteMidQEntry(struct mid_q_entry *midEntry)
|
||||
/* commands taking longer than one second are indications that
|
||||
something is wrong, unless it is quite a slow link or server */
|
||||
if ((now - midEntry->when_alloc) > HZ) {
|
||||
if ((cifsFYI & CIFS_TIMER) &&
|
||||
(midEntry->command != cpu_to_le16(SMB_COM_LOCKING_ANDX))) {
|
||||
if ((cifsFYI & CIFS_TIMER) && (midEntry->command != command)) {
|
||||
printk(KERN_DEBUG " CIFS slow rsp: cmd %d mid %llu",
|
||||
midEntry->command, midEntry->mid);
|
||||
printk(" A: 0x%lx S: 0x%lx R: 0x%lx\n",
|
||||
|
Loading…
Reference in New Issue
Block a user