123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334 |
- /*
- * SMB2 version specific operations
- *
- * Copyright (c) 2012, Jeff Layton <jlayton@redhat.com>
- *
- * This library is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License v2 as published
- * by the Free Software Foundation.
- *
- * 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 "cifsglob.h"
- #include "smb2pdu.h"
- #include "smb2proto.h"
- #include "cifsproto.h"
- #include "cifs_debug.h"
- static int
- change_conf(struct TCP_Server_Info *server)
- {
- server->credits += server->echo_credits + server->oplock_credits;
- server->oplock_credits = server->echo_credits = 0;
- switch (server->credits) {
- case 0:
- return -1;
- case 1:
- server->echoes = false;
- server->oplocks = false;
- cERROR(1, "disabling echoes and oplocks");
- break;
- case 2:
- server->echoes = true;
- server->oplocks = false;
- server->echo_credits = 1;
- cFYI(1, "disabling oplocks");
- break;
- default:
- server->echoes = true;
- server->oplocks = true;
- server->echo_credits = 1;
- server->oplock_credits = 1;
- }
- server->credits -= server->echo_credits + server->oplock_credits;
- return 0;
- }
- static void
- smb2_add_credits(struct TCP_Server_Info *server, const unsigned int add,
- const int optype)
- {
- int *val, rc = 0;
- spin_lock(&server->req_lock);
- val = server->ops->get_credits_field(server, optype);
- *val += add;
- server->in_flight--;
- if (server->in_flight == 0 && (optype & CIFS_OP_MASK) != CIFS_NEG_OP)
- rc = change_conf(server);
- spin_unlock(&server->req_lock);
- wake_up(&server->request_q);
- if (rc)
- cifs_reconnect(server);
- }
- static void
- smb2_set_credits(struct TCP_Server_Info *server, const int val)
- {
- spin_lock(&server->req_lock);
- server->credits = val;
- spin_unlock(&server->req_lock);
- }
- static int *
- smb2_get_credits_field(struct TCP_Server_Info *server, const int optype)
- {
- switch (optype) {
- case CIFS_ECHO_OP:
- return &server->echo_credits;
- case CIFS_OBREAK_OP:
- return &server->oplock_credits;
- default:
- return &server->credits;
- }
- }
- static unsigned int
- smb2_get_credits(struct mid_q_entry *mid)
- {
- return le16_to_cpu(((struct smb2_hdr *)mid->resp_buf)->CreditRequest);
- }
- 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;
- }
- static struct mid_q_entry *
- smb2_find_mid(struct TCP_Server_Info *server, char *buf)
- {
- struct mid_q_entry *mid;
- struct smb2_hdr *hdr = (struct smb2_hdr *)buf;
- spin_lock(&GlobalMid_Lock);
- list_for_each_entry(mid, &server->pending_mid_q, qhead) {
- if ((mid->mid == hdr->MessageId) &&
- (mid->mid_state == MID_REQUEST_SUBMITTED) &&
- (mid->command == hdr->Command)) {
- spin_unlock(&GlobalMid_Lock);
- return mid;
- }
- }
- spin_unlock(&GlobalMid_Lock);
- return NULL;
- }
- static void
- smb2_dump_detail(void *buf)
- {
- #ifdef CONFIG_CIFS_DEBUG2
- struct smb2_hdr *smb = (struct smb2_hdr *)buf;
- cERROR(1, "Cmd: %d Err: 0x%x Flags: 0x%x Mid: %llu Pid: %d",
- smb->Command, smb->Status, smb->Flags, smb->MessageId,
- smb->ProcessId);
- cERROR(1, "smb buf %p len %u", smb, smb2_calc_size(smb));
- #endif
- }
- static bool
- smb2_need_neg(struct TCP_Server_Info *server)
- {
- return server->max_read == 0;
- }
- static int
- smb2_negotiate(const unsigned int xid, struct cifs_ses *ses)
- {
- int rc;
- ses->server->CurrentMid = 0;
- rc = SMB2_negotiate(xid, ses);
- /* BB we probably don't need to retry with modern servers */
- if (rc == -EAGAIN)
- rc = -EHOSTDOWN;
- return rc;
- }
- static int
- smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
- struct cifs_sb_info *cifs_sb, const char *full_path)
- {
- int rc;
- __u64 persistent_fid, volatile_fid;
- __le16 *utf16_path;
- utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
- if (!utf16_path)
- return -ENOMEM;
- rc = SMB2_open(xid, tcon, utf16_path, &persistent_fid, &volatile_fid,
- FILE_READ_ATTRIBUTES, FILE_OPEN, 0, 0);
- if (rc) {
- kfree(utf16_path);
- return rc;
- }
- rc = SMB2_close(xid, tcon, persistent_fid, volatile_fid);
- kfree(utf16_path);
- return rc;
- }
- static int
- smb2_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon,
- struct cifs_sb_info *cifs_sb, const char *full_path,
- u64 *uniqueid, FILE_ALL_INFO *data)
- {
- *uniqueid = le64_to_cpu(data->IndexNumber);
- return 0;
- }
- static char *
- smb2_build_path_to_root(struct smb_vol *vol, struct cifs_sb_info *cifs_sb,
- struct cifs_tcon *tcon)
- {
- int pplen = vol->prepath ? strlen(vol->prepath) : 0;
- char *full_path = NULL;
- /* if no prefix path, simply set path to the root of share to "" */
- if (pplen == 0) {
- full_path = kzalloc(2, GFP_KERNEL);
- return full_path;
- }
- cERROR(1, "prefixpath is not supported for SMB2 now");
- return NULL;
- }
- static bool
- smb2_can_echo(struct TCP_Server_Info *server)
- {
- return server->echoes;
- }
- static void
- smb2_clear_stats(struct cifs_tcon *tcon)
- {
- #ifdef CONFIG_CIFS_STATS
- int i;
- for (i = 0; i < NUMBER_OF_SMB2_COMMANDS; i++) {
- atomic_set(&tcon->stats.smb2_stats.smb2_com_sent[i], 0);
- atomic_set(&tcon->stats.smb2_stats.smb2_com_failed[i], 0);
- }
- #endif
- }
- static void
- smb2_print_stats(struct seq_file *m, struct cifs_tcon *tcon)
- {
- #ifdef CONFIG_CIFS_STATS
- atomic_t *sent = tcon->stats.smb2_stats.smb2_com_sent;
- atomic_t *failed = tcon->stats.smb2_stats.smb2_com_failed;
- seq_printf(m, "\nNegotiates: %d sent %d failed",
- atomic_read(&sent[SMB2_NEGOTIATE_HE]),
- atomic_read(&failed[SMB2_NEGOTIATE_HE]));
- seq_printf(m, "\nSessionSetups: %d sent %d failed",
- atomic_read(&sent[SMB2_SESSION_SETUP_HE]),
- atomic_read(&failed[SMB2_SESSION_SETUP_HE]));
- #define SMB2LOGOFF 0x0002 /* trivial request/resp */
- seq_printf(m, "\nLogoffs: %d sent %d failed",
- atomic_read(&sent[SMB2_LOGOFF_HE]),
- atomic_read(&failed[SMB2_LOGOFF_HE]));
- seq_printf(m, "\nTreeConnects: %d sent %d failed",
- atomic_read(&sent[SMB2_TREE_CONNECT_HE]),
- atomic_read(&failed[SMB2_TREE_CONNECT_HE]));
- seq_printf(m, "\nTreeDisconnects: %d sent %d failed",
- atomic_read(&sent[SMB2_TREE_DISCONNECT_HE]),
- atomic_read(&failed[SMB2_TREE_DISCONNECT_HE]));
- seq_printf(m, "\nCreates: %d sent %d failed",
- atomic_read(&sent[SMB2_CREATE_HE]),
- atomic_read(&failed[SMB2_CREATE_HE]));
- seq_printf(m, "\nCloses: %d sent %d failed",
- atomic_read(&sent[SMB2_CLOSE_HE]),
- atomic_read(&failed[SMB2_CLOSE_HE]));
- seq_printf(m, "\nFlushes: %d sent %d failed",
- atomic_read(&sent[SMB2_FLUSH_HE]),
- atomic_read(&failed[SMB2_FLUSH_HE]));
- seq_printf(m, "\nReads: %d sent %d failed",
- atomic_read(&sent[SMB2_READ_HE]),
- atomic_read(&failed[SMB2_READ_HE]));
- seq_printf(m, "\nWrites: %d sent %d failed",
- atomic_read(&sent[SMB2_WRITE_HE]),
- atomic_read(&failed[SMB2_WRITE_HE]));
- seq_printf(m, "\nLocks: %d sent %d failed",
- atomic_read(&sent[SMB2_LOCK_HE]),
- atomic_read(&failed[SMB2_LOCK_HE]));
- seq_printf(m, "\nIOCTLs: %d sent %d failed",
- atomic_read(&sent[SMB2_IOCTL_HE]),
- atomic_read(&failed[SMB2_IOCTL_HE]));
- seq_printf(m, "\nCancels: %d sent %d failed",
- atomic_read(&sent[SMB2_CANCEL_HE]),
- atomic_read(&failed[SMB2_CANCEL_HE]));
- seq_printf(m, "\nEchos: %d sent %d failed",
- atomic_read(&sent[SMB2_ECHO_HE]),
- atomic_read(&failed[SMB2_ECHO_HE]));
- seq_printf(m, "\nQueryDirectories: %d sent %d failed",
- atomic_read(&sent[SMB2_QUERY_DIRECTORY_HE]),
- atomic_read(&failed[SMB2_QUERY_DIRECTORY_HE]));
- seq_printf(m, "\nChangeNotifies: %d sent %d failed",
- atomic_read(&sent[SMB2_CHANGE_NOTIFY_HE]),
- atomic_read(&failed[SMB2_CHANGE_NOTIFY_HE]));
- seq_printf(m, "\nQueryInfos: %d sent %d failed",
- atomic_read(&sent[SMB2_QUERY_INFO_HE]),
- atomic_read(&failed[SMB2_QUERY_INFO_HE]));
- seq_printf(m, "\nSetInfos: %d sent %d failed",
- atomic_read(&sent[SMB2_SET_INFO_HE]),
- atomic_read(&failed[SMB2_SET_INFO_HE]));
- seq_printf(m, "\nOplockBreaks: %d sent %d failed",
- atomic_read(&sent[SMB2_OPLOCK_BREAK_HE]),
- atomic_read(&failed[SMB2_OPLOCK_BREAK_HE]));
- #endif
- }
- struct smb_version_operations smb21_operations = {
- .setup_request = smb2_setup_request,
- .setup_async_request = smb2_setup_async_request,
- .check_receive = smb2_check_receive,
- .add_credits = smb2_add_credits,
- .set_credits = smb2_set_credits,
- .get_credits_field = smb2_get_credits_field,
- .get_credits = smb2_get_credits,
- .get_next_mid = smb2_get_next_mid,
- .find_mid = smb2_find_mid,
- .check_message = smb2_check_message,
- .dump_detail = smb2_dump_detail,
- .clear_stats = smb2_clear_stats,
- .print_stats = smb2_print_stats,
- .need_neg = smb2_need_neg,
- .negotiate = smb2_negotiate,
- .sess_setup = SMB2_sess_setup,
- .logoff = SMB2_logoff,
- .tree_connect = SMB2_tcon,
- .tree_disconnect = SMB2_tdis,
- .is_path_accessible = smb2_is_path_accessible,
- .can_echo = smb2_can_echo,
- .echo = SMB2_echo,
- .query_path_info = smb2_query_path_info,
- .get_srv_inum = smb2_get_srv_inum,
- .build_path_to_root = smb2_build_path_to_root,
- .mkdir = smb2_mkdir,
- .mkdir_setinfo = smb2_mkdir_setinfo,
- .rmdir = smb2_rmdir,
- };
- struct smb_version_values smb21_values = {
- .version_string = SMB21_VERSION_STRING,
- .header_size = sizeof(struct smb2_hdr),
- .max_header_size = MAX_SMB2_HDR_SIZE,
- .lock_cmd = SMB2_LOCK,
- .cap_unix = 0,
- .cap_nt_find = SMB2_NT_FIND,
- .cap_large_files = SMB2_LARGE_FILES,
- };
|