CIFS: Make transport routines work with SMB2
authorPavel Shilovsky <piastry@etersoft.ru>
Mon, 26 Dec 2011 18:53:34 +0000 (22:53 +0400)
committerSteve French <smfrench@gmail.com>
Tue, 24 Jul 2012 15:25:20 +0000 (10:25 -0500)
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Pavel Shilovsky <piastry@etersoft.ru>
Signed-off-by: Steve French <smfrench@gmail.com>
fs/cifs/Makefile
fs/cifs/cifsglob.h
fs/cifs/cifsproto.h
fs/cifs/smb1ops.c
fs/cifs/smb2ops.c
fs/cifs/smb2pdu.h
fs/cifs/smb2proto.h
fs/cifs/smb2transport.c [new file with mode: 0644]
fs/cifs/transport.c

index 4a77271437214ddd1694f1f7b22de6601986df00..a73d7f888846fbea535e902ebc33f4439c1c530b 100644 (file)
@@ -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
index 6d18962c99034b93d488c35909589f4ab1b5d46a..3575f0f832b19d2499b7547c04580017ecb6733d 100644 (file)
@@ -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;
index 8797e40646620b0b8b37786069b9de9b5086bc5d..88967d0885bfa703e93f8ce554a7c39e36559183 100644 (file)
@@ -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,
index 728595f096c993f1c88302ff8f1b64612f9791e7..8f873863142a07a77662639d03e2954fc034e5d4 100644 (file)
@@ -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),
 };
index f065e89756a167d1354835d72d260c1223ab5700..09530f41612306a7a69753e6b3e589b56f1fe79a 100644 (file)
  */
 
 #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,
 };
index d35ac689f24ba483d94732df82dff53fe58509be..c7f52e363d37864b371b6e50648698299b865e0e 100644 (file)
 
 #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
  *
index 08249eecdf69c1b50134a77cbaf849cd85f046f2..0e59afb5edf991f568d6397fc314d012049b7977 100644 (file)
@@ -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 */
diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c
new file mode 100644 (file)
index 0000000..b4b6b9a
--- /dev/null
@@ -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 */
index 904702db25269cded30cfb24ac8368126fcf6376..bcc02b476f6e5c93d09d9d8037fb66060a84620f 100644 (file)
 #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",