forked from Minki/linux
c481e9feee
When CONFIG_CIFS_STATS2 enabled query adapter info for debugging It is easy now in SMB3 to query the information about the server's network interfaces (and at least Windows 8 and above do this, if not other clients) there are some useful pieces of information you can get including: - all of the network interfaces that the server advertises (not just the one you are mounting over), and with SMB3 supporting multichannel this helps with more than just failover (also aggregating multiple sockets under one mount) - whether the adapter supports RSS (useful to know if you want to estimate whether setting up two or more socket connections to the same address is going to be faster due to RSS offload in the adapter) - whether the server supports RDMA - whether the server has IPv6 interfaces (if you connected over IPv4 but prefer IPv6 e.g.) - what the link speed is (you might want to reconnect over a higher speed interface if available) (Of course we could also rerequest this on every mount cheaplly to the same server, as Windows apparently does, so we can update the adapter info on new mounts, and also on every reconnect if the network interface drops temporarily - so we don't have to rely on info from the first mount to this server) It is trivial to request this information - and certainly will be useful when we get to the point of doing multichannel (and eventually RDMA), but some of this (linkspeed etc.) info may help for debugging in the meantime. Enable this request when CONFIG_CIFS_STATS2 is on (only for smb3 mounts since it is an SMB3 or later ioctl). Signed-off-by: Steve French <smfrench@gmail.com>
1249 lines
37 KiB
C
1249 lines
37 KiB
C
/*
|
|
* 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 <linux/pagemap.h>
|
|
#include <linux/vfs.h>
|
|
#include "cifsglob.h"
|
|
#include "smb2pdu.h"
|
|
#include "smb2proto.h"
|
|
#include "cifsproto.h"
|
|
#include "cifs_debug.h"
|
|
#include "cifs_unicode.h"
|
|
#include "smb2status.h"
|
|
#include "smb2glob.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;
|
|
cifs_dbg(VFS, "disabling echoes and oplocks\n");
|
|
break;
|
|
case 2:
|
|
server->echoes = true;
|
|
server->oplocks = false;
|
|
server->echo_credits = 1;
|
|
cifs_dbg(FYI, "disabling oplocks\n");
|
|
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);
|
|
/*
|
|
* Sometimes server returns 0 credits on oplock break ack - we need to
|
|
* rebalance credits in this case.
|
|
*/
|
|
else if (server->in_flight > 0 && server->oplock_credits == 0 &&
|
|
server->oplocks) {
|
|
if (server->credits > 1) {
|
|
server->credits--;
|
|
server->oplock_credits++;
|
|
}
|
|
}
|
|
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;
|
|
|
|
cifs_dbg(VFS, "Cmd: %d Err: 0x%x Flags: 0x%x Mid: %llu Pid: %d\n",
|
|
smb->Command, smb->Status, smb->Flags, smb->MessageId,
|
|
smb->ProcessId);
|
|
cifs_dbg(VFS, "smb buf %p len %u\n", 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 unsigned int
|
|
smb2_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *volume_info)
|
|
{
|
|
struct TCP_Server_Info *server = tcon->ses->server;
|
|
unsigned int wsize;
|
|
|
|
/* start with specified wsize, or default */
|
|
wsize = volume_info->wsize ? volume_info->wsize : CIFS_DEFAULT_IOSIZE;
|
|
wsize = min_t(unsigned int, wsize, server->max_write);
|
|
/*
|
|
* limit write size to 2 ** 16, because we don't support multicredit
|
|
* requests now.
|
|
*/
|
|
wsize = min_t(unsigned int, wsize, 2 << 15);
|
|
|
|
return wsize;
|
|
}
|
|
|
|
static unsigned int
|
|
smb2_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info)
|
|
{
|
|
struct TCP_Server_Info *server = tcon->ses->server;
|
|
unsigned int rsize;
|
|
|
|
/* start with specified rsize, or default */
|
|
rsize = volume_info->rsize ? volume_info->rsize : CIFS_DEFAULT_IOSIZE;
|
|
rsize = min_t(unsigned int, rsize, server->max_read);
|
|
/*
|
|
* limit write size to 2 ** 16, because we don't support multicredit
|
|
* requests now.
|
|
*/
|
|
rsize = min_t(unsigned int, rsize, 2 << 15);
|
|
|
|
return rsize;
|
|
}
|
|
|
|
#ifdef CONFIG_CIFS_STATS2
|
|
static int
|
|
SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon)
|
|
{
|
|
int rc;
|
|
unsigned int ret_data_len = 0;
|
|
struct network_interface_info_ioctl_rsp *out_buf;
|
|
|
|
rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
|
|
FSCTL_QUERY_NETWORK_INTERFACE_INFO, true /* is_fsctl */,
|
|
NULL /* no data input */, 0 /* no data input */,
|
|
(char **)&out_buf, &ret_data_len);
|
|
|
|
if ((rc == 0) && (ret_data_len > 0)) {
|
|
/* Dump info on first interface */
|
|
cifs_dbg(FYI, "Adapter Capability 0x%x\t",
|
|
le32_to_cpu(out_buf->Capability));
|
|
cifs_dbg(FYI, "Link Speed %lld\n",
|
|
le64_to_cpu(out_buf->LinkSpeed));
|
|
} else
|
|
cifs_dbg(VFS, "error %d on ioctl to get interface list\n", rc);
|
|
|
|
return rc;
|
|
}
|
|
#endif /* STATS2 */
|
|
|
|
static void
|
|
smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon)
|
|
{
|
|
int rc;
|
|
__le16 srch_path = 0; /* Null - open root of share */
|
|
u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
|
|
struct cifs_open_parms oparms;
|
|
struct cifs_fid fid;
|
|
|
|
oparms.tcon = tcon;
|
|
oparms.desired_access = FILE_READ_ATTRIBUTES;
|
|
oparms.disposition = FILE_OPEN;
|
|
oparms.create_options = 0;
|
|
oparms.fid = &fid;
|
|
oparms.reconnect = false;
|
|
|
|
rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL);
|
|
if (rc)
|
|
return;
|
|
|
|
#ifdef CONFIG_CIFS_STATS2
|
|
SMB3_request_interfaces(xid, tcon);
|
|
#endif /* STATS2 */
|
|
|
|
SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid,
|
|
FS_ATTRIBUTE_INFORMATION);
|
|
SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid,
|
|
FS_DEVICE_INFORMATION);
|
|
SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid,
|
|
FS_SECTOR_SIZE_INFORMATION); /* SMB3 specific */
|
|
SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
smb2_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon)
|
|
{
|
|
int rc;
|
|
__le16 srch_path = 0; /* Null - open root of share */
|
|
u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
|
|
struct cifs_open_parms oparms;
|
|
struct cifs_fid fid;
|
|
|
|
oparms.tcon = tcon;
|
|
oparms.desired_access = FILE_READ_ATTRIBUTES;
|
|
oparms.disposition = FILE_OPEN;
|
|
oparms.create_options = 0;
|
|
oparms.fid = &fid;
|
|
oparms.reconnect = false;
|
|
|
|
rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL);
|
|
if (rc)
|
|
return;
|
|
|
|
SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid,
|
|
FS_ATTRIBUTE_INFORMATION);
|
|
SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid,
|
|
FS_DEVICE_INFORMATION);
|
|
SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
|
|
return;
|
|
}
|
|
|
|
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;
|
|
__le16 *utf16_path;
|
|
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
|
|
struct cifs_open_parms oparms;
|
|
struct cifs_fid fid;
|
|
|
|
utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
|
|
if (!utf16_path)
|
|
return -ENOMEM;
|
|
|
|
oparms.tcon = tcon;
|
|
oparms.desired_access = FILE_READ_ATTRIBUTES;
|
|
oparms.disposition = FILE_OPEN;
|
|
oparms.create_options = 0;
|
|
oparms.fid = &fid;
|
|
oparms.reconnect = false;
|
|
|
|
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL);
|
|
if (rc) {
|
|
kfree(utf16_path);
|
|
return rc;
|
|
}
|
|
|
|
rc = SMB2_close(xid, tcon, fid.persistent_fid, 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 int
|
|
smb2_query_file_info(const unsigned int xid, struct cifs_tcon *tcon,
|
|
struct cifs_fid *fid, FILE_ALL_INFO *data)
|
|
{
|
|
int rc;
|
|
struct smb2_file_all_info *smb2_data;
|
|
|
|
smb2_data = kzalloc(sizeof(struct smb2_file_all_info) + MAX_NAME * 2,
|
|
GFP_KERNEL);
|
|
if (smb2_data == NULL)
|
|
return -ENOMEM;
|
|
|
|
rc = SMB2_query_info(xid, tcon, fid->persistent_fid, fid->volatile_fid,
|
|
smb2_data);
|
|
if (!rc)
|
|
move_smb2_info_to_cifs(data, smb2_data);
|
|
kfree(smb2_data);
|
|
return rc;
|
|
}
|
|
|
|
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_dump_share_caps(struct seq_file *m, struct cifs_tcon *tcon)
|
|
{
|
|
seq_puts(m, "\n\tShare Capabilities:");
|
|
if (tcon->capabilities & SMB2_SHARE_CAP_DFS)
|
|
seq_puts(m, " DFS,");
|
|
if (tcon->capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY)
|
|
seq_puts(m, " CONTINUOUS AVAILABILITY,");
|
|
if (tcon->capabilities & SMB2_SHARE_CAP_SCALEOUT)
|
|
seq_puts(m, " SCALEOUT,");
|
|
if (tcon->capabilities & SMB2_SHARE_CAP_CLUSTER)
|
|
seq_puts(m, " CLUSTER,");
|
|
if (tcon->capabilities & SMB2_SHARE_CAP_ASYMMETRIC)
|
|
seq_puts(m, " ASYMMETRIC,");
|
|
if (tcon->capabilities == 0)
|
|
seq_puts(m, " None");
|
|
if (tcon->ss_flags & SSINFO_FLAGS_ALIGNED_DEVICE)
|
|
seq_puts(m, " Aligned,");
|
|
if (tcon->ss_flags & SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE)
|
|
seq_puts(m, " Partition Aligned,");
|
|
if (tcon->ss_flags & SSINFO_FLAGS_NO_SEEK_PENALTY)
|
|
seq_puts(m, " SSD,");
|
|
if (tcon->ss_flags & SSINFO_FLAGS_TRIM_ENABLED)
|
|
seq_puts(m, " TRIM-support,");
|
|
|
|
seq_printf(m, "\tShare Flags: 0x%x", tcon->share_flags);
|
|
if (tcon->perf_sector_size)
|
|
seq_printf(m, "\tOptimal sector size: 0x%x",
|
|
tcon->perf_sector_size);
|
|
}
|
|
|
|
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]));
|
|
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
|
|
}
|
|
|
|
static void
|
|
smb2_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock)
|
|
{
|
|
struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
|
|
struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server;
|
|
|
|
cfile->fid.persistent_fid = fid->persistent_fid;
|
|
cfile->fid.volatile_fid = fid->volatile_fid;
|
|
server->ops->set_oplock_level(cinode, oplock, fid->epoch,
|
|
&fid->purge_cache);
|
|
cinode->can_cache_brlcks = CIFS_CACHE_WRITE(cinode);
|
|
}
|
|
|
|
static void
|
|
smb2_close_file(const unsigned int xid, struct cifs_tcon *tcon,
|
|
struct cifs_fid *fid)
|
|
{
|
|
SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid);
|
|
}
|
|
|
|
static int
|
|
smb2_flush_file(const unsigned int xid, struct cifs_tcon *tcon,
|
|
struct cifs_fid *fid)
|
|
{
|
|
return SMB2_flush(xid, tcon, fid->persistent_fid, fid->volatile_fid);
|
|
}
|
|
|
|
static unsigned int
|
|
smb2_read_data_offset(char *buf)
|
|
{
|
|
struct smb2_read_rsp *rsp = (struct smb2_read_rsp *)buf;
|
|
return rsp->DataOffset;
|
|
}
|
|
|
|
static unsigned int
|
|
smb2_read_data_length(char *buf)
|
|
{
|
|
struct smb2_read_rsp *rsp = (struct smb2_read_rsp *)buf;
|
|
return le32_to_cpu(rsp->DataLength);
|
|
}
|
|
|
|
|
|
static int
|
|
smb2_sync_read(const unsigned int xid, struct cifsFileInfo *cfile,
|
|
struct cifs_io_parms *parms, unsigned int *bytes_read,
|
|
char **buf, int *buf_type)
|
|
{
|
|
parms->persistent_fid = cfile->fid.persistent_fid;
|
|
parms->volatile_fid = cfile->fid.volatile_fid;
|
|
return SMB2_read(xid, parms, bytes_read, buf, buf_type);
|
|
}
|
|
|
|
static int
|
|
smb2_sync_write(const unsigned int xid, struct cifsFileInfo *cfile,
|
|
struct cifs_io_parms *parms, unsigned int *written,
|
|
struct kvec *iov, unsigned long nr_segs)
|
|
{
|
|
|
|
parms->persistent_fid = cfile->fid.persistent_fid;
|
|
parms->volatile_fid = cfile->fid.volatile_fid;
|
|
return SMB2_write(xid, parms, written, iov, nr_segs);
|
|
}
|
|
|
|
static int
|
|
smb2_set_file_size(const unsigned int xid, struct cifs_tcon *tcon,
|
|
struct cifsFileInfo *cfile, __u64 size, bool set_alloc)
|
|
{
|
|
__le64 eof = cpu_to_le64(size);
|
|
return SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
|
|
cfile->fid.volatile_fid, cfile->pid, &eof);
|
|
}
|
|
|
|
static int
|
|
smb2_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
|
|
struct cifsFileInfo *cfile)
|
|
{
|
|
return SMB2_set_compression(xid, tcon, cfile->fid.persistent_fid,
|
|
cfile->fid.volatile_fid);
|
|
}
|
|
|
|
static int
|
|
smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
|
|
const char *path, struct cifs_sb_info *cifs_sb,
|
|
struct cifs_fid *fid, __u16 search_flags,
|
|
struct cifs_search_info *srch_inf)
|
|
{
|
|
__le16 *utf16_path;
|
|
int rc;
|
|
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
|
|
struct cifs_open_parms oparms;
|
|
|
|
utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
|
|
if (!utf16_path)
|
|
return -ENOMEM;
|
|
|
|
oparms.tcon = tcon;
|
|
oparms.desired_access = FILE_READ_ATTRIBUTES | FILE_READ_DATA;
|
|
oparms.disposition = FILE_OPEN;
|
|
oparms.create_options = 0;
|
|
oparms.fid = fid;
|
|
oparms.reconnect = false;
|
|
|
|
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL);
|
|
kfree(utf16_path);
|
|
if (rc) {
|
|
cifs_dbg(VFS, "open dir failed\n");
|
|
return rc;
|
|
}
|
|
|
|
srch_inf->entries_in_buffer = 0;
|
|
srch_inf->index_of_last_entry = 0;
|
|
|
|
rc = SMB2_query_directory(xid, tcon, fid->persistent_fid,
|
|
fid->volatile_fid, 0, srch_inf);
|
|
if (rc) {
|
|
cifs_dbg(VFS, "query directory failed\n");
|
|
SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static int
|
|
smb2_query_dir_next(const unsigned int xid, struct cifs_tcon *tcon,
|
|
struct cifs_fid *fid, __u16 search_flags,
|
|
struct cifs_search_info *srch_inf)
|
|
{
|
|
return SMB2_query_directory(xid, tcon, fid->persistent_fid,
|
|
fid->volatile_fid, 0, srch_inf);
|
|
}
|
|
|
|
static int
|
|
smb2_close_dir(const unsigned int xid, struct cifs_tcon *tcon,
|
|
struct cifs_fid *fid)
|
|
{
|
|
return SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid);
|
|
}
|
|
|
|
/*
|
|
* If we negotiate SMB2 protocol and get STATUS_PENDING - update
|
|
* the number of credits and return true. Otherwise - return false.
|
|
*/
|
|
static bool
|
|
smb2_is_status_pending(char *buf, struct TCP_Server_Info *server, int length)
|
|
{
|
|
struct smb2_hdr *hdr = (struct smb2_hdr *)buf;
|
|
|
|
if (hdr->Status != STATUS_PENDING)
|
|
return false;
|
|
|
|
if (!length) {
|
|
spin_lock(&server->req_lock);
|
|
server->credits += le16_to_cpu(hdr->CreditRequest);
|
|
spin_unlock(&server->req_lock);
|
|
wake_up(&server->request_q);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static int
|
|
smb2_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid,
|
|
struct cifsInodeInfo *cinode)
|
|
{
|
|
if (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING)
|
|
return SMB2_lease_break(0, tcon, cinode->lease_key,
|
|
smb2_get_lease_state(cinode));
|
|
|
|
return SMB2_oplock_break(0, tcon, fid->persistent_fid,
|
|
fid->volatile_fid,
|
|
CIFS_CACHE_READ(cinode) ? 1 : 0);
|
|
}
|
|
|
|
static int
|
|
smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon,
|
|
struct kstatfs *buf)
|
|
{
|
|
int rc;
|
|
__le16 srch_path = 0; /* Null - open root of share */
|
|
u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
|
|
struct cifs_open_parms oparms;
|
|
struct cifs_fid fid;
|
|
|
|
oparms.tcon = tcon;
|
|
oparms.desired_access = FILE_READ_ATTRIBUTES;
|
|
oparms.disposition = FILE_OPEN;
|
|
oparms.create_options = 0;
|
|
oparms.fid = &fid;
|
|
oparms.reconnect = false;
|
|
|
|
rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL);
|
|
if (rc)
|
|
return rc;
|
|
buf->f_type = SMB2_MAGIC_NUMBER;
|
|
rc = SMB2_QFS_info(xid, tcon, fid.persistent_fid, fid.volatile_fid,
|
|
buf);
|
|
SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
|
|
return rc;
|
|
}
|
|
|
|
static bool
|
|
smb2_compare_fids(struct cifsFileInfo *ob1, struct cifsFileInfo *ob2)
|
|
{
|
|
return ob1->fid.persistent_fid == ob2->fid.persistent_fid &&
|
|
ob1->fid.volatile_fid == ob2->fid.volatile_fid;
|
|
}
|
|
|
|
static int
|
|
smb2_mand_lock(const unsigned int xid, struct cifsFileInfo *cfile, __u64 offset,
|
|
__u64 length, __u32 type, int lock, int unlock, bool wait)
|
|
{
|
|
if (unlock && !lock)
|
|
type = SMB2_LOCKFLAG_UNLOCK;
|
|
return SMB2_lock(xid, tlink_tcon(cfile->tlink),
|
|
cfile->fid.persistent_fid, cfile->fid.volatile_fid,
|
|
current->tgid, length, offset, type, wait);
|
|
}
|
|
|
|
static void
|
|
smb2_get_lease_key(struct inode *inode, struct cifs_fid *fid)
|
|
{
|
|
memcpy(fid->lease_key, CIFS_I(inode)->lease_key, SMB2_LEASE_KEY_SIZE);
|
|
}
|
|
|
|
static void
|
|
smb2_set_lease_key(struct inode *inode, struct cifs_fid *fid)
|
|
{
|
|
memcpy(CIFS_I(inode)->lease_key, fid->lease_key, SMB2_LEASE_KEY_SIZE);
|
|
}
|
|
|
|
static void
|
|
smb2_new_lease_key(struct cifs_fid *fid)
|
|
{
|
|
get_random_bytes(fid->lease_key, SMB2_LEASE_KEY_SIZE);
|
|
}
|
|
|
|
static int
|
|
smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
|
|
const char *full_path, char **target_path,
|
|
struct cifs_sb_info *cifs_sb)
|
|
{
|
|
int rc;
|
|
__le16 *utf16_path;
|
|
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
|
|
struct cifs_open_parms oparms;
|
|
struct cifs_fid fid;
|
|
struct smb2_err_rsp *err_buf = NULL;
|
|
struct smb2_symlink_err_rsp *symlink;
|
|
unsigned int sub_len, sub_offset;
|
|
|
|
cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path);
|
|
|
|
utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
|
|
if (!utf16_path)
|
|
return -ENOMEM;
|
|
|
|
oparms.tcon = tcon;
|
|
oparms.desired_access = FILE_READ_ATTRIBUTES;
|
|
oparms.disposition = FILE_OPEN;
|
|
oparms.create_options = 0;
|
|
oparms.fid = &fid;
|
|
oparms.reconnect = false;
|
|
|
|
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, &err_buf);
|
|
|
|
if (!rc || !err_buf) {
|
|
kfree(utf16_path);
|
|
return -ENOENT;
|
|
}
|
|
/* open must fail on symlink - reset rc */
|
|
rc = 0;
|
|
symlink = (struct smb2_symlink_err_rsp *)err_buf->ErrorData;
|
|
sub_len = le16_to_cpu(symlink->SubstituteNameLength);
|
|
sub_offset = le16_to_cpu(symlink->SubstituteNameOffset);
|
|
*target_path = cifs_strndup_from_utf16(
|
|
(char *)symlink->PathBuffer + sub_offset,
|
|
sub_len, true, cifs_sb->local_nls);
|
|
if (!(*target_path)) {
|
|
kfree(utf16_path);
|
|
return -ENOMEM;
|
|
}
|
|
convert_delimiter(*target_path, '/');
|
|
cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path);
|
|
kfree(utf16_path);
|
|
return rc;
|
|
}
|
|
|
|
static void
|
|
smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock,
|
|
unsigned int epoch, bool *purge_cache)
|
|
{
|
|
oplock &= 0xFF;
|
|
if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE)
|
|
return;
|
|
if (oplock == SMB2_OPLOCK_LEVEL_BATCH) {
|
|
cinode->oplock = CIFS_CACHE_RHW_FLG;
|
|
cifs_dbg(FYI, "Batch Oplock granted on inode %p\n",
|
|
&cinode->vfs_inode);
|
|
} else if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) {
|
|
cinode->oplock = CIFS_CACHE_RW_FLG;
|
|
cifs_dbg(FYI, "Exclusive Oplock granted on inode %p\n",
|
|
&cinode->vfs_inode);
|
|
} else if (oplock == SMB2_OPLOCK_LEVEL_II) {
|
|
cinode->oplock = CIFS_CACHE_READ_FLG;
|
|
cifs_dbg(FYI, "Level II Oplock granted on inode %p\n",
|
|
&cinode->vfs_inode);
|
|
} else
|
|
cinode->oplock = 0;
|
|
}
|
|
|
|
static void
|
|
smb21_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock,
|
|
unsigned int epoch, bool *purge_cache)
|
|
{
|
|
char message[5] = {0};
|
|
|
|
oplock &= 0xFF;
|
|
if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE)
|
|
return;
|
|
|
|
cinode->oplock = 0;
|
|
if (oplock & SMB2_LEASE_READ_CACHING_HE) {
|
|
cinode->oplock |= CIFS_CACHE_READ_FLG;
|
|
strcat(message, "R");
|
|
}
|
|
if (oplock & SMB2_LEASE_HANDLE_CACHING_HE) {
|
|
cinode->oplock |= CIFS_CACHE_HANDLE_FLG;
|
|
strcat(message, "H");
|
|
}
|
|
if (oplock & SMB2_LEASE_WRITE_CACHING_HE) {
|
|
cinode->oplock |= CIFS_CACHE_WRITE_FLG;
|
|
strcat(message, "W");
|
|
}
|
|
if (!cinode->oplock)
|
|
strcat(message, "None");
|
|
cifs_dbg(FYI, "%s Lease granted on inode %p\n", message,
|
|
&cinode->vfs_inode);
|
|
}
|
|
|
|
static void
|
|
smb3_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock,
|
|
unsigned int epoch, bool *purge_cache)
|
|
{
|
|
unsigned int old_oplock = cinode->oplock;
|
|
|
|
smb21_set_oplock_level(cinode, oplock, epoch, purge_cache);
|
|
|
|
if (purge_cache) {
|
|
*purge_cache = false;
|
|
if (old_oplock == CIFS_CACHE_READ_FLG) {
|
|
if (cinode->oplock == CIFS_CACHE_READ_FLG &&
|
|
(epoch - cinode->epoch > 0))
|
|
*purge_cache = true;
|
|
else if (cinode->oplock == CIFS_CACHE_RH_FLG &&
|
|
(epoch - cinode->epoch > 1))
|
|
*purge_cache = true;
|
|
else if (cinode->oplock == CIFS_CACHE_RHW_FLG &&
|
|
(epoch - cinode->epoch > 1))
|
|
*purge_cache = true;
|
|
else if (cinode->oplock == 0 &&
|
|
(epoch - cinode->epoch > 0))
|
|
*purge_cache = true;
|
|
} else if (old_oplock == CIFS_CACHE_RH_FLG) {
|
|
if (cinode->oplock == CIFS_CACHE_RH_FLG &&
|
|
(epoch - cinode->epoch > 0))
|
|
*purge_cache = true;
|
|
else if (cinode->oplock == CIFS_CACHE_RHW_FLG &&
|
|
(epoch - cinode->epoch > 1))
|
|
*purge_cache = true;
|
|
}
|
|
cinode->epoch = epoch;
|
|
}
|
|
}
|
|
|
|
static bool
|
|
smb2_is_read_op(__u32 oplock)
|
|
{
|
|
return oplock == SMB2_OPLOCK_LEVEL_II;
|
|
}
|
|
|
|
static bool
|
|
smb21_is_read_op(__u32 oplock)
|
|
{
|
|
return (oplock & SMB2_LEASE_READ_CACHING_HE) &&
|
|
!(oplock & SMB2_LEASE_WRITE_CACHING_HE);
|
|
}
|
|
|
|
static __le32
|
|
map_oplock_to_lease(u8 oplock)
|
|
{
|
|
if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE)
|
|
return SMB2_LEASE_WRITE_CACHING | SMB2_LEASE_READ_CACHING;
|
|
else if (oplock == SMB2_OPLOCK_LEVEL_II)
|
|
return SMB2_LEASE_READ_CACHING;
|
|
else if (oplock == SMB2_OPLOCK_LEVEL_BATCH)
|
|
return SMB2_LEASE_HANDLE_CACHING | SMB2_LEASE_READ_CACHING |
|
|
SMB2_LEASE_WRITE_CACHING;
|
|
return 0;
|
|
}
|
|
|
|
static char *
|
|
smb2_create_lease_buf(u8 *lease_key, u8 oplock)
|
|
{
|
|
struct create_lease *buf;
|
|
|
|
buf = kzalloc(sizeof(struct create_lease), GFP_KERNEL);
|
|
if (!buf)
|
|
return NULL;
|
|
|
|
buf->lcontext.LeaseKeyLow = cpu_to_le64(*((u64 *)lease_key));
|
|
buf->lcontext.LeaseKeyHigh = cpu_to_le64(*((u64 *)(lease_key + 8)));
|
|
buf->lcontext.LeaseState = map_oplock_to_lease(oplock);
|
|
|
|
buf->ccontext.DataOffset = cpu_to_le16(offsetof
|
|
(struct create_lease, lcontext));
|
|
buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context));
|
|
buf->ccontext.NameOffset = cpu_to_le16(offsetof
|
|
(struct create_lease, Name));
|
|
buf->ccontext.NameLength = cpu_to_le16(4);
|
|
buf->Name[0] = 'R';
|
|
buf->Name[1] = 'q';
|
|
buf->Name[2] = 'L';
|
|
buf->Name[3] = 's';
|
|
return (char *)buf;
|
|
}
|
|
|
|
static char *
|
|
smb3_create_lease_buf(u8 *lease_key, u8 oplock)
|
|
{
|
|
struct create_lease_v2 *buf;
|
|
|
|
buf = kzalloc(sizeof(struct create_lease_v2), GFP_KERNEL);
|
|
if (!buf)
|
|
return NULL;
|
|
|
|
buf->lcontext.LeaseKeyLow = cpu_to_le64(*((u64 *)lease_key));
|
|
buf->lcontext.LeaseKeyHigh = cpu_to_le64(*((u64 *)(lease_key + 8)));
|
|
buf->lcontext.LeaseState = map_oplock_to_lease(oplock);
|
|
|
|
buf->ccontext.DataOffset = cpu_to_le16(offsetof
|
|
(struct create_lease_v2, lcontext));
|
|
buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context_v2));
|
|
buf->ccontext.NameOffset = cpu_to_le16(offsetof
|
|
(struct create_lease_v2, Name));
|
|
buf->ccontext.NameLength = cpu_to_le16(4);
|
|
buf->Name[0] = 'R';
|
|
buf->Name[1] = 'q';
|
|
buf->Name[2] = 'L';
|
|
buf->Name[3] = 's';
|
|
return (char *)buf;
|
|
}
|
|
|
|
static __u8
|
|
smb2_parse_lease_buf(void *buf, unsigned int *epoch)
|
|
{
|
|
struct create_lease *lc = (struct create_lease *)buf;
|
|
|
|
*epoch = 0; /* not used */
|
|
if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS)
|
|
return SMB2_OPLOCK_LEVEL_NOCHANGE;
|
|
return le32_to_cpu(lc->lcontext.LeaseState);
|
|
}
|
|
|
|
static __u8
|
|
smb3_parse_lease_buf(void *buf, unsigned int *epoch)
|
|
{
|
|
struct create_lease_v2 *lc = (struct create_lease_v2 *)buf;
|
|
|
|
*epoch = le16_to_cpu(lc->lcontext.Epoch);
|
|
if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS)
|
|
return SMB2_OPLOCK_LEVEL_NOCHANGE;
|
|
return le32_to_cpu(lc->lcontext.LeaseState);
|
|
}
|
|
|
|
struct smb_version_operations smb20_operations = {
|
|
.compare_fids = smb2_compare_fids,
|
|
.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,
|
|
.read_data_offset = smb2_read_data_offset,
|
|
.read_data_length = smb2_read_data_length,
|
|
.map_error = map_smb2_to_linux_error,
|
|
.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,
|
|
.is_oplock_break = smb2_is_valid_oplock_break,
|
|
.need_neg = smb2_need_neg,
|
|
.negotiate = smb2_negotiate,
|
|
.negotiate_wsize = smb2_negotiate_wsize,
|
|
.negotiate_rsize = smb2_negotiate_rsize,
|
|
.sess_setup = SMB2_sess_setup,
|
|
.logoff = SMB2_logoff,
|
|
.tree_connect = SMB2_tcon,
|
|
.tree_disconnect = SMB2_tdis,
|
|
.qfs_tcon = smb2_qfs_tcon,
|
|
.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,
|
|
.query_file_info = smb2_query_file_info,
|
|
.set_path_size = smb2_set_path_size,
|
|
.set_file_size = smb2_set_file_size,
|
|
.set_file_info = smb2_set_file_info,
|
|
.set_compression = smb2_set_compression,
|
|
.mkdir = smb2_mkdir,
|
|
.mkdir_setinfo = smb2_mkdir_setinfo,
|
|
.rmdir = smb2_rmdir,
|
|
.unlink = smb2_unlink,
|
|
.rename = smb2_rename_path,
|
|
.create_hardlink = smb2_create_hardlink,
|
|
.query_symlink = smb2_query_symlink,
|
|
.open = smb2_open_file,
|
|
.set_fid = smb2_set_fid,
|
|
.close = smb2_close_file,
|
|
.flush = smb2_flush_file,
|
|
.async_readv = smb2_async_readv,
|
|
.async_writev = smb2_async_writev,
|
|
.sync_read = smb2_sync_read,
|
|
.sync_write = smb2_sync_write,
|
|
.query_dir_first = smb2_query_dir_first,
|
|
.query_dir_next = smb2_query_dir_next,
|
|
.close_dir = smb2_close_dir,
|
|
.calc_smb_size = smb2_calc_size,
|
|
.is_status_pending = smb2_is_status_pending,
|
|
.oplock_response = smb2_oplock_response,
|
|
.queryfs = smb2_queryfs,
|
|
.mand_lock = smb2_mand_lock,
|
|
.mand_unlock_range = smb2_unlock_range,
|
|
.push_mand_locks = smb2_push_mandatory_locks,
|
|
.get_lease_key = smb2_get_lease_key,
|
|
.set_lease_key = smb2_set_lease_key,
|
|
.new_lease_key = smb2_new_lease_key,
|
|
.calc_signature = smb2_calc_signature,
|
|
.is_read_op = smb2_is_read_op,
|
|
.set_oplock_level = smb2_set_oplock_level,
|
|
.create_lease_buf = smb2_create_lease_buf,
|
|
.parse_lease_buf = smb2_parse_lease_buf,
|
|
};
|
|
|
|
struct smb_version_operations smb21_operations = {
|
|
.compare_fids = smb2_compare_fids,
|
|
.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,
|
|
.read_data_offset = smb2_read_data_offset,
|
|
.read_data_length = smb2_read_data_length,
|
|
.map_error = map_smb2_to_linux_error,
|
|
.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,
|
|
.is_oplock_break = smb2_is_valid_oplock_break,
|
|
.need_neg = smb2_need_neg,
|
|
.negotiate = smb2_negotiate,
|
|
.negotiate_wsize = smb2_negotiate_wsize,
|
|
.negotiate_rsize = smb2_negotiate_rsize,
|
|
.sess_setup = SMB2_sess_setup,
|
|
.logoff = SMB2_logoff,
|
|
.tree_connect = SMB2_tcon,
|
|
.tree_disconnect = SMB2_tdis,
|
|
.qfs_tcon = smb2_qfs_tcon,
|
|
.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,
|
|
.query_file_info = smb2_query_file_info,
|
|
.set_path_size = smb2_set_path_size,
|
|
.set_file_size = smb2_set_file_size,
|
|
.set_file_info = smb2_set_file_info,
|
|
.set_compression = smb2_set_compression,
|
|
.mkdir = smb2_mkdir,
|
|
.mkdir_setinfo = smb2_mkdir_setinfo,
|
|
.rmdir = smb2_rmdir,
|
|
.unlink = smb2_unlink,
|
|
.rename = smb2_rename_path,
|
|
.create_hardlink = smb2_create_hardlink,
|
|
.query_symlink = smb2_query_symlink,
|
|
.open = smb2_open_file,
|
|
.set_fid = smb2_set_fid,
|
|
.close = smb2_close_file,
|
|
.flush = smb2_flush_file,
|
|
.async_readv = smb2_async_readv,
|
|
.async_writev = smb2_async_writev,
|
|
.sync_read = smb2_sync_read,
|
|
.sync_write = smb2_sync_write,
|
|
.query_dir_first = smb2_query_dir_first,
|
|
.query_dir_next = smb2_query_dir_next,
|
|
.close_dir = smb2_close_dir,
|
|
.calc_smb_size = smb2_calc_size,
|
|
.is_status_pending = smb2_is_status_pending,
|
|
.oplock_response = smb2_oplock_response,
|
|
.queryfs = smb2_queryfs,
|
|
.mand_lock = smb2_mand_lock,
|
|
.mand_unlock_range = smb2_unlock_range,
|
|
.push_mand_locks = smb2_push_mandatory_locks,
|
|
.get_lease_key = smb2_get_lease_key,
|
|
.set_lease_key = smb2_set_lease_key,
|
|
.new_lease_key = smb2_new_lease_key,
|
|
.calc_signature = smb2_calc_signature,
|
|
.is_read_op = smb21_is_read_op,
|
|
.set_oplock_level = smb21_set_oplock_level,
|
|
.create_lease_buf = smb2_create_lease_buf,
|
|
.parse_lease_buf = smb2_parse_lease_buf,
|
|
};
|
|
|
|
struct smb_version_operations smb30_operations = {
|
|
.compare_fids = smb2_compare_fids,
|
|
.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,
|
|
.read_data_offset = smb2_read_data_offset,
|
|
.read_data_length = smb2_read_data_length,
|
|
.map_error = map_smb2_to_linux_error,
|
|
.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,
|
|
.dump_share_caps = smb2_dump_share_caps,
|
|
.is_oplock_break = smb2_is_valid_oplock_break,
|
|
.need_neg = smb2_need_neg,
|
|
.negotiate = smb2_negotiate,
|
|
.negotiate_wsize = smb2_negotiate_wsize,
|
|
.negotiate_rsize = smb2_negotiate_rsize,
|
|
.sess_setup = SMB2_sess_setup,
|
|
.logoff = SMB2_logoff,
|
|
.tree_connect = SMB2_tcon,
|
|
.tree_disconnect = SMB2_tdis,
|
|
.qfs_tcon = smb3_qfs_tcon,
|
|
.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,
|
|
.query_file_info = smb2_query_file_info,
|
|
.set_path_size = smb2_set_path_size,
|
|
.set_file_size = smb2_set_file_size,
|
|
.set_file_info = smb2_set_file_info,
|
|
.set_compression = smb2_set_compression,
|
|
.mkdir = smb2_mkdir,
|
|
.mkdir_setinfo = smb2_mkdir_setinfo,
|
|
.rmdir = smb2_rmdir,
|
|
.unlink = smb2_unlink,
|
|
.rename = smb2_rename_path,
|
|
.create_hardlink = smb2_create_hardlink,
|
|
.query_symlink = smb2_query_symlink,
|
|
.open = smb2_open_file,
|
|
.set_fid = smb2_set_fid,
|
|
.close = smb2_close_file,
|
|
.flush = smb2_flush_file,
|
|
.async_readv = smb2_async_readv,
|
|
.async_writev = smb2_async_writev,
|
|
.sync_read = smb2_sync_read,
|
|
.sync_write = smb2_sync_write,
|
|
.query_dir_first = smb2_query_dir_first,
|
|
.query_dir_next = smb2_query_dir_next,
|
|
.close_dir = smb2_close_dir,
|
|
.calc_smb_size = smb2_calc_size,
|
|
.is_status_pending = smb2_is_status_pending,
|
|
.oplock_response = smb2_oplock_response,
|
|
.queryfs = smb2_queryfs,
|
|
.mand_lock = smb2_mand_lock,
|
|
.mand_unlock_range = smb2_unlock_range,
|
|
.push_mand_locks = smb2_push_mandatory_locks,
|
|
.get_lease_key = smb2_get_lease_key,
|
|
.set_lease_key = smb2_set_lease_key,
|
|
.new_lease_key = smb2_new_lease_key,
|
|
.generate_signingkey = generate_smb3signingkey,
|
|
.calc_signature = smb3_calc_signature,
|
|
.is_read_op = smb21_is_read_op,
|
|
.set_oplock_level = smb3_set_oplock_level,
|
|
.create_lease_buf = smb3_create_lease_buf,
|
|
.parse_lease_buf = smb3_parse_lease_buf,
|
|
};
|
|
|
|
struct smb_version_values smb20_values = {
|
|
.version_string = SMB20_VERSION_STRING,
|
|
.protocol_id = SMB20_PROT_ID,
|
|
.req_capabilities = 0, /* MBZ */
|
|
.large_lock_type = 0,
|
|
.exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK,
|
|
.shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
|
|
.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
|
|
.header_size = sizeof(struct smb2_hdr),
|
|
.max_header_size = MAX_SMB2_HDR_SIZE,
|
|
.read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
|
|
.lock_cmd = SMB2_LOCK,
|
|
.cap_unix = 0,
|
|
.cap_nt_find = SMB2_NT_FIND,
|
|
.cap_large_files = SMB2_LARGE_FILES,
|
|
.signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED,
|
|
.signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED,
|
|
.create_lease_size = sizeof(struct create_lease),
|
|
};
|
|
|
|
struct smb_version_values smb21_values = {
|
|
.version_string = SMB21_VERSION_STRING,
|
|
.protocol_id = SMB21_PROT_ID,
|
|
.req_capabilities = 0, /* MBZ on negotiate req until SMB3 dialect */
|
|
.large_lock_type = 0,
|
|
.exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK,
|
|
.shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
|
|
.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
|
|
.header_size = sizeof(struct smb2_hdr),
|
|
.max_header_size = MAX_SMB2_HDR_SIZE,
|
|
.read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
|
|
.lock_cmd = SMB2_LOCK,
|
|
.cap_unix = 0,
|
|
.cap_nt_find = SMB2_NT_FIND,
|
|
.cap_large_files = SMB2_LARGE_FILES,
|
|
.signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED,
|
|
.signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED,
|
|
.create_lease_size = sizeof(struct create_lease),
|
|
};
|
|
|
|
struct smb_version_values smb30_values = {
|
|
.version_string = SMB30_VERSION_STRING,
|
|
.protocol_id = SMB30_PROT_ID,
|
|
.req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU,
|
|
.large_lock_type = 0,
|
|
.exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK,
|
|
.shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
|
|
.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
|
|
.header_size = sizeof(struct smb2_hdr),
|
|
.max_header_size = MAX_SMB2_HDR_SIZE,
|
|
.read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
|
|
.lock_cmd = SMB2_LOCK,
|
|
.cap_unix = 0,
|
|
.cap_nt_find = SMB2_NT_FIND,
|
|
.cap_large_files = SMB2_LARGE_FILES,
|
|
.signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED,
|
|
.signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED,
|
|
.create_lease_size = sizeof(struct create_lease_v2),
|
|
};
|
|
|
|
struct smb_version_values smb302_values = {
|
|
.version_string = SMB302_VERSION_STRING,
|
|
.protocol_id = SMB302_PROT_ID,
|
|
.req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU,
|
|
.large_lock_type = 0,
|
|
.exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK,
|
|
.shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
|
|
.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
|
|
.header_size = sizeof(struct smb2_hdr),
|
|
.max_header_size = MAX_SMB2_HDR_SIZE,
|
|
.read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
|
|
.lock_cmd = SMB2_LOCK,
|
|
.cap_unix = 0,
|
|
.cap_nt_find = SMB2_NT_FIND,
|
|
.cap_large_files = SMB2_LARGE_FILES,
|
|
.signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED,
|
|
.signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED,
|
|
.create_lease_size = sizeof(struct create_lease_v2),
|
|
};
|