linux/fs/cifs/smb2ops.c
<<
>>
Prefs
   1/*
   2 *  SMB2 version specific operations
   3 *
   4 *  Copyright (c) 2012, Jeff Layton <jlayton@redhat.com>
   5 *
   6 *  This library is free software; you can redistribute it and/or modify
   7 *  it under the terms of the GNU General Public License v2 as published
   8 *  by the Free Software Foundation.
   9 *
  10 *  This library is distributed in the hope that it will be useful,
  11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
  13 *  the GNU Lesser General Public License for more details.
  14 *
  15 *  You should have received a copy of the GNU Lesser General Public License
  16 *  along with this library; if not, write to the Free Software
  17 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  18 */
  19
  20#include "cifsglob.h"
  21#include "smb2pdu.h"
  22#include "smb2proto.h"
  23#include "cifsproto.h"
  24#include "cifs_debug.h"
  25
  26static int
  27change_conf(struct TCP_Server_Info *server)
  28{
  29        server->credits += server->echo_credits + server->oplock_credits;
  30        server->oplock_credits = server->echo_credits = 0;
  31        switch (server->credits) {
  32        case 0:
  33                return -1;
  34        case 1:
  35                server->echoes = false;
  36                server->oplocks = false;
  37                cERROR(1, "disabling echoes and oplocks");
  38                break;
  39        case 2:
  40                server->echoes = true;
  41                server->oplocks = false;
  42                server->echo_credits = 1;
  43                cFYI(1, "disabling oplocks");
  44                break;
  45        default:
  46                server->echoes = true;
  47                server->oplocks = true;
  48                server->echo_credits = 1;
  49                server->oplock_credits = 1;
  50        }
  51        server->credits -= server->echo_credits + server->oplock_credits;
  52        return 0;
  53}
  54
  55static void
  56smb2_add_credits(struct TCP_Server_Info *server, const unsigned int add,
  57                 const int optype)
  58{
  59        int *val, rc = 0;
  60        spin_lock(&server->req_lock);
  61        val = server->ops->get_credits_field(server, optype);
  62        *val += add;
  63        server->in_flight--;
  64        if (server->in_flight == 0 && (optype & CIFS_OP_MASK) != CIFS_NEG_OP)
  65                rc = change_conf(server);
  66        spin_unlock(&server->req_lock);
  67        wake_up(&server->request_q);
  68        if (rc)
  69                cifs_reconnect(server);
  70}
  71
  72static void
  73smb2_set_credits(struct TCP_Server_Info *server, const int val)
  74{
  75        spin_lock(&server->req_lock);
  76        server->credits = val;
  77        spin_unlock(&server->req_lock);
  78}
  79
  80static int *
  81smb2_get_credits_field(struct TCP_Server_Info *server, const int optype)
  82{
  83        switch (optype) {
  84        case CIFS_ECHO_OP:
  85                return &server->echo_credits;
  86        case CIFS_OBREAK_OP:
  87                return &server->oplock_credits;
  88        default:
  89                return &server->credits;
  90        }
  91}
  92
  93static unsigned int
  94smb2_get_credits(struct mid_q_entry *mid)
  95{
  96        return le16_to_cpu(((struct smb2_hdr *)mid->resp_buf)->CreditRequest);
  97}
  98
  99static __u64
 100smb2_get_next_mid(struct TCP_Server_Info *server)
 101{
 102        __u64 mid;
 103        /* for SMB2 we need the current value */
 104        spin_lock(&GlobalMid_Lock);
 105        mid = server->CurrentMid++;
 106        spin_unlock(&GlobalMid_Lock);
 107        return mid;
 108}
 109
 110static struct mid_q_entry *
 111smb2_find_mid(struct TCP_Server_Info *server, char *buf)
 112{
 113        struct mid_q_entry *mid;
 114        struct smb2_hdr *hdr = (struct smb2_hdr *)buf;
 115
 116        spin_lock(&GlobalMid_Lock);
 117        list_for_each_entry(mid, &server->pending_mid_q, qhead) {
 118                if ((mid->mid == hdr->MessageId) &&
 119                    (mid->mid_state == MID_REQUEST_SUBMITTED) &&
 120                    (mid->command == hdr->Command)) {
 121                        spin_unlock(&GlobalMid_Lock);
 122                        return mid;
 123                }
 124        }
 125        spin_unlock(&GlobalMid_Lock);
 126        return NULL;
 127}
 128
 129static void
 130smb2_dump_detail(void *buf)
 131{
 132#ifdef CONFIG_CIFS_DEBUG2
 133        struct smb2_hdr *smb = (struct smb2_hdr *)buf;
 134
 135        cERROR(1, "Cmd: %d Err: 0x%x Flags: 0x%x Mid: %llu Pid: %d",
 136                  smb->Command, smb->Status, smb->Flags, smb->MessageId,
 137                  smb->ProcessId);
 138        cERROR(1, "smb buf %p len %u", smb, smb2_calc_size(smb));
 139#endif
 140}
 141
 142static bool
 143smb2_need_neg(struct TCP_Server_Info *server)
 144{
 145        return server->max_read == 0;
 146}
 147
 148static int
 149smb2_negotiate(const unsigned int xid, struct cifs_ses *ses)
 150{
 151        int rc;
 152        ses->server->CurrentMid = 0;
 153        rc = SMB2_negotiate(xid, ses);
 154        /* BB we probably don't need to retry with modern servers */
 155        if (rc == -EAGAIN)
 156                rc = -EHOSTDOWN;
 157        return rc;
 158}
 159
 160static int
 161smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
 162                        struct cifs_sb_info *cifs_sb, const char *full_path)
 163{
 164        int rc;
 165        __u64 persistent_fid, volatile_fid;
 166        __le16 *utf16_path;
 167
 168        utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
 169        if (!utf16_path)
 170                return -ENOMEM;
 171
 172        rc = SMB2_open(xid, tcon, utf16_path, &persistent_fid, &volatile_fid,
 173                       FILE_READ_ATTRIBUTES, FILE_OPEN, 0, 0);
 174        if (rc) {
 175                kfree(utf16_path);
 176                return rc;
 177        }
 178
 179        rc = SMB2_close(xid, tcon, persistent_fid, volatile_fid);
 180        kfree(utf16_path);
 181        return rc;
 182}
 183
 184static int
 185smb2_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon,
 186                  struct cifs_sb_info *cifs_sb, const char *full_path,
 187                  u64 *uniqueid, FILE_ALL_INFO *data)
 188{
 189        *uniqueid = le64_to_cpu(data->IndexNumber);
 190        return 0;
 191}
 192
 193static char *
 194smb2_build_path_to_root(struct smb_vol *vol, struct cifs_sb_info *cifs_sb,
 195                        struct cifs_tcon *tcon)
 196{
 197        int pplen = vol->prepath ? strlen(vol->prepath) : 0;
 198        char *full_path = NULL;
 199
 200        /* if no prefix path, simply set path to the root of share to "" */
 201        if (pplen == 0) {
 202                full_path = kzalloc(2, GFP_KERNEL);
 203                return full_path;
 204        }
 205
 206        cERROR(1, "prefixpath is not supported for SMB2 now");
 207        return NULL;
 208}
 209
 210static bool
 211smb2_can_echo(struct TCP_Server_Info *server)
 212{
 213        return server->echoes;
 214}
 215
 216static void
 217smb2_clear_stats(struct cifs_tcon *tcon)
 218{
 219#ifdef CONFIG_CIFS_STATS
 220        int i;
 221        for (i = 0; i < NUMBER_OF_SMB2_COMMANDS; i++) {
 222                atomic_set(&tcon->stats.smb2_stats.smb2_com_sent[i], 0);
 223                atomic_set(&tcon->stats.smb2_stats.smb2_com_failed[i], 0);
 224        }
 225#endif
 226}
 227
 228static void
 229smb2_print_stats(struct seq_file *m, struct cifs_tcon *tcon)
 230{
 231#ifdef CONFIG_CIFS_STATS
 232        atomic_t *sent = tcon->stats.smb2_stats.smb2_com_sent;
 233        atomic_t *failed = tcon->stats.smb2_stats.smb2_com_failed;
 234        seq_printf(m, "\nNegotiates: %d sent %d failed",
 235                   atomic_read(&sent[SMB2_NEGOTIATE_HE]),
 236                   atomic_read(&failed[SMB2_NEGOTIATE_HE]));
 237        seq_printf(m, "\nSessionSetups: %d sent %d failed",
 238                   atomic_read(&sent[SMB2_SESSION_SETUP_HE]),
 239                   atomic_read(&failed[SMB2_SESSION_SETUP_HE]));
 240#define SMB2LOGOFF              0x0002 /* trivial request/resp */
 241        seq_printf(m, "\nLogoffs: %d sent %d failed",
 242                   atomic_read(&sent[SMB2_LOGOFF_HE]),
 243                   atomic_read(&failed[SMB2_LOGOFF_HE]));
 244        seq_printf(m, "\nTreeConnects: %d sent %d failed",
 245                   atomic_read(&sent[SMB2_TREE_CONNECT_HE]),
 246                   atomic_read(&failed[SMB2_TREE_CONNECT_HE]));
 247        seq_printf(m, "\nTreeDisconnects: %d sent %d failed",
 248                   atomic_read(&sent[SMB2_TREE_DISCONNECT_HE]),
 249                   atomic_read(&failed[SMB2_TREE_DISCONNECT_HE]));
 250        seq_printf(m, "\nCreates: %d sent %d failed",
 251                   atomic_read(&sent[SMB2_CREATE_HE]),
 252                   atomic_read(&failed[SMB2_CREATE_HE]));
 253        seq_printf(m, "\nCloses: %d sent %d failed",
 254                   atomic_read(&sent[SMB2_CLOSE_HE]),
 255                   atomic_read(&failed[SMB2_CLOSE_HE]));
 256        seq_printf(m, "\nFlushes: %d sent %d failed",
 257                   atomic_read(&sent[SMB2_FLUSH_HE]),
 258                   atomic_read(&failed[SMB2_FLUSH_HE]));
 259        seq_printf(m, "\nReads: %d sent %d failed",
 260                   atomic_read(&sent[SMB2_READ_HE]),
 261                   atomic_read(&failed[SMB2_READ_HE]));
 262        seq_printf(m, "\nWrites: %d sent %d failed",
 263                   atomic_read(&sent[SMB2_WRITE_HE]),
 264                   atomic_read(&failed[SMB2_WRITE_HE]));
 265        seq_printf(m, "\nLocks: %d sent %d failed",
 266                   atomic_read(&sent[SMB2_LOCK_HE]),
 267                   atomic_read(&failed[SMB2_LOCK_HE]));
 268        seq_printf(m, "\nIOCTLs: %d sent %d failed",
 269                   atomic_read(&sent[SMB2_IOCTL_HE]),
 270                   atomic_read(&failed[SMB2_IOCTL_HE]));
 271        seq_printf(m, "\nCancels: %d sent %d failed",
 272                   atomic_read(&sent[SMB2_CANCEL_HE]),
 273                   atomic_read(&failed[SMB2_CANCEL_HE]));
 274        seq_printf(m, "\nEchos: %d sent %d failed",
 275                   atomic_read(&sent[SMB2_ECHO_HE]),
 276                   atomic_read(&failed[SMB2_ECHO_HE]));
 277        seq_printf(m, "\nQueryDirectories: %d sent %d failed",
 278                   atomic_read(&sent[SMB2_QUERY_DIRECTORY_HE]),
 279                   atomic_read(&failed[SMB2_QUERY_DIRECTORY_HE]));
 280        seq_printf(m, "\nChangeNotifies: %d sent %d failed",
 281                   atomic_read(&sent[SMB2_CHANGE_NOTIFY_HE]),
 282                   atomic_read(&failed[SMB2_CHANGE_NOTIFY_HE]));
 283        seq_printf(m, "\nQueryInfos: %d sent %d failed",
 284                   atomic_read(&sent[SMB2_QUERY_INFO_HE]),
 285                   atomic_read(&failed[SMB2_QUERY_INFO_HE]));
 286        seq_printf(m, "\nSetInfos: %d sent %d failed",
 287                   atomic_read(&sent[SMB2_SET_INFO_HE]),
 288                   atomic_read(&failed[SMB2_SET_INFO_HE]));
 289        seq_printf(m, "\nOplockBreaks: %d sent %d failed",
 290                   atomic_read(&sent[SMB2_OPLOCK_BREAK_HE]),
 291                   atomic_read(&failed[SMB2_OPLOCK_BREAK_HE]));
 292#endif
 293}
 294
 295struct smb_version_operations smb21_operations = {
 296        .setup_request = smb2_setup_request,
 297        .setup_async_request = smb2_setup_async_request,
 298        .check_receive = smb2_check_receive,
 299        .add_credits = smb2_add_credits,
 300        .set_credits = smb2_set_credits,
 301        .get_credits_field = smb2_get_credits_field,
 302        .get_credits = smb2_get_credits,
 303        .get_next_mid = smb2_get_next_mid,
 304        .find_mid = smb2_find_mid,
 305        .check_message = smb2_check_message,
 306        .dump_detail = smb2_dump_detail,
 307        .clear_stats = smb2_clear_stats,
 308        .print_stats = smb2_print_stats,
 309        .need_neg = smb2_need_neg,
 310        .negotiate = smb2_negotiate,
 311        .sess_setup = SMB2_sess_setup,
 312        .logoff = SMB2_logoff,
 313        .tree_connect = SMB2_tcon,
 314        .tree_disconnect = SMB2_tdis,
 315        .is_path_accessible = smb2_is_path_accessible,
 316        .can_echo = smb2_can_echo,
 317        .echo = SMB2_echo,
 318        .query_path_info = smb2_query_path_info,
 319        .get_srv_inum = smb2_get_srv_inum,
 320        .build_path_to_root = smb2_build_path_to_root,
 321        .mkdir = smb2_mkdir,
 322        .mkdir_setinfo = smb2_mkdir_setinfo,
 323        .rmdir = smb2_rmdir,
 324};
 325
 326struct smb_version_values smb21_values = {
 327        .version_string = SMB21_VERSION_STRING,
 328        .header_size = sizeof(struct smb2_hdr),
 329        .max_header_size = MAX_SMB2_HDR_SIZE,
 330        .lock_cmd = SMB2_LOCK,
 331        .cap_unix = 0,
 332        .cap_nt_find = SMB2_NT_FIND,
 333        .cap_large_files = SMB2_LARGE_FILES,
 334};
 335
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.