162306a36Sopenharmony_ci// SPDX-License-Identifier: LGPL-2.1 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) International Business Machines Corp., 2009, 2013 562306a36Sopenharmony_ci * Etersoft, 2012 662306a36Sopenharmony_ci * Author(s): Steve French (sfrench@us.ibm.com) 762306a36Sopenharmony_ci * Pavel Shilovsky (pshilovsky@samba.org) 2012 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Contains the routines for constructing the SMB2 PDUs themselves 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci /* SMB2 PDU handling routines here - except for leftovers (eg session setup) */ 1462306a36Sopenharmony_ci /* Note that there are handle based routines which must be */ 1562306a36Sopenharmony_ci /* treated slightly differently for reconnection purposes since we never */ 1662306a36Sopenharmony_ci /* want to reuse a stale file handle and only the caller knows the file info */ 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/fs.h> 1962306a36Sopenharmony_ci#include <linux/kernel.h> 2062306a36Sopenharmony_ci#include <linux/vfs.h> 2162306a36Sopenharmony_ci#include <linux/task_io_accounting_ops.h> 2262306a36Sopenharmony_ci#include <linux/uaccess.h> 2362306a36Sopenharmony_ci#include <linux/uuid.h> 2462306a36Sopenharmony_ci#include <linux/pagemap.h> 2562306a36Sopenharmony_ci#include <linux/xattr.h> 2662306a36Sopenharmony_ci#include "cifsglob.h" 2762306a36Sopenharmony_ci#include "cifsacl.h" 2862306a36Sopenharmony_ci#include "cifsproto.h" 2962306a36Sopenharmony_ci#include "smb2proto.h" 3062306a36Sopenharmony_ci#include "cifs_unicode.h" 3162306a36Sopenharmony_ci#include "cifs_debug.h" 3262306a36Sopenharmony_ci#include "ntlmssp.h" 3362306a36Sopenharmony_ci#include "smb2status.h" 3462306a36Sopenharmony_ci#include "smb2glob.h" 3562306a36Sopenharmony_ci#include "cifspdu.h" 3662306a36Sopenharmony_ci#include "cifs_spnego.h" 3762306a36Sopenharmony_ci#include "smbdirect.h" 3862306a36Sopenharmony_ci#include "trace.h" 3962306a36Sopenharmony_ci#ifdef CONFIG_CIFS_DFS_UPCALL 4062306a36Sopenharmony_ci#include "dfs_cache.h" 4162306a36Sopenharmony_ci#endif 4262306a36Sopenharmony_ci#include "cached_dir.h" 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* 4562306a36Sopenharmony_ci * The following table defines the expected "StructureSize" of SMB2 requests 4662306a36Sopenharmony_ci * in order by SMB2 command. This is similar to "wct" in SMB/CIFS requests. 4762306a36Sopenharmony_ci * 4862306a36Sopenharmony_ci * Note that commands are defined in smb2pdu.h in le16 but the array below is 4962306a36Sopenharmony_ci * indexed by command in host byte order. 5062306a36Sopenharmony_ci */ 5162306a36Sopenharmony_cistatic const int smb2_req_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = { 5262306a36Sopenharmony_ci /* SMB2_NEGOTIATE */ 36, 5362306a36Sopenharmony_ci /* SMB2_SESSION_SETUP */ 25, 5462306a36Sopenharmony_ci /* SMB2_LOGOFF */ 4, 5562306a36Sopenharmony_ci /* SMB2_TREE_CONNECT */ 9, 5662306a36Sopenharmony_ci /* SMB2_TREE_DISCONNECT */ 4, 5762306a36Sopenharmony_ci /* SMB2_CREATE */ 57, 5862306a36Sopenharmony_ci /* SMB2_CLOSE */ 24, 5962306a36Sopenharmony_ci /* SMB2_FLUSH */ 24, 6062306a36Sopenharmony_ci /* SMB2_READ */ 49, 6162306a36Sopenharmony_ci /* SMB2_WRITE */ 49, 6262306a36Sopenharmony_ci /* SMB2_LOCK */ 48, 6362306a36Sopenharmony_ci /* SMB2_IOCTL */ 57, 6462306a36Sopenharmony_ci /* SMB2_CANCEL */ 4, 6562306a36Sopenharmony_ci /* SMB2_ECHO */ 4, 6662306a36Sopenharmony_ci /* SMB2_QUERY_DIRECTORY */ 33, 6762306a36Sopenharmony_ci /* SMB2_CHANGE_NOTIFY */ 32, 6862306a36Sopenharmony_ci /* SMB2_QUERY_INFO */ 41, 6962306a36Sopenharmony_ci /* SMB2_SET_INFO */ 33, 7062306a36Sopenharmony_ci /* SMB2_OPLOCK_BREAK */ 24 /* BB this is 36 for LEASE_BREAK variant */ 7162306a36Sopenharmony_ci}; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ciint smb3_encryption_required(const struct cifs_tcon *tcon) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci if (!tcon || !tcon->ses) 7662306a36Sopenharmony_ci return 0; 7762306a36Sopenharmony_ci if ((tcon->ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) || 7862306a36Sopenharmony_ci (tcon->share_flags & SHI1005_FLAGS_ENCRYPT_DATA)) 7962306a36Sopenharmony_ci return 1; 8062306a36Sopenharmony_ci if (tcon->seal && 8162306a36Sopenharmony_ci (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) 8262306a36Sopenharmony_ci return 1; 8362306a36Sopenharmony_ci return 0; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic void 8762306a36Sopenharmony_cismb2_hdr_assemble(struct smb2_hdr *shdr, __le16 smb2_cmd, 8862306a36Sopenharmony_ci const struct cifs_tcon *tcon, 8962306a36Sopenharmony_ci struct TCP_Server_Info *server) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct smb3_hdr_req *smb3_hdr; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci shdr->ProtocolId = SMB2_PROTO_NUMBER; 9462306a36Sopenharmony_ci shdr->StructureSize = cpu_to_le16(64); 9562306a36Sopenharmony_ci shdr->Command = smb2_cmd; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (server) { 9862306a36Sopenharmony_ci /* After reconnect SMB3 must set ChannelSequence on subsequent reqs */ 9962306a36Sopenharmony_ci if (server->dialect >= SMB30_PROT_ID) { 10062306a36Sopenharmony_ci smb3_hdr = (struct smb3_hdr_req *)shdr; 10162306a36Sopenharmony_ci /* 10262306a36Sopenharmony_ci * if primary channel is not set yet, use default 10362306a36Sopenharmony_ci * channel for chan sequence num 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_ci if (SERVER_IS_CHAN(server)) 10662306a36Sopenharmony_ci smb3_hdr->ChannelSequence = 10762306a36Sopenharmony_ci cpu_to_le16(server->primary_server->channel_sequence_num); 10862306a36Sopenharmony_ci else 10962306a36Sopenharmony_ci smb3_hdr->ChannelSequence = 11062306a36Sopenharmony_ci cpu_to_le16(server->channel_sequence_num); 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci spin_lock(&server->req_lock); 11362306a36Sopenharmony_ci /* Request up to 10 credits but don't go over the limit. */ 11462306a36Sopenharmony_ci if (server->credits >= server->max_credits) 11562306a36Sopenharmony_ci shdr->CreditRequest = cpu_to_le16(0); 11662306a36Sopenharmony_ci else 11762306a36Sopenharmony_ci shdr->CreditRequest = cpu_to_le16( 11862306a36Sopenharmony_ci min_t(int, server->max_credits - 11962306a36Sopenharmony_ci server->credits, 10)); 12062306a36Sopenharmony_ci spin_unlock(&server->req_lock); 12162306a36Sopenharmony_ci } else { 12262306a36Sopenharmony_ci shdr->CreditRequest = cpu_to_le16(2); 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci shdr->Id.SyncId.ProcessId = cpu_to_le32((__u16)current->tgid); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (!tcon) 12762306a36Sopenharmony_ci goto out; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* GLOBAL_CAP_LARGE_MTU will only be set if dialect > SMB2.02 */ 13062306a36Sopenharmony_ci /* See sections 2.2.4 and 3.2.4.1.5 of MS-SMB2 */ 13162306a36Sopenharmony_ci if (server && (server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) 13262306a36Sopenharmony_ci shdr->CreditCharge = cpu_to_le16(1); 13362306a36Sopenharmony_ci /* else CreditCharge MBZ */ 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci shdr->Id.SyncId.TreeId = cpu_to_le32(tcon->tid); 13662306a36Sopenharmony_ci /* Uid is not converted */ 13762306a36Sopenharmony_ci if (tcon->ses) 13862306a36Sopenharmony_ci shdr->SessionId = cpu_to_le64(tcon->ses->Suid); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci /* 14162306a36Sopenharmony_ci * If we would set SMB2_FLAGS_DFS_OPERATIONS on open we also would have 14262306a36Sopenharmony_ci * to pass the path on the Open SMB prefixed by \\server\share. 14362306a36Sopenharmony_ci * Not sure when we would need to do the augmented path (if ever) and 14462306a36Sopenharmony_ci * setting this flag breaks the SMB2 open operation since it is 14562306a36Sopenharmony_ci * illegal to send an empty path name (without \\server\share prefix) 14662306a36Sopenharmony_ci * when the DFS flag is set in the SMB open header. We could 14762306a36Sopenharmony_ci * consider setting the flag on all operations other than open 14862306a36Sopenharmony_ci * but it is safer to net set it for now. 14962306a36Sopenharmony_ci */ 15062306a36Sopenharmony_ci/* if (tcon->share_flags & SHI1005_FLAGS_DFS) 15162306a36Sopenharmony_ci shdr->Flags |= SMB2_FLAGS_DFS_OPERATIONS; */ 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (server && server->sign && !smb3_encryption_required(tcon)) 15462306a36Sopenharmony_ci shdr->Flags |= SMB2_FLAGS_SIGNED; 15562306a36Sopenharmony_ciout: 15662306a36Sopenharmony_ci return; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/* helper function for code reuse */ 16062306a36Sopenharmony_cistatic int 16162306a36Sopenharmony_cicifs_chan_skip_or_disable(struct cifs_ses *ses, 16262306a36Sopenharmony_ci struct TCP_Server_Info *server, 16362306a36Sopenharmony_ci bool from_reconnect) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci struct TCP_Server_Info *pserver; 16662306a36Sopenharmony_ci unsigned int chan_index; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (SERVER_IS_CHAN(server)) { 16962306a36Sopenharmony_ci cifs_dbg(VFS, 17062306a36Sopenharmony_ci "server %s does not support multichannel anymore. Skip secondary channel\n", 17162306a36Sopenharmony_ci ses->server->hostname); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci spin_lock(&ses->chan_lock); 17462306a36Sopenharmony_ci chan_index = cifs_ses_get_chan_index(ses, server); 17562306a36Sopenharmony_ci if (chan_index == CIFS_INVAL_CHAN_INDEX) { 17662306a36Sopenharmony_ci spin_unlock(&ses->chan_lock); 17762306a36Sopenharmony_ci goto skip_terminate; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci ses->chans[chan_index].server = NULL; 18162306a36Sopenharmony_ci server->terminate = true; 18262306a36Sopenharmony_ci spin_unlock(&ses->chan_lock); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci /* 18562306a36Sopenharmony_ci * the above reference of server by channel 18662306a36Sopenharmony_ci * needs to be dropped without holding chan_lock 18762306a36Sopenharmony_ci * as cifs_put_tcp_session takes a higher lock 18862306a36Sopenharmony_ci * i.e. cifs_tcp_ses_lock 18962306a36Sopenharmony_ci */ 19062306a36Sopenharmony_ci cifs_put_tcp_session(server, from_reconnect); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci cifs_signal_cifsd_for_reconnect(server, false); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* mark primary server as needing reconnect */ 19562306a36Sopenharmony_ci pserver = server->primary_server; 19662306a36Sopenharmony_ci cifs_signal_cifsd_for_reconnect(pserver, false); 19762306a36Sopenharmony_ciskip_terminate: 19862306a36Sopenharmony_ci return -EHOSTDOWN; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci cifs_server_dbg(VFS, 20262306a36Sopenharmony_ci "server does not support multichannel anymore. Disable all other channels\n"); 20362306a36Sopenharmony_ci cifs_disable_secondary_channels(ses); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci return 0; 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic int 21062306a36Sopenharmony_cismb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, 21162306a36Sopenharmony_ci struct TCP_Server_Info *server, bool from_reconnect) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci int rc = 0; 21462306a36Sopenharmony_ci struct nls_table *nls_codepage = NULL; 21562306a36Sopenharmony_ci struct cifs_ses *ses; 21662306a36Sopenharmony_ci int xid; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* 21962306a36Sopenharmony_ci * SMB2s NegProt, SessSetup, Logoff do not have tcon yet so 22062306a36Sopenharmony_ci * check for tcp and smb session status done differently 22162306a36Sopenharmony_ci * for those three - in the calling routine. 22262306a36Sopenharmony_ci */ 22362306a36Sopenharmony_ci if (tcon == NULL) 22462306a36Sopenharmony_ci return 0; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci /* 22762306a36Sopenharmony_ci * Need to also skip SMB2_IOCTL because it is used for checking nested dfs links in 22862306a36Sopenharmony_ci * cifs_tree_connect(). 22962306a36Sopenharmony_ci */ 23062306a36Sopenharmony_ci if (smb2_command == SMB2_TREE_CONNECT || smb2_command == SMB2_IOCTL) 23162306a36Sopenharmony_ci return 0; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci spin_lock(&tcon->tc_lock); 23462306a36Sopenharmony_ci if (tcon->status == TID_EXITING) { 23562306a36Sopenharmony_ci /* 23662306a36Sopenharmony_ci * only tree disconnect allowed when disconnecting ... 23762306a36Sopenharmony_ci */ 23862306a36Sopenharmony_ci if (smb2_command != SMB2_TREE_DISCONNECT) { 23962306a36Sopenharmony_ci spin_unlock(&tcon->tc_lock); 24062306a36Sopenharmony_ci cifs_dbg(FYI, "can not send cmd %d while umounting\n", 24162306a36Sopenharmony_ci smb2_command); 24262306a36Sopenharmony_ci return -ENODEV; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci spin_unlock(&tcon->tc_lock); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci ses = tcon->ses; 24862306a36Sopenharmony_ci if (!ses) 24962306a36Sopenharmony_ci return -EIO; 25062306a36Sopenharmony_ci spin_lock(&ses->ses_lock); 25162306a36Sopenharmony_ci if (ses->ses_status == SES_EXITING) { 25262306a36Sopenharmony_ci spin_unlock(&ses->ses_lock); 25362306a36Sopenharmony_ci return -EIO; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci spin_unlock(&ses->ses_lock); 25662306a36Sopenharmony_ci if (!ses->server || !server) 25762306a36Sopenharmony_ci return -EIO; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci spin_lock(&server->srv_lock); 26062306a36Sopenharmony_ci if (server->tcpStatus == CifsNeedReconnect) { 26162306a36Sopenharmony_ci /* 26262306a36Sopenharmony_ci * Return to caller for TREE_DISCONNECT and LOGOFF and CLOSE 26362306a36Sopenharmony_ci * here since they are implicitly done when session drops. 26462306a36Sopenharmony_ci */ 26562306a36Sopenharmony_ci switch (smb2_command) { 26662306a36Sopenharmony_ci /* 26762306a36Sopenharmony_ci * BB Should we keep oplock break and add flush to exceptions? 26862306a36Sopenharmony_ci */ 26962306a36Sopenharmony_ci case SMB2_TREE_DISCONNECT: 27062306a36Sopenharmony_ci case SMB2_CANCEL: 27162306a36Sopenharmony_ci case SMB2_CLOSE: 27262306a36Sopenharmony_ci case SMB2_OPLOCK_BREAK: 27362306a36Sopenharmony_ci spin_unlock(&server->srv_lock); 27462306a36Sopenharmony_ci return -EAGAIN; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* if server is marked for termination, cifsd will cleanup */ 27962306a36Sopenharmony_ci if (server->terminate) { 28062306a36Sopenharmony_ci spin_unlock(&server->srv_lock); 28162306a36Sopenharmony_ci return -EHOSTDOWN; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci spin_unlock(&server->srv_lock); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ciagain: 28662306a36Sopenharmony_ci rc = cifs_wait_for_server_reconnect(server, tcon->retry); 28762306a36Sopenharmony_ci if (rc) 28862306a36Sopenharmony_ci return rc; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci spin_lock(&ses->chan_lock); 29162306a36Sopenharmony_ci if (!cifs_chan_needs_reconnect(ses, server) && !tcon->need_reconnect) { 29262306a36Sopenharmony_ci spin_unlock(&ses->chan_lock); 29362306a36Sopenharmony_ci return 0; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci spin_unlock(&ses->chan_lock); 29662306a36Sopenharmony_ci cifs_dbg(FYI, "sess reconnect mask: 0x%lx, tcon reconnect: %d", 29762306a36Sopenharmony_ci tcon->ses->chans_need_reconnect, 29862306a36Sopenharmony_ci tcon->need_reconnect); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci mutex_lock(&ses->session_mutex); 30162306a36Sopenharmony_ci /* 30262306a36Sopenharmony_ci * if this is called by delayed work, and the channel has been disabled 30362306a36Sopenharmony_ci * in parallel, the delayed work can continue to execute in parallel 30462306a36Sopenharmony_ci * there's a chance that this channel may not exist anymore 30562306a36Sopenharmony_ci */ 30662306a36Sopenharmony_ci spin_lock(&server->srv_lock); 30762306a36Sopenharmony_ci if (server->tcpStatus == CifsExiting) { 30862306a36Sopenharmony_ci spin_unlock(&server->srv_lock); 30962306a36Sopenharmony_ci mutex_unlock(&ses->session_mutex); 31062306a36Sopenharmony_ci rc = -EHOSTDOWN; 31162306a36Sopenharmony_ci goto out; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci /* 31562306a36Sopenharmony_ci * Recheck after acquire mutex. If another thread is negotiating 31662306a36Sopenharmony_ci * and the server never sends an answer the socket will be closed 31762306a36Sopenharmony_ci * and tcpStatus set to reconnect. 31862306a36Sopenharmony_ci */ 31962306a36Sopenharmony_ci if (server->tcpStatus == CifsNeedReconnect) { 32062306a36Sopenharmony_ci spin_unlock(&server->srv_lock); 32162306a36Sopenharmony_ci mutex_unlock(&ses->session_mutex); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (tcon->retry) 32462306a36Sopenharmony_ci goto again; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci rc = -EHOSTDOWN; 32762306a36Sopenharmony_ci goto out; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci spin_unlock(&server->srv_lock); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci nls_codepage = ses->local_nls; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci /* 33462306a36Sopenharmony_ci * need to prevent multiple threads trying to simultaneously 33562306a36Sopenharmony_ci * reconnect the same SMB session 33662306a36Sopenharmony_ci */ 33762306a36Sopenharmony_ci spin_lock(&ses->ses_lock); 33862306a36Sopenharmony_ci spin_lock(&ses->chan_lock); 33962306a36Sopenharmony_ci if (!cifs_chan_needs_reconnect(ses, server) && 34062306a36Sopenharmony_ci ses->ses_status == SES_GOOD) { 34162306a36Sopenharmony_ci spin_unlock(&ses->chan_lock); 34262306a36Sopenharmony_ci spin_unlock(&ses->ses_lock); 34362306a36Sopenharmony_ci /* this means that we only need to tree connect */ 34462306a36Sopenharmony_ci if (tcon->need_reconnect) 34562306a36Sopenharmony_ci goto skip_sess_setup; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci mutex_unlock(&ses->session_mutex); 34862306a36Sopenharmony_ci goto out; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci spin_unlock(&ses->chan_lock); 35162306a36Sopenharmony_ci spin_unlock(&ses->ses_lock); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci rc = cifs_negotiate_protocol(0, ses, server); 35462306a36Sopenharmony_ci if (!rc) { 35562306a36Sopenharmony_ci /* 35662306a36Sopenharmony_ci * if server stopped supporting multichannel 35762306a36Sopenharmony_ci * and the first channel reconnected, disable all the others. 35862306a36Sopenharmony_ci */ 35962306a36Sopenharmony_ci if (ses->chan_count > 1 && 36062306a36Sopenharmony_ci !(server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) { 36162306a36Sopenharmony_ci rc = cifs_chan_skip_or_disable(ses, server, 36262306a36Sopenharmony_ci from_reconnect); 36362306a36Sopenharmony_ci if (rc) { 36462306a36Sopenharmony_ci mutex_unlock(&ses->session_mutex); 36562306a36Sopenharmony_ci goto out; 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci rc = cifs_setup_session(0, ses, server, nls_codepage); 37062306a36Sopenharmony_ci if ((rc == -EACCES) && !tcon->retry) { 37162306a36Sopenharmony_ci mutex_unlock(&ses->session_mutex); 37262306a36Sopenharmony_ci rc = -EHOSTDOWN; 37362306a36Sopenharmony_ci goto failed; 37462306a36Sopenharmony_ci } else if (rc) { 37562306a36Sopenharmony_ci mutex_unlock(&ses->session_mutex); 37662306a36Sopenharmony_ci goto out; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci } else { 37962306a36Sopenharmony_ci mutex_unlock(&ses->session_mutex); 38062306a36Sopenharmony_ci goto out; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ciskip_sess_setup: 38462306a36Sopenharmony_ci if (!tcon->need_reconnect) { 38562306a36Sopenharmony_ci mutex_unlock(&ses->session_mutex); 38662306a36Sopenharmony_ci goto out; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci cifs_mark_open_files_invalid(tcon); 38962306a36Sopenharmony_ci if (tcon->use_persistent) 39062306a36Sopenharmony_ci tcon->need_reopen_files = true; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci rc = cifs_tree_connect(0, tcon, nls_codepage); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc); 39562306a36Sopenharmony_ci if (rc) { 39662306a36Sopenharmony_ci /* If sess reconnected but tcon didn't, something strange ... */ 39762306a36Sopenharmony_ci mutex_unlock(&ses->session_mutex); 39862306a36Sopenharmony_ci cifs_dbg(VFS, "reconnect tcon failed rc = %d\n", rc); 39962306a36Sopenharmony_ci goto out; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci spin_lock(&ses->ses_lock); 40362306a36Sopenharmony_ci if (ses->flags & CIFS_SES_FLAG_SCALE_CHANNELS) { 40462306a36Sopenharmony_ci spin_unlock(&ses->ses_lock); 40562306a36Sopenharmony_ci mutex_unlock(&ses->session_mutex); 40662306a36Sopenharmony_ci goto skip_add_channels; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci ses->flags |= CIFS_SES_FLAG_SCALE_CHANNELS; 40962306a36Sopenharmony_ci spin_unlock(&ses->ses_lock); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (!rc && 41262306a36Sopenharmony_ci (server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) { 41362306a36Sopenharmony_ci mutex_unlock(&ses->session_mutex); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci /* 41662306a36Sopenharmony_ci * query server network interfaces, in case they change 41762306a36Sopenharmony_ci */ 41862306a36Sopenharmony_ci xid = get_xid(); 41962306a36Sopenharmony_ci rc = SMB3_request_interfaces(xid, tcon, false); 42062306a36Sopenharmony_ci free_xid(xid); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci if (rc == -EOPNOTSUPP && ses->chan_count > 1) { 42362306a36Sopenharmony_ci /* 42462306a36Sopenharmony_ci * some servers like Azure SMB server do not advertise 42562306a36Sopenharmony_ci * that multichannel has been disabled with server 42662306a36Sopenharmony_ci * capabilities, rather return STATUS_NOT_IMPLEMENTED. 42762306a36Sopenharmony_ci * treat this as server not supporting multichannel 42862306a36Sopenharmony_ci */ 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci rc = cifs_chan_skip_or_disable(ses, server, 43162306a36Sopenharmony_ci from_reconnect); 43262306a36Sopenharmony_ci goto skip_add_channels; 43362306a36Sopenharmony_ci } else if (rc) 43462306a36Sopenharmony_ci cifs_dbg(FYI, "%s: failed to query server interfaces: %d\n", 43562306a36Sopenharmony_ci __func__, rc); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci if (ses->chan_max > ses->chan_count && 43862306a36Sopenharmony_ci ses->iface_count && 43962306a36Sopenharmony_ci !SERVER_IS_CHAN(server)) { 44062306a36Sopenharmony_ci if (ses->chan_count == 1) { 44162306a36Sopenharmony_ci cifs_server_dbg(VFS, "supports multichannel now\n"); 44262306a36Sopenharmony_ci queue_delayed_work(cifsiod_wq, &tcon->query_interfaces, 44362306a36Sopenharmony_ci (SMB_INTERFACE_POLL_INTERVAL * HZ)); 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci cifs_try_adding_channels(ses); 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci } else { 44962306a36Sopenharmony_ci mutex_unlock(&ses->session_mutex); 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ciskip_add_channels: 45362306a36Sopenharmony_ci spin_lock(&ses->ses_lock); 45462306a36Sopenharmony_ci ses->flags &= ~CIFS_SES_FLAG_SCALE_CHANNELS; 45562306a36Sopenharmony_ci spin_unlock(&ses->ses_lock); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci if (smb2_command != SMB2_INTERNAL_CMD) 45862306a36Sopenharmony_ci mod_delayed_work(cifsiod_wq, &server->reconnect, 0); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci atomic_inc(&tconInfoReconnectCount); 46162306a36Sopenharmony_ciout: 46262306a36Sopenharmony_ci /* 46362306a36Sopenharmony_ci * Check if handle based operation so we know whether we can continue 46462306a36Sopenharmony_ci * or not without returning to caller to reset file handle. 46562306a36Sopenharmony_ci */ 46662306a36Sopenharmony_ci /* 46762306a36Sopenharmony_ci * BB Is flush done by server on drop of tcp session? Should we special 46862306a36Sopenharmony_ci * case it and skip above? 46962306a36Sopenharmony_ci */ 47062306a36Sopenharmony_ci switch (smb2_command) { 47162306a36Sopenharmony_ci case SMB2_FLUSH: 47262306a36Sopenharmony_ci case SMB2_READ: 47362306a36Sopenharmony_ci case SMB2_WRITE: 47462306a36Sopenharmony_ci case SMB2_LOCK: 47562306a36Sopenharmony_ci case SMB2_QUERY_DIRECTORY: 47662306a36Sopenharmony_ci case SMB2_CHANGE_NOTIFY: 47762306a36Sopenharmony_ci case SMB2_QUERY_INFO: 47862306a36Sopenharmony_ci case SMB2_SET_INFO: 47962306a36Sopenharmony_ci rc = -EAGAIN; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_cifailed: 48262306a36Sopenharmony_ci return rc; 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic void 48662306a36Sopenharmony_cifill_small_buf(__le16 smb2_command, struct cifs_tcon *tcon, 48762306a36Sopenharmony_ci struct TCP_Server_Info *server, 48862306a36Sopenharmony_ci void *buf, 48962306a36Sopenharmony_ci unsigned int *total_len) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci struct smb2_pdu *spdu = buf; 49262306a36Sopenharmony_ci /* lookup word count ie StructureSize from table */ 49362306a36Sopenharmony_ci __u16 parmsize = smb2_req_struct_sizes[le16_to_cpu(smb2_command)]; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci /* 49662306a36Sopenharmony_ci * smaller than SMALL_BUFFER_SIZE but bigger than fixed area of 49762306a36Sopenharmony_ci * largest operations (Create) 49862306a36Sopenharmony_ci */ 49962306a36Sopenharmony_ci memset(buf, 0, 256); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci smb2_hdr_assemble(&spdu->hdr, smb2_command, tcon, server); 50262306a36Sopenharmony_ci spdu->StructureSize2 = cpu_to_le16(parmsize); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci *total_len = parmsize + sizeof(struct smb2_hdr); 50562306a36Sopenharmony_ci} 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci/* 50862306a36Sopenharmony_ci * Allocate and return pointer to an SMB request hdr, and set basic 50962306a36Sopenharmony_ci * SMB information in the SMB header. If the return code is zero, this 51062306a36Sopenharmony_ci * function must have filled in request_buf pointer. 51162306a36Sopenharmony_ci */ 51262306a36Sopenharmony_cistatic int __smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon, 51362306a36Sopenharmony_ci struct TCP_Server_Info *server, 51462306a36Sopenharmony_ci void **request_buf, unsigned int *total_len) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci /* BB eventually switch this to SMB2 specific small buf size */ 51762306a36Sopenharmony_ci switch (smb2_command) { 51862306a36Sopenharmony_ci case SMB2_SET_INFO: 51962306a36Sopenharmony_ci case SMB2_QUERY_INFO: 52062306a36Sopenharmony_ci *request_buf = cifs_buf_get(); 52162306a36Sopenharmony_ci break; 52262306a36Sopenharmony_ci default: 52362306a36Sopenharmony_ci *request_buf = cifs_small_buf_get(); 52462306a36Sopenharmony_ci break; 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci if (*request_buf == NULL) { 52762306a36Sopenharmony_ci /* BB should we add a retry in here if not a writepage? */ 52862306a36Sopenharmony_ci return -ENOMEM; 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci fill_small_buf(smb2_command, tcon, server, 53262306a36Sopenharmony_ci (struct smb2_hdr *)(*request_buf), 53362306a36Sopenharmony_ci total_len); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci if (tcon != NULL) { 53662306a36Sopenharmony_ci uint16_t com_code = le16_to_cpu(smb2_command); 53762306a36Sopenharmony_ci cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_sent[com_code]); 53862306a36Sopenharmony_ci cifs_stats_inc(&tcon->num_smbs_sent); 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci return 0; 54262306a36Sopenharmony_ci} 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_cistatic int smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon, 54562306a36Sopenharmony_ci struct TCP_Server_Info *server, 54662306a36Sopenharmony_ci void **request_buf, unsigned int *total_len) 54762306a36Sopenharmony_ci{ 54862306a36Sopenharmony_ci int rc; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci rc = smb2_reconnect(smb2_command, tcon, server, false); 55162306a36Sopenharmony_ci if (rc) 55262306a36Sopenharmony_ci return rc; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci return __smb2_plain_req_init(smb2_command, tcon, server, request_buf, 55562306a36Sopenharmony_ci total_len); 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_cistatic int smb2_ioctl_req_init(u32 opcode, struct cifs_tcon *tcon, 55962306a36Sopenharmony_ci struct TCP_Server_Info *server, 56062306a36Sopenharmony_ci void **request_buf, unsigned int *total_len) 56162306a36Sopenharmony_ci{ 56262306a36Sopenharmony_ci /* Skip reconnect only for FSCTL_VALIDATE_NEGOTIATE_INFO IOCTLs */ 56362306a36Sopenharmony_ci if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO) { 56462306a36Sopenharmony_ci return __smb2_plain_req_init(SMB2_IOCTL, tcon, server, 56562306a36Sopenharmony_ci request_buf, total_len); 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci return smb2_plain_req_init(SMB2_IOCTL, tcon, server, 56862306a36Sopenharmony_ci request_buf, total_len); 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci/* For explanation of negotiate contexts see MS-SMB2 section 2.2.3.1 */ 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_cistatic void 57462306a36Sopenharmony_cibuild_preauth_ctxt(struct smb2_preauth_neg_context *pneg_ctxt) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci pneg_ctxt->ContextType = SMB2_PREAUTH_INTEGRITY_CAPABILITIES; 57762306a36Sopenharmony_ci pneg_ctxt->DataLength = cpu_to_le16(38); 57862306a36Sopenharmony_ci pneg_ctxt->HashAlgorithmCount = cpu_to_le16(1); 57962306a36Sopenharmony_ci pneg_ctxt->SaltLength = cpu_to_le16(SMB311_SALT_SIZE); 58062306a36Sopenharmony_ci get_random_bytes(pneg_ctxt->Salt, SMB311_SALT_SIZE); 58162306a36Sopenharmony_ci pneg_ctxt->HashAlgorithms = SMB2_PREAUTH_INTEGRITY_SHA512; 58262306a36Sopenharmony_ci} 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_cistatic void 58562306a36Sopenharmony_cibuild_compression_ctxt(struct smb2_compression_capabilities_context *pneg_ctxt) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci pneg_ctxt->ContextType = SMB2_COMPRESSION_CAPABILITIES; 58862306a36Sopenharmony_ci pneg_ctxt->DataLength = 58962306a36Sopenharmony_ci cpu_to_le16(sizeof(struct smb2_compression_capabilities_context) 59062306a36Sopenharmony_ci - sizeof(struct smb2_neg_context)); 59162306a36Sopenharmony_ci pneg_ctxt->CompressionAlgorithmCount = cpu_to_le16(3); 59262306a36Sopenharmony_ci pneg_ctxt->CompressionAlgorithms[0] = SMB3_COMPRESS_LZ77; 59362306a36Sopenharmony_ci pneg_ctxt->CompressionAlgorithms[1] = SMB3_COMPRESS_LZ77_HUFF; 59462306a36Sopenharmony_ci pneg_ctxt->CompressionAlgorithms[2] = SMB3_COMPRESS_LZNT1; 59562306a36Sopenharmony_ci} 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_cistatic unsigned int 59862306a36Sopenharmony_cibuild_signing_ctxt(struct smb2_signing_capabilities *pneg_ctxt) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci unsigned int ctxt_len = sizeof(struct smb2_signing_capabilities); 60162306a36Sopenharmony_ci unsigned short num_algs = 1; /* number of signing algorithms sent */ 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci pneg_ctxt->ContextType = SMB2_SIGNING_CAPABILITIES; 60462306a36Sopenharmony_ci /* 60562306a36Sopenharmony_ci * Context Data length must be rounded to multiple of 8 for some servers 60662306a36Sopenharmony_ci */ 60762306a36Sopenharmony_ci pneg_ctxt->DataLength = cpu_to_le16(ALIGN(sizeof(struct smb2_signing_capabilities) - 60862306a36Sopenharmony_ci sizeof(struct smb2_neg_context) + 60962306a36Sopenharmony_ci (num_algs * sizeof(u16)), 8)); 61062306a36Sopenharmony_ci pneg_ctxt->SigningAlgorithmCount = cpu_to_le16(num_algs); 61162306a36Sopenharmony_ci pneg_ctxt->SigningAlgorithms[0] = cpu_to_le16(SIGNING_ALG_AES_CMAC); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci ctxt_len += sizeof(__le16) * num_algs; 61462306a36Sopenharmony_ci ctxt_len = ALIGN(ctxt_len, 8); 61562306a36Sopenharmony_ci return ctxt_len; 61662306a36Sopenharmony_ci /* TBD add SIGNING_ALG_AES_GMAC and/or SIGNING_ALG_HMAC_SHA256 */ 61762306a36Sopenharmony_ci} 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_cistatic void 62062306a36Sopenharmony_cibuild_encrypt_ctxt(struct smb2_encryption_neg_context *pneg_ctxt) 62162306a36Sopenharmony_ci{ 62262306a36Sopenharmony_ci pneg_ctxt->ContextType = SMB2_ENCRYPTION_CAPABILITIES; 62362306a36Sopenharmony_ci if (require_gcm_256) { 62462306a36Sopenharmony_ci pneg_ctxt->DataLength = cpu_to_le16(4); /* Cipher Count + 1 cipher */ 62562306a36Sopenharmony_ci pneg_ctxt->CipherCount = cpu_to_le16(1); 62662306a36Sopenharmony_ci pneg_ctxt->Ciphers[0] = SMB2_ENCRYPTION_AES256_GCM; 62762306a36Sopenharmony_ci } else if (enable_gcm_256) { 62862306a36Sopenharmony_ci pneg_ctxt->DataLength = cpu_to_le16(8); /* Cipher Count + 3 ciphers */ 62962306a36Sopenharmony_ci pneg_ctxt->CipherCount = cpu_to_le16(3); 63062306a36Sopenharmony_ci pneg_ctxt->Ciphers[0] = SMB2_ENCRYPTION_AES128_GCM; 63162306a36Sopenharmony_ci pneg_ctxt->Ciphers[1] = SMB2_ENCRYPTION_AES256_GCM; 63262306a36Sopenharmony_ci pneg_ctxt->Ciphers[2] = SMB2_ENCRYPTION_AES128_CCM; 63362306a36Sopenharmony_ci } else { 63462306a36Sopenharmony_ci pneg_ctxt->DataLength = cpu_to_le16(6); /* Cipher Count + 2 ciphers */ 63562306a36Sopenharmony_ci pneg_ctxt->CipherCount = cpu_to_le16(2); 63662306a36Sopenharmony_ci pneg_ctxt->Ciphers[0] = SMB2_ENCRYPTION_AES128_GCM; 63762306a36Sopenharmony_ci pneg_ctxt->Ciphers[1] = SMB2_ENCRYPTION_AES128_CCM; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_cistatic unsigned int 64262306a36Sopenharmony_cibuild_netname_ctxt(struct smb2_netname_neg_context *pneg_ctxt, char *hostname) 64362306a36Sopenharmony_ci{ 64462306a36Sopenharmony_ci struct nls_table *cp = load_nls_default(); 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci pneg_ctxt->ContextType = SMB2_NETNAME_NEGOTIATE_CONTEXT_ID; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci /* copy up to max of first 100 bytes of server name to NetName field */ 64962306a36Sopenharmony_ci pneg_ctxt->DataLength = cpu_to_le16(2 * cifs_strtoUTF16(pneg_ctxt->NetName, hostname, 100, cp)); 65062306a36Sopenharmony_ci /* context size is DataLength + minimal smb2_neg_context */ 65162306a36Sopenharmony_ci return ALIGN(le16_to_cpu(pneg_ctxt->DataLength) + sizeof(struct smb2_neg_context), 8); 65262306a36Sopenharmony_ci} 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_cistatic void 65562306a36Sopenharmony_cibuild_posix_ctxt(struct smb2_posix_neg_context *pneg_ctxt) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci pneg_ctxt->ContextType = SMB2_POSIX_EXTENSIONS_AVAILABLE; 65862306a36Sopenharmony_ci pneg_ctxt->DataLength = cpu_to_le16(POSIX_CTXT_DATA_LEN); 65962306a36Sopenharmony_ci /* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */ 66062306a36Sopenharmony_ci pneg_ctxt->Name[0] = 0x93; 66162306a36Sopenharmony_ci pneg_ctxt->Name[1] = 0xAD; 66262306a36Sopenharmony_ci pneg_ctxt->Name[2] = 0x25; 66362306a36Sopenharmony_ci pneg_ctxt->Name[3] = 0x50; 66462306a36Sopenharmony_ci pneg_ctxt->Name[4] = 0x9C; 66562306a36Sopenharmony_ci pneg_ctxt->Name[5] = 0xB4; 66662306a36Sopenharmony_ci pneg_ctxt->Name[6] = 0x11; 66762306a36Sopenharmony_ci pneg_ctxt->Name[7] = 0xE7; 66862306a36Sopenharmony_ci pneg_ctxt->Name[8] = 0xB4; 66962306a36Sopenharmony_ci pneg_ctxt->Name[9] = 0x23; 67062306a36Sopenharmony_ci pneg_ctxt->Name[10] = 0x83; 67162306a36Sopenharmony_ci pneg_ctxt->Name[11] = 0xDE; 67262306a36Sopenharmony_ci pneg_ctxt->Name[12] = 0x96; 67362306a36Sopenharmony_ci pneg_ctxt->Name[13] = 0x8B; 67462306a36Sopenharmony_ci pneg_ctxt->Name[14] = 0xCD; 67562306a36Sopenharmony_ci pneg_ctxt->Name[15] = 0x7C; 67662306a36Sopenharmony_ci} 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_cistatic void 67962306a36Sopenharmony_ciassemble_neg_contexts(struct smb2_negotiate_req *req, 68062306a36Sopenharmony_ci struct TCP_Server_Info *server, unsigned int *total_len) 68162306a36Sopenharmony_ci{ 68262306a36Sopenharmony_ci unsigned int ctxt_len, neg_context_count; 68362306a36Sopenharmony_ci struct TCP_Server_Info *pserver; 68462306a36Sopenharmony_ci char *pneg_ctxt; 68562306a36Sopenharmony_ci char *hostname; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci if (*total_len > 200) { 68862306a36Sopenharmony_ci /* In case length corrupted don't want to overrun smb buffer */ 68962306a36Sopenharmony_ci cifs_server_dbg(VFS, "Bad frame length assembling neg contexts\n"); 69062306a36Sopenharmony_ci return; 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci /* 69462306a36Sopenharmony_ci * round up total_len of fixed part of SMB3 negotiate request to 8 69562306a36Sopenharmony_ci * byte boundary before adding negotiate contexts 69662306a36Sopenharmony_ci */ 69762306a36Sopenharmony_ci *total_len = ALIGN(*total_len, 8); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci pneg_ctxt = (*total_len) + (char *)req; 70062306a36Sopenharmony_ci req->NegotiateContextOffset = cpu_to_le32(*total_len); 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci build_preauth_ctxt((struct smb2_preauth_neg_context *)pneg_ctxt); 70362306a36Sopenharmony_ci ctxt_len = ALIGN(sizeof(struct smb2_preauth_neg_context), 8); 70462306a36Sopenharmony_ci *total_len += ctxt_len; 70562306a36Sopenharmony_ci pneg_ctxt += ctxt_len; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci build_encrypt_ctxt((struct smb2_encryption_neg_context *)pneg_ctxt); 70862306a36Sopenharmony_ci ctxt_len = ALIGN(sizeof(struct smb2_encryption_neg_context), 8); 70962306a36Sopenharmony_ci *total_len += ctxt_len; 71062306a36Sopenharmony_ci pneg_ctxt += ctxt_len; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci /* 71362306a36Sopenharmony_ci * secondary channels don't have the hostname field populated 71462306a36Sopenharmony_ci * use the hostname field in the primary channel instead 71562306a36Sopenharmony_ci */ 71662306a36Sopenharmony_ci pserver = SERVER_IS_CHAN(server) ? server->primary_server : server; 71762306a36Sopenharmony_ci cifs_server_lock(pserver); 71862306a36Sopenharmony_ci hostname = pserver->hostname; 71962306a36Sopenharmony_ci if (hostname && (hostname[0] != 0)) { 72062306a36Sopenharmony_ci ctxt_len = build_netname_ctxt((struct smb2_netname_neg_context *)pneg_ctxt, 72162306a36Sopenharmony_ci hostname); 72262306a36Sopenharmony_ci *total_len += ctxt_len; 72362306a36Sopenharmony_ci pneg_ctxt += ctxt_len; 72462306a36Sopenharmony_ci neg_context_count = 3; 72562306a36Sopenharmony_ci } else 72662306a36Sopenharmony_ci neg_context_count = 2; 72762306a36Sopenharmony_ci cifs_server_unlock(pserver); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci build_posix_ctxt((struct smb2_posix_neg_context *)pneg_ctxt); 73062306a36Sopenharmony_ci *total_len += sizeof(struct smb2_posix_neg_context); 73162306a36Sopenharmony_ci pneg_ctxt += sizeof(struct smb2_posix_neg_context); 73262306a36Sopenharmony_ci neg_context_count++; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci if (server->compress_algorithm) { 73562306a36Sopenharmony_ci build_compression_ctxt((struct smb2_compression_capabilities_context *) 73662306a36Sopenharmony_ci pneg_ctxt); 73762306a36Sopenharmony_ci ctxt_len = ALIGN(sizeof(struct smb2_compression_capabilities_context), 8); 73862306a36Sopenharmony_ci *total_len += ctxt_len; 73962306a36Sopenharmony_ci pneg_ctxt += ctxt_len; 74062306a36Sopenharmony_ci neg_context_count++; 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if (enable_negotiate_signing) { 74462306a36Sopenharmony_ci ctxt_len = build_signing_ctxt((struct smb2_signing_capabilities *) 74562306a36Sopenharmony_ci pneg_ctxt); 74662306a36Sopenharmony_ci *total_len += ctxt_len; 74762306a36Sopenharmony_ci pneg_ctxt += ctxt_len; 74862306a36Sopenharmony_ci neg_context_count++; 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci /* check for and add transport_capabilities and signing capabilities */ 75262306a36Sopenharmony_ci req->NegotiateContextCount = cpu_to_le16(neg_context_count); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci} 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci/* If invalid preauth context warn but use what we requested, SHA-512 */ 75762306a36Sopenharmony_cistatic void decode_preauth_context(struct smb2_preauth_neg_context *ctxt) 75862306a36Sopenharmony_ci{ 75962306a36Sopenharmony_ci unsigned int len = le16_to_cpu(ctxt->DataLength); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci /* 76262306a36Sopenharmony_ci * Caller checked that DataLength remains within SMB boundary. We still 76362306a36Sopenharmony_ci * need to confirm that one HashAlgorithms member is accounted for. 76462306a36Sopenharmony_ci */ 76562306a36Sopenharmony_ci if (len < MIN_PREAUTH_CTXT_DATA_LEN) { 76662306a36Sopenharmony_ci pr_warn_once("server sent bad preauth context\n"); 76762306a36Sopenharmony_ci return; 76862306a36Sopenharmony_ci } else if (len < MIN_PREAUTH_CTXT_DATA_LEN + le16_to_cpu(ctxt->SaltLength)) { 76962306a36Sopenharmony_ci pr_warn_once("server sent invalid SaltLength\n"); 77062306a36Sopenharmony_ci return; 77162306a36Sopenharmony_ci } 77262306a36Sopenharmony_ci if (le16_to_cpu(ctxt->HashAlgorithmCount) != 1) 77362306a36Sopenharmony_ci pr_warn_once("Invalid SMB3 hash algorithm count\n"); 77462306a36Sopenharmony_ci if (ctxt->HashAlgorithms != SMB2_PREAUTH_INTEGRITY_SHA512) 77562306a36Sopenharmony_ci pr_warn_once("unknown SMB3 hash algorithm\n"); 77662306a36Sopenharmony_ci} 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_cistatic void decode_compress_ctx(struct TCP_Server_Info *server, 77962306a36Sopenharmony_ci struct smb2_compression_capabilities_context *ctxt) 78062306a36Sopenharmony_ci{ 78162306a36Sopenharmony_ci unsigned int len = le16_to_cpu(ctxt->DataLength); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci /* 78462306a36Sopenharmony_ci * Caller checked that DataLength remains within SMB boundary. We still 78562306a36Sopenharmony_ci * need to confirm that one CompressionAlgorithms member is accounted 78662306a36Sopenharmony_ci * for. 78762306a36Sopenharmony_ci */ 78862306a36Sopenharmony_ci if (len < 10) { 78962306a36Sopenharmony_ci pr_warn_once("server sent bad compression cntxt\n"); 79062306a36Sopenharmony_ci return; 79162306a36Sopenharmony_ci } 79262306a36Sopenharmony_ci if (le16_to_cpu(ctxt->CompressionAlgorithmCount) != 1) { 79362306a36Sopenharmony_ci pr_warn_once("Invalid SMB3 compress algorithm count\n"); 79462306a36Sopenharmony_ci return; 79562306a36Sopenharmony_ci } 79662306a36Sopenharmony_ci if (le16_to_cpu(ctxt->CompressionAlgorithms[0]) > 3) { 79762306a36Sopenharmony_ci pr_warn_once("unknown compression algorithm\n"); 79862306a36Sopenharmony_ci return; 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci server->compress_algorithm = ctxt->CompressionAlgorithms[0]; 80162306a36Sopenharmony_ci} 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_cistatic int decode_encrypt_ctx(struct TCP_Server_Info *server, 80462306a36Sopenharmony_ci struct smb2_encryption_neg_context *ctxt) 80562306a36Sopenharmony_ci{ 80662306a36Sopenharmony_ci unsigned int len = le16_to_cpu(ctxt->DataLength); 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci cifs_dbg(FYI, "decode SMB3.11 encryption neg context of len %d\n", len); 80962306a36Sopenharmony_ci /* 81062306a36Sopenharmony_ci * Caller checked that DataLength remains within SMB boundary. We still 81162306a36Sopenharmony_ci * need to confirm that one Cipher flexible array member is accounted 81262306a36Sopenharmony_ci * for. 81362306a36Sopenharmony_ci */ 81462306a36Sopenharmony_ci if (len < MIN_ENCRYPT_CTXT_DATA_LEN) { 81562306a36Sopenharmony_ci pr_warn_once("server sent bad crypto ctxt len\n"); 81662306a36Sopenharmony_ci return -EINVAL; 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci if (le16_to_cpu(ctxt->CipherCount) != 1) { 82062306a36Sopenharmony_ci pr_warn_once("Invalid SMB3.11 cipher count\n"); 82162306a36Sopenharmony_ci return -EINVAL; 82262306a36Sopenharmony_ci } 82362306a36Sopenharmony_ci cifs_dbg(FYI, "SMB311 cipher type:%d\n", le16_to_cpu(ctxt->Ciphers[0])); 82462306a36Sopenharmony_ci if (require_gcm_256) { 82562306a36Sopenharmony_ci if (ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES256_GCM) { 82662306a36Sopenharmony_ci cifs_dbg(VFS, "Server does not support requested encryption type (AES256 GCM)\n"); 82762306a36Sopenharmony_ci return -EOPNOTSUPP; 82862306a36Sopenharmony_ci } 82962306a36Sopenharmony_ci } else if (ctxt->Ciphers[0] == 0) { 83062306a36Sopenharmony_ci /* 83162306a36Sopenharmony_ci * e.g. if server only supported AES256_CCM (very unlikely) 83262306a36Sopenharmony_ci * or server supported no encryption types or had all disabled. 83362306a36Sopenharmony_ci * Since GLOBAL_CAP_ENCRYPTION will be not set, in the case 83462306a36Sopenharmony_ci * in which mount requested encryption ("seal") checks later 83562306a36Sopenharmony_ci * on during tree connection will return proper rc, but if 83662306a36Sopenharmony_ci * seal not requested by client, since server is allowed to 83762306a36Sopenharmony_ci * return 0 to indicate no supported cipher, we can't fail here 83862306a36Sopenharmony_ci */ 83962306a36Sopenharmony_ci server->cipher_type = 0; 84062306a36Sopenharmony_ci server->capabilities &= ~SMB2_GLOBAL_CAP_ENCRYPTION; 84162306a36Sopenharmony_ci pr_warn_once("Server does not support requested encryption types\n"); 84262306a36Sopenharmony_ci return 0; 84362306a36Sopenharmony_ci } else if ((ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES128_CCM) && 84462306a36Sopenharmony_ci (ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES128_GCM) && 84562306a36Sopenharmony_ci (ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES256_GCM)) { 84662306a36Sopenharmony_ci /* server returned a cipher we didn't ask for */ 84762306a36Sopenharmony_ci pr_warn_once("Invalid SMB3.11 cipher returned\n"); 84862306a36Sopenharmony_ci return -EINVAL; 84962306a36Sopenharmony_ci } 85062306a36Sopenharmony_ci server->cipher_type = ctxt->Ciphers[0]; 85162306a36Sopenharmony_ci server->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; 85262306a36Sopenharmony_ci return 0; 85362306a36Sopenharmony_ci} 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_cistatic void decode_signing_ctx(struct TCP_Server_Info *server, 85662306a36Sopenharmony_ci struct smb2_signing_capabilities *pctxt) 85762306a36Sopenharmony_ci{ 85862306a36Sopenharmony_ci unsigned int len = le16_to_cpu(pctxt->DataLength); 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci /* 86162306a36Sopenharmony_ci * Caller checked that DataLength remains within SMB boundary. We still 86262306a36Sopenharmony_ci * need to confirm that one SigningAlgorithms flexible array member is 86362306a36Sopenharmony_ci * accounted for. 86462306a36Sopenharmony_ci */ 86562306a36Sopenharmony_ci if ((len < 4) || (len > 16)) { 86662306a36Sopenharmony_ci pr_warn_once("server sent bad signing negcontext\n"); 86762306a36Sopenharmony_ci return; 86862306a36Sopenharmony_ci } 86962306a36Sopenharmony_ci if (le16_to_cpu(pctxt->SigningAlgorithmCount) != 1) { 87062306a36Sopenharmony_ci pr_warn_once("Invalid signing algorithm count\n"); 87162306a36Sopenharmony_ci return; 87262306a36Sopenharmony_ci } 87362306a36Sopenharmony_ci if (le16_to_cpu(pctxt->SigningAlgorithms[0]) > 2) { 87462306a36Sopenharmony_ci pr_warn_once("unknown signing algorithm\n"); 87562306a36Sopenharmony_ci return; 87662306a36Sopenharmony_ci } 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci server->signing_negotiated = true; 87962306a36Sopenharmony_ci server->signing_algorithm = le16_to_cpu(pctxt->SigningAlgorithms[0]); 88062306a36Sopenharmony_ci cifs_dbg(FYI, "signing algorithm %d chosen\n", 88162306a36Sopenharmony_ci server->signing_algorithm); 88262306a36Sopenharmony_ci} 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_cistatic int smb311_decode_neg_context(struct smb2_negotiate_rsp *rsp, 88662306a36Sopenharmony_ci struct TCP_Server_Info *server, 88762306a36Sopenharmony_ci unsigned int len_of_smb) 88862306a36Sopenharmony_ci{ 88962306a36Sopenharmony_ci struct smb2_neg_context *pctx; 89062306a36Sopenharmony_ci unsigned int offset = le32_to_cpu(rsp->NegotiateContextOffset); 89162306a36Sopenharmony_ci unsigned int ctxt_cnt = le16_to_cpu(rsp->NegotiateContextCount); 89262306a36Sopenharmony_ci unsigned int len_of_ctxts, i; 89362306a36Sopenharmony_ci int rc = 0; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci cifs_dbg(FYI, "decoding %d negotiate contexts\n", ctxt_cnt); 89662306a36Sopenharmony_ci if (len_of_smb <= offset) { 89762306a36Sopenharmony_ci cifs_server_dbg(VFS, "Invalid response: negotiate context offset\n"); 89862306a36Sopenharmony_ci return -EINVAL; 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci len_of_ctxts = len_of_smb - offset; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci for (i = 0; i < ctxt_cnt; i++) { 90462306a36Sopenharmony_ci int clen; 90562306a36Sopenharmony_ci /* check that offset is not beyond end of SMB */ 90662306a36Sopenharmony_ci if (len_of_ctxts < sizeof(struct smb2_neg_context)) 90762306a36Sopenharmony_ci break; 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci pctx = (struct smb2_neg_context *)(offset + (char *)rsp); 91062306a36Sopenharmony_ci clen = sizeof(struct smb2_neg_context) 91162306a36Sopenharmony_ci + le16_to_cpu(pctx->DataLength); 91262306a36Sopenharmony_ci /* 91362306a36Sopenharmony_ci * 2.2.4 SMB2 NEGOTIATE Response 91462306a36Sopenharmony_ci * Subsequent negotiate contexts MUST appear at the first 8-byte 91562306a36Sopenharmony_ci * aligned offset following the previous negotiate context. 91662306a36Sopenharmony_ci */ 91762306a36Sopenharmony_ci if (i + 1 != ctxt_cnt) 91862306a36Sopenharmony_ci clen = ALIGN(clen, 8); 91962306a36Sopenharmony_ci if (clen > len_of_ctxts) 92062306a36Sopenharmony_ci break; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci if (pctx->ContextType == SMB2_PREAUTH_INTEGRITY_CAPABILITIES) 92362306a36Sopenharmony_ci decode_preauth_context( 92462306a36Sopenharmony_ci (struct smb2_preauth_neg_context *)pctx); 92562306a36Sopenharmony_ci else if (pctx->ContextType == SMB2_ENCRYPTION_CAPABILITIES) 92662306a36Sopenharmony_ci rc = decode_encrypt_ctx(server, 92762306a36Sopenharmony_ci (struct smb2_encryption_neg_context *)pctx); 92862306a36Sopenharmony_ci else if (pctx->ContextType == SMB2_COMPRESSION_CAPABILITIES) 92962306a36Sopenharmony_ci decode_compress_ctx(server, 93062306a36Sopenharmony_ci (struct smb2_compression_capabilities_context *)pctx); 93162306a36Sopenharmony_ci else if (pctx->ContextType == SMB2_POSIX_EXTENSIONS_AVAILABLE) 93262306a36Sopenharmony_ci server->posix_ext_supported = true; 93362306a36Sopenharmony_ci else if (pctx->ContextType == SMB2_SIGNING_CAPABILITIES) 93462306a36Sopenharmony_ci decode_signing_ctx(server, 93562306a36Sopenharmony_ci (struct smb2_signing_capabilities *)pctx); 93662306a36Sopenharmony_ci else 93762306a36Sopenharmony_ci cifs_server_dbg(VFS, "unknown negcontext of type %d ignored\n", 93862306a36Sopenharmony_ci le16_to_cpu(pctx->ContextType)); 93962306a36Sopenharmony_ci if (rc) 94062306a36Sopenharmony_ci break; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci offset += clen; 94362306a36Sopenharmony_ci len_of_ctxts -= clen; 94462306a36Sopenharmony_ci } 94562306a36Sopenharmony_ci return rc; 94662306a36Sopenharmony_ci} 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_cistatic struct create_posix * 94962306a36Sopenharmony_cicreate_posix_buf(umode_t mode) 95062306a36Sopenharmony_ci{ 95162306a36Sopenharmony_ci struct create_posix *buf; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci buf = kzalloc(sizeof(struct create_posix), 95462306a36Sopenharmony_ci GFP_KERNEL); 95562306a36Sopenharmony_ci if (!buf) 95662306a36Sopenharmony_ci return NULL; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci buf->ccontext.DataOffset = 95962306a36Sopenharmony_ci cpu_to_le16(offsetof(struct create_posix, Mode)); 96062306a36Sopenharmony_ci buf->ccontext.DataLength = cpu_to_le32(4); 96162306a36Sopenharmony_ci buf->ccontext.NameOffset = 96262306a36Sopenharmony_ci cpu_to_le16(offsetof(struct create_posix, Name)); 96362306a36Sopenharmony_ci buf->ccontext.NameLength = cpu_to_le16(16); 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci /* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */ 96662306a36Sopenharmony_ci buf->Name[0] = 0x93; 96762306a36Sopenharmony_ci buf->Name[1] = 0xAD; 96862306a36Sopenharmony_ci buf->Name[2] = 0x25; 96962306a36Sopenharmony_ci buf->Name[3] = 0x50; 97062306a36Sopenharmony_ci buf->Name[4] = 0x9C; 97162306a36Sopenharmony_ci buf->Name[5] = 0xB4; 97262306a36Sopenharmony_ci buf->Name[6] = 0x11; 97362306a36Sopenharmony_ci buf->Name[7] = 0xE7; 97462306a36Sopenharmony_ci buf->Name[8] = 0xB4; 97562306a36Sopenharmony_ci buf->Name[9] = 0x23; 97662306a36Sopenharmony_ci buf->Name[10] = 0x83; 97762306a36Sopenharmony_ci buf->Name[11] = 0xDE; 97862306a36Sopenharmony_ci buf->Name[12] = 0x96; 97962306a36Sopenharmony_ci buf->Name[13] = 0x8B; 98062306a36Sopenharmony_ci buf->Name[14] = 0xCD; 98162306a36Sopenharmony_ci buf->Name[15] = 0x7C; 98262306a36Sopenharmony_ci buf->Mode = cpu_to_le32(mode); 98362306a36Sopenharmony_ci cifs_dbg(FYI, "mode on posix create 0%o\n", mode); 98462306a36Sopenharmony_ci return buf; 98562306a36Sopenharmony_ci} 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_cistatic int 98862306a36Sopenharmony_ciadd_posix_context(struct kvec *iov, unsigned int *num_iovec, umode_t mode) 98962306a36Sopenharmony_ci{ 99062306a36Sopenharmony_ci unsigned int num = *num_iovec; 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci iov[num].iov_base = create_posix_buf(mode); 99362306a36Sopenharmony_ci if (mode == ACL_NO_MODE) 99462306a36Sopenharmony_ci cifs_dbg(FYI, "%s: no mode\n", __func__); 99562306a36Sopenharmony_ci if (iov[num].iov_base == NULL) 99662306a36Sopenharmony_ci return -ENOMEM; 99762306a36Sopenharmony_ci iov[num].iov_len = sizeof(struct create_posix); 99862306a36Sopenharmony_ci *num_iovec = num + 1; 99962306a36Sopenharmony_ci return 0; 100062306a36Sopenharmony_ci} 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci/* 100462306a36Sopenharmony_ci * 100562306a36Sopenharmony_ci * SMB2 Worker functions follow: 100662306a36Sopenharmony_ci * 100762306a36Sopenharmony_ci * The general structure of the worker functions is: 100862306a36Sopenharmony_ci * 1) Call smb2_init (assembles SMB2 header) 100962306a36Sopenharmony_ci * 2) Initialize SMB2 command specific fields in fixed length area of SMB 101062306a36Sopenharmony_ci * 3) Call smb_sendrcv2 (sends request on socket and waits for response) 101162306a36Sopenharmony_ci * 4) Decode SMB2 command specific fields in the fixed length area 101262306a36Sopenharmony_ci * 5) Decode variable length data area (if any for this SMB2 command type) 101362306a36Sopenharmony_ci * 6) Call free smb buffer 101462306a36Sopenharmony_ci * 7) return 101562306a36Sopenharmony_ci * 101662306a36Sopenharmony_ci */ 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ciint 101962306a36Sopenharmony_ciSMB2_negotiate(const unsigned int xid, 102062306a36Sopenharmony_ci struct cifs_ses *ses, 102162306a36Sopenharmony_ci struct TCP_Server_Info *server) 102262306a36Sopenharmony_ci{ 102362306a36Sopenharmony_ci struct smb_rqst rqst; 102462306a36Sopenharmony_ci struct smb2_negotiate_req *req; 102562306a36Sopenharmony_ci struct smb2_negotiate_rsp *rsp; 102662306a36Sopenharmony_ci struct kvec iov[1]; 102762306a36Sopenharmony_ci struct kvec rsp_iov; 102862306a36Sopenharmony_ci int rc; 102962306a36Sopenharmony_ci int resp_buftype; 103062306a36Sopenharmony_ci int blob_offset, blob_length; 103162306a36Sopenharmony_ci char *security_blob; 103262306a36Sopenharmony_ci int flags = CIFS_NEG_OP; 103362306a36Sopenharmony_ci unsigned int total_len; 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci cifs_dbg(FYI, "Negotiate protocol\n"); 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci if (!server) { 103862306a36Sopenharmony_ci WARN(1, "%s: server is NULL!\n", __func__); 103962306a36Sopenharmony_ci return -EIO; 104062306a36Sopenharmony_ci } 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci rc = smb2_plain_req_init(SMB2_NEGOTIATE, NULL, server, 104362306a36Sopenharmony_ci (void **) &req, &total_len); 104462306a36Sopenharmony_ci if (rc) 104562306a36Sopenharmony_ci return rc; 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci req->hdr.SessionId = 0; 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci memset(server->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE); 105062306a36Sopenharmony_ci memset(ses->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE); 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci if (strcmp(server->vals->version_string, 105362306a36Sopenharmony_ci SMB3ANY_VERSION_STRING) == 0) { 105462306a36Sopenharmony_ci req->Dialects[0] = cpu_to_le16(SMB30_PROT_ID); 105562306a36Sopenharmony_ci req->Dialects[1] = cpu_to_le16(SMB302_PROT_ID); 105662306a36Sopenharmony_ci req->Dialects[2] = cpu_to_le16(SMB311_PROT_ID); 105762306a36Sopenharmony_ci req->DialectCount = cpu_to_le16(3); 105862306a36Sopenharmony_ci total_len += 6; 105962306a36Sopenharmony_ci } else if (strcmp(server->vals->version_string, 106062306a36Sopenharmony_ci SMBDEFAULT_VERSION_STRING) == 0) { 106162306a36Sopenharmony_ci req->Dialects[0] = cpu_to_le16(SMB21_PROT_ID); 106262306a36Sopenharmony_ci req->Dialects[1] = cpu_to_le16(SMB30_PROT_ID); 106362306a36Sopenharmony_ci req->Dialects[2] = cpu_to_le16(SMB302_PROT_ID); 106462306a36Sopenharmony_ci req->Dialects[3] = cpu_to_le16(SMB311_PROT_ID); 106562306a36Sopenharmony_ci req->DialectCount = cpu_to_le16(4); 106662306a36Sopenharmony_ci total_len += 8; 106762306a36Sopenharmony_ci } else { 106862306a36Sopenharmony_ci /* otherwise send specific dialect */ 106962306a36Sopenharmony_ci req->Dialects[0] = cpu_to_le16(server->vals->protocol_id); 107062306a36Sopenharmony_ci req->DialectCount = cpu_to_le16(1); 107162306a36Sopenharmony_ci total_len += 2; 107262306a36Sopenharmony_ci } 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci /* only one of SMB2 signing flags may be set in SMB2 request */ 107562306a36Sopenharmony_ci if (ses->sign) 107662306a36Sopenharmony_ci req->SecurityMode = cpu_to_le16(SMB2_NEGOTIATE_SIGNING_REQUIRED); 107762306a36Sopenharmony_ci else if (global_secflags & CIFSSEC_MAY_SIGN) 107862306a36Sopenharmony_ci req->SecurityMode = cpu_to_le16(SMB2_NEGOTIATE_SIGNING_ENABLED); 107962306a36Sopenharmony_ci else 108062306a36Sopenharmony_ci req->SecurityMode = 0; 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci req->Capabilities = cpu_to_le32(server->vals->req_capabilities); 108362306a36Sopenharmony_ci if (ses->chan_max > 1) 108462306a36Sopenharmony_ci req->Capabilities |= cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL); 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci /* ClientGUID must be zero for SMB2.02 dialect */ 108762306a36Sopenharmony_ci if (server->vals->protocol_id == SMB20_PROT_ID) 108862306a36Sopenharmony_ci memset(req->ClientGUID, 0, SMB2_CLIENT_GUID_SIZE); 108962306a36Sopenharmony_ci else { 109062306a36Sopenharmony_ci memcpy(req->ClientGUID, server->client_guid, 109162306a36Sopenharmony_ci SMB2_CLIENT_GUID_SIZE); 109262306a36Sopenharmony_ci if ((server->vals->protocol_id == SMB311_PROT_ID) || 109362306a36Sopenharmony_ci (strcmp(server->vals->version_string, 109462306a36Sopenharmony_ci SMB3ANY_VERSION_STRING) == 0) || 109562306a36Sopenharmony_ci (strcmp(server->vals->version_string, 109662306a36Sopenharmony_ci SMBDEFAULT_VERSION_STRING) == 0)) 109762306a36Sopenharmony_ci assemble_neg_contexts(req, server, &total_len); 109862306a36Sopenharmony_ci } 109962306a36Sopenharmony_ci iov[0].iov_base = (char *)req; 110062306a36Sopenharmony_ci iov[0].iov_len = total_len; 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 110362306a36Sopenharmony_ci rqst.rq_iov = iov; 110462306a36Sopenharmony_ci rqst.rq_nvec = 1; 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci rc = cifs_send_recv(xid, ses, server, 110762306a36Sopenharmony_ci &rqst, &resp_buftype, flags, &rsp_iov); 110862306a36Sopenharmony_ci cifs_small_buf_release(req); 110962306a36Sopenharmony_ci rsp = (struct smb2_negotiate_rsp *)rsp_iov.iov_base; 111062306a36Sopenharmony_ci /* 111162306a36Sopenharmony_ci * No tcon so can't do 111262306a36Sopenharmony_ci * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]); 111362306a36Sopenharmony_ci */ 111462306a36Sopenharmony_ci if (rc == -EOPNOTSUPP) { 111562306a36Sopenharmony_ci cifs_server_dbg(VFS, "Dialect not supported by server. Consider specifying vers=1.0 or vers=2.0 on mount for accessing older servers\n"); 111662306a36Sopenharmony_ci goto neg_exit; 111762306a36Sopenharmony_ci } else if (rc != 0) 111862306a36Sopenharmony_ci goto neg_exit; 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci rc = -EIO; 112162306a36Sopenharmony_ci if (strcmp(server->vals->version_string, 112262306a36Sopenharmony_ci SMB3ANY_VERSION_STRING) == 0) { 112362306a36Sopenharmony_ci if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) { 112462306a36Sopenharmony_ci cifs_server_dbg(VFS, 112562306a36Sopenharmony_ci "SMB2 dialect returned but not requested\n"); 112662306a36Sopenharmony_ci goto neg_exit; 112762306a36Sopenharmony_ci } else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) { 112862306a36Sopenharmony_ci cifs_server_dbg(VFS, 112962306a36Sopenharmony_ci "SMB2.1 dialect returned but not requested\n"); 113062306a36Sopenharmony_ci goto neg_exit; 113162306a36Sopenharmony_ci } else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) { 113262306a36Sopenharmony_ci /* ops set to 3.0 by default for default so update */ 113362306a36Sopenharmony_ci server->ops = &smb311_operations; 113462306a36Sopenharmony_ci server->vals = &smb311_values; 113562306a36Sopenharmony_ci } 113662306a36Sopenharmony_ci } else if (strcmp(server->vals->version_string, 113762306a36Sopenharmony_ci SMBDEFAULT_VERSION_STRING) == 0) { 113862306a36Sopenharmony_ci if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) { 113962306a36Sopenharmony_ci cifs_server_dbg(VFS, 114062306a36Sopenharmony_ci "SMB2 dialect returned but not requested\n"); 114162306a36Sopenharmony_ci goto neg_exit; 114262306a36Sopenharmony_ci } else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) { 114362306a36Sopenharmony_ci /* ops set to 3.0 by default for default so update */ 114462306a36Sopenharmony_ci server->ops = &smb21_operations; 114562306a36Sopenharmony_ci server->vals = &smb21_values; 114662306a36Sopenharmony_ci } else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) { 114762306a36Sopenharmony_ci server->ops = &smb311_operations; 114862306a36Sopenharmony_ci server->vals = &smb311_values; 114962306a36Sopenharmony_ci } 115062306a36Sopenharmony_ci } else if (le16_to_cpu(rsp->DialectRevision) != 115162306a36Sopenharmony_ci server->vals->protocol_id) { 115262306a36Sopenharmony_ci /* if requested single dialect ensure returned dialect matched */ 115362306a36Sopenharmony_ci cifs_server_dbg(VFS, "Invalid 0x%x dialect returned: not requested\n", 115462306a36Sopenharmony_ci le16_to_cpu(rsp->DialectRevision)); 115562306a36Sopenharmony_ci goto neg_exit; 115662306a36Sopenharmony_ci } 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci cifs_dbg(FYI, "mode 0x%x\n", rsp->SecurityMode); 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) 116162306a36Sopenharmony_ci cifs_dbg(FYI, "negotiated smb2.0 dialect\n"); 116262306a36Sopenharmony_ci else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) 116362306a36Sopenharmony_ci cifs_dbg(FYI, "negotiated smb2.1 dialect\n"); 116462306a36Sopenharmony_ci else if (rsp->DialectRevision == cpu_to_le16(SMB30_PROT_ID)) 116562306a36Sopenharmony_ci cifs_dbg(FYI, "negotiated smb3.0 dialect\n"); 116662306a36Sopenharmony_ci else if (rsp->DialectRevision == cpu_to_le16(SMB302_PROT_ID)) 116762306a36Sopenharmony_ci cifs_dbg(FYI, "negotiated smb3.02 dialect\n"); 116862306a36Sopenharmony_ci else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) 116962306a36Sopenharmony_ci cifs_dbg(FYI, "negotiated smb3.1.1 dialect\n"); 117062306a36Sopenharmony_ci else { 117162306a36Sopenharmony_ci cifs_server_dbg(VFS, "Invalid dialect returned by server 0x%x\n", 117262306a36Sopenharmony_ci le16_to_cpu(rsp->DialectRevision)); 117362306a36Sopenharmony_ci goto neg_exit; 117462306a36Sopenharmony_ci } 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci rc = 0; 117762306a36Sopenharmony_ci server->dialect = le16_to_cpu(rsp->DialectRevision); 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci /* 118062306a36Sopenharmony_ci * Keep a copy of the hash after negprot. This hash will be 118162306a36Sopenharmony_ci * the starting hash value for all sessions made from this 118262306a36Sopenharmony_ci * server. 118362306a36Sopenharmony_ci */ 118462306a36Sopenharmony_ci memcpy(server->preauth_sha_hash, ses->preauth_sha_hash, 118562306a36Sopenharmony_ci SMB2_PREAUTH_HASH_SIZE); 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci /* SMB2 only has an extended negflavor */ 118862306a36Sopenharmony_ci server->negflavor = CIFS_NEGFLAVOR_EXTENDED; 118962306a36Sopenharmony_ci /* set it to the maximum buffer size value we can send with 1 credit */ 119062306a36Sopenharmony_ci server->maxBuf = min_t(unsigned int, le32_to_cpu(rsp->MaxTransactSize), 119162306a36Sopenharmony_ci SMB2_MAX_BUFFER_SIZE); 119262306a36Sopenharmony_ci server->max_read = le32_to_cpu(rsp->MaxReadSize); 119362306a36Sopenharmony_ci server->max_write = le32_to_cpu(rsp->MaxWriteSize); 119462306a36Sopenharmony_ci server->sec_mode = le16_to_cpu(rsp->SecurityMode); 119562306a36Sopenharmony_ci if ((server->sec_mode & SMB2_SEC_MODE_FLAGS_ALL) != server->sec_mode) 119662306a36Sopenharmony_ci cifs_dbg(FYI, "Server returned unexpected security mode 0x%x\n", 119762306a36Sopenharmony_ci server->sec_mode); 119862306a36Sopenharmony_ci server->capabilities = le32_to_cpu(rsp->Capabilities); 119962306a36Sopenharmony_ci /* Internal types */ 120062306a36Sopenharmony_ci server->capabilities |= SMB2_NT_FIND | SMB2_LARGE_FILES; 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci /* 120362306a36Sopenharmony_ci * SMB3.0 supports only 1 cipher and doesn't have a encryption neg context 120462306a36Sopenharmony_ci * Set the cipher type manually. 120562306a36Sopenharmony_ci */ 120662306a36Sopenharmony_ci if (server->dialect == SMB30_PROT_ID && (server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) 120762306a36Sopenharmony_ci server->cipher_type = SMB2_ENCRYPTION_AES128_CCM; 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci security_blob = smb2_get_data_area_len(&blob_offset, &blob_length, 121062306a36Sopenharmony_ci (struct smb2_hdr *)rsp); 121162306a36Sopenharmony_ci /* 121262306a36Sopenharmony_ci * See MS-SMB2 section 2.2.4: if no blob, client picks default which 121362306a36Sopenharmony_ci * for us will be 121462306a36Sopenharmony_ci * ses->sectype = RawNTLMSSP; 121562306a36Sopenharmony_ci * but for time being this is our only auth choice so doesn't matter. 121662306a36Sopenharmony_ci * We just found a server which sets blob length to zero expecting raw. 121762306a36Sopenharmony_ci */ 121862306a36Sopenharmony_ci if (blob_length == 0) { 121962306a36Sopenharmony_ci cifs_dbg(FYI, "missing security blob on negprot\n"); 122062306a36Sopenharmony_ci server->sec_ntlmssp = true; 122162306a36Sopenharmony_ci } 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci rc = cifs_enable_signing(server, ses->sign); 122462306a36Sopenharmony_ci if (rc) 122562306a36Sopenharmony_ci goto neg_exit; 122662306a36Sopenharmony_ci if (blob_length) { 122762306a36Sopenharmony_ci rc = decode_negTokenInit(security_blob, blob_length, server); 122862306a36Sopenharmony_ci if (rc == 1) 122962306a36Sopenharmony_ci rc = 0; 123062306a36Sopenharmony_ci else if (rc == 0) 123162306a36Sopenharmony_ci rc = -EIO; 123262306a36Sopenharmony_ci } 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) { 123562306a36Sopenharmony_ci if (rsp->NegotiateContextCount) 123662306a36Sopenharmony_ci rc = smb311_decode_neg_context(rsp, server, 123762306a36Sopenharmony_ci rsp_iov.iov_len); 123862306a36Sopenharmony_ci else 123962306a36Sopenharmony_ci cifs_server_dbg(VFS, "Missing expected negotiate contexts\n"); 124062306a36Sopenharmony_ci } 124162306a36Sopenharmony_cineg_exit: 124262306a36Sopenharmony_ci free_rsp_buf(resp_buftype, rsp); 124362306a36Sopenharmony_ci return rc; 124462306a36Sopenharmony_ci} 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ciint smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) 124762306a36Sopenharmony_ci{ 124862306a36Sopenharmony_ci int rc; 124962306a36Sopenharmony_ci struct validate_negotiate_info_req *pneg_inbuf; 125062306a36Sopenharmony_ci struct validate_negotiate_info_rsp *pneg_rsp = NULL; 125162306a36Sopenharmony_ci u32 rsplen; 125262306a36Sopenharmony_ci u32 inbuflen; /* max of 4 dialects */ 125362306a36Sopenharmony_ci struct TCP_Server_Info *server = tcon->ses->server; 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci cifs_dbg(FYI, "validate negotiate\n"); 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci /* In SMB3.11 preauth integrity supersedes validate negotiate */ 125862306a36Sopenharmony_ci if (server->dialect == SMB311_PROT_ID) 125962306a36Sopenharmony_ci return 0; 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci /* 126262306a36Sopenharmony_ci * validation ioctl must be signed, so no point sending this if we 126362306a36Sopenharmony_ci * can not sign it (ie are not known user). Even if signing is not 126462306a36Sopenharmony_ci * required (enabled but not negotiated), in those cases we selectively 126562306a36Sopenharmony_ci * sign just this, the first and only signed request on a connection. 126662306a36Sopenharmony_ci * Having validation of negotiate info helps reduce attack vectors. 126762306a36Sopenharmony_ci */ 126862306a36Sopenharmony_ci if (tcon->ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST) 126962306a36Sopenharmony_ci return 0; /* validation requires signing */ 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci if (tcon->ses->user_name == NULL) { 127262306a36Sopenharmony_ci cifs_dbg(FYI, "Can't validate negotiate: null user mount\n"); 127362306a36Sopenharmony_ci return 0; /* validation requires signing */ 127462306a36Sopenharmony_ci } 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci if (tcon->ses->session_flags & SMB2_SESSION_FLAG_IS_NULL) 127762306a36Sopenharmony_ci cifs_tcon_dbg(VFS, "Unexpected null user (anonymous) auth flag sent by server\n"); 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci pneg_inbuf = kmalloc(sizeof(*pneg_inbuf), GFP_NOFS); 128062306a36Sopenharmony_ci if (!pneg_inbuf) 128162306a36Sopenharmony_ci return -ENOMEM; 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci pneg_inbuf->Capabilities = 128462306a36Sopenharmony_ci cpu_to_le32(server->vals->req_capabilities); 128562306a36Sopenharmony_ci if (tcon->ses->chan_max > 1) 128662306a36Sopenharmony_ci pneg_inbuf->Capabilities |= cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL); 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci memcpy(pneg_inbuf->Guid, server->client_guid, 128962306a36Sopenharmony_ci SMB2_CLIENT_GUID_SIZE); 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci if (tcon->ses->sign) 129262306a36Sopenharmony_ci pneg_inbuf->SecurityMode = 129362306a36Sopenharmony_ci cpu_to_le16(SMB2_NEGOTIATE_SIGNING_REQUIRED); 129462306a36Sopenharmony_ci else if (global_secflags & CIFSSEC_MAY_SIGN) 129562306a36Sopenharmony_ci pneg_inbuf->SecurityMode = 129662306a36Sopenharmony_ci cpu_to_le16(SMB2_NEGOTIATE_SIGNING_ENABLED); 129762306a36Sopenharmony_ci else 129862306a36Sopenharmony_ci pneg_inbuf->SecurityMode = 0; 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci if (strcmp(server->vals->version_string, 130262306a36Sopenharmony_ci SMB3ANY_VERSION_STRING) == 0) { 130362306a36Sopenharmony_ci pneg_inbuf->Dialects[0] = cpu_to_le16(SMB30_PROT_ID); 130462306a36Sopenharmony_ci pneg_inbuf->Dialects[1] = cpu_to_le16(SMB302_PROT_ID); 130562306a36Sopenharmony_ci pneg_inbuf->Dialects[2] = cpu_to_le16(SMB311_PROT_ID); 130662306a36Sopenharmony_ci pneg_inbuf->DialectCount = cpu_to_le16(3); 130762306a36Sopenharmony_ci /* SMB 2.1 not included so subtract one dialect from len */ 130862306a36Sopenharmony_ci inbuflen = sizeof(*pneg_inbuf) - 130962306a36Sopenharmony_ci (sizeof(pneg_inbuf->Dialects[0])); 131062306a36Sopenharmony_ci } else if (strcmp(server->vals->version_string, 131162306a36Sopenharmony_ci SMBDEFAULT_VERSION_STRING) == 0) { 131262306a36Sopenharmony_ci pneg_inbuf->Dialects[0] = cpu_to_le16(SMB21_PROT_ID); 131362306a36Sopenharmony_ci pneg_inbuf->Dialects[1] = cpu_to_le16(SMB30_PROT_ID); 131462306a36Sopenharmony_ci pneg_inbuf->Dialects[2] = cpu_to_le16(SMB302_PROT_ID); 131562306a36Sopenharmony_ci pneg_inbuf->Dialects[3] = cpu_to_le16(SMB311_PROT_ID); 131662306a36Sopenharmony_ci pneg_inbuf->DialectCount = cpu_to_le16(4); 131762306a36Sopenharmony_ci /* structure is big enough for 4 dialects */ 131862306a36Sopenharmony_ci inbuflen = sizeof(*pneg_inbuf); 131962306a36Sopenharmony_ci } else { 132062306a36Sopenharmony_ci /* otherwise specific dialect was requested */ 132162306a36Sopenharmony_ci pneg_inbuf->Dialects[0] = 132262306a36Sopenharmony_ci cpu_to_le16(server->vals->protocol_id); 132362306a36Sopenharmony_ci pneg_inbuf->DialectCount = cpu_to_le16(1); 132462306a36Sopenharmony_ci /* structure is big enough for 4 dialects, sending only 1 */ 132562306a36Sopenharmony_ci inbuflen = sizeof(*pneg_inbuf) - 132662306a36Sopenharmony_ci sizeof(pneg_inbuf->Dialects[0]) * 3; 132762306a36Sopenharmony_ci } 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, 133062306a36Sopenharmony_ci FSCTL_VALIDATE_NEGOTIATE_INFO, 133162306a36Sopenharmony_ci (char *)pneg_inbuf, inbuflen, CIFSMaxBufSize, 133262306a36Sopenharmony_ci (char **)&pneg_rsp, &rsplen); 133362306a36Sopenharmony_ci if (rc == -EOPNOTSUPP) { 133462306a36Sopenharmony_ci /* 133562306a36Sopenharmony_ci * Old Windows versions or Netapp SMB server can return 133662306a36Sopenharmony_ci * not supported error. Client should accept it. 133762306a36Sopenharmony_ci */ 133862306a36Sopenharmony_ci cifs_tcon_dbg(VFS, "Server does not support validate negotiate\n"); 133962306a36Sopenharmony_ci rc = 0; 134062306a36Sopenharmony_ci goto out_free_inbuf; 134162306a36Sopenharmony_ci } else if (rc != 0) { 134262306a36Sopenharmony_ci cifs_tcon_dbg(VFS, "validate protocol negotiate failed: %d\n", 134362306a36Sopenharmony_ci rc); 134462306a36Sopenharmony_ci rc = -EIO; 134562306a36Sopenharmony_ci goto out_free_inbuf; 134662306a36Sopenharmony_ci } 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci rc = -EIO; 134962306a36Sopenharmony_ci if (rsplen != sizeof(*pneg_rsp)) { 135062306a36Sopenharmony_ci cifs_tcon_dbg(VFS, "Invalid protocol negotiate response size: %d\n", 135162306a36Sopenharmony_ci rsplen); 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci /* relax check since Mac returns max bufsize allowed on ioctl */ 135462306a36Sopenharmony_ci if (rsplen > CIFSMaxBufSize || rsplen < sizeof(*pneg_rsp)) 135562306a36Sopenharmony_ci goto out_free_rsp; 135662306a36Sopenharmony_ci } 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci /* check validate negotiate info response matches what we got earlier */ 135962306a36Sopenharmony_ci if (pneg_rsp->Dialect != cpu_to_le16(server->dialect)) 136062306a36Sopenharmony_ci goto vneg_out; 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci if (pneg_rsp->SecurityMode != cpu_to_le16(server->sec_mode)) 136362306a36Sopenharmony_ci goto vneg_out; 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci /* do not validate server guid because not saved at negprot time yet */ 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci if ((le32_to_cpu(pneg_rsp->Capabilities) | SMB2_NT_FIND | 136862306a36Sopenharmony_ci SMB2_LARGE_FILES) != server->capabilities) 136962306a36Sopenharmony_ci goto vneg_out; 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci /* validate negotiate successful */ 137262306a36Sopenharmony_ci rc = 0; 137362306a36Sopenharmony_ci cifs_dbg(FYI, "validate negotiate info successful\n"); 137462306a36Sopenharmony_ci goto out_free_rsp; 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_civneg_out: 137762306a36Sopenharmony_ci cifs_tcon_dbg(VFS, "protocol revalidation - security settings mismatch\n"); 137862306a36Sopenharmony_ciout_free_rsp: 137962306a36Sopenharmony_ci kfree(pneg_rsp); 138062306a36Sopenharmony_ciout_free_inbuf: 138162306a36Sopenharmony_ci kfree(pneg_inbuf); 138262306a36Sopenharmony_ci return rc; 138362306a36Sopenharmony_ci} 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_cienum securityEnum 138662306a36Sopenharmony_cismb2_select_sectype(struct TCP_Server_Info *server, enum securityEnum requested) 138762306a36Sopenharmony_ci{ 138862306a36Sopenharmony_ci switch (requested) { 138962306a36Sopenharmony_ci case Kerberos: 139062306a36Sopenharmony_ci case RawNTLMSSP: 139162306a36Sopenharmony_ci return requested; 139262306a36Sopenharmony_ci case NTLMv2: 139362306a36Sopenharmony_ci return RawNTLMSSP; 139462306a36Sopenharmony_ci case Unspecified: 139562306a36Sopenharmony_ci if (server->sec_ntlmssp && 139662306a36Sopenharmony_ci (global_secflags & CIFSSEC_MAY_NTLMSSP)) 139762306a36Sopenharmony_ci return RawNTLMSSP; 139862306a36Sopenharmony_ci if ((server->sec_kerberos || server->sec_mskerberos) && 139962306a36Sopenharmony_ci (global_secflags & CIFSSEC_MAY_KRB5)) 140062306a36Sopenharmony_ci return Kerberos; 140162306a36Sopenharmony_ci fallthrough; 140262306a36Sopenharmony_ci default: 140362306a36Sopenharmony_ci return Unspecified; 140462306a36Sopenharmony_ci } 140562306a36Sopenharmony_ci} 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_cistruct SMB2_sess_data { 140862306a36Sopenharmony_ci unsigned int xid; 140962306a36Sopenharmony_ci struct cifs_ses *ses; 141062306a36Sopenharmony_ci struct TCP_Server_Info *server; 141162306a36Sopenharmony_ci struct nls_table *nls_cp; 141262306a36Sopenharmony_ci void (*func)(struct SMB2_sess_data *); 141362306a36Sopenharmony_ci int result; 141462306a36Sopenharmony_ci u64 previous_session; 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci /* we will send the SMB in three pieces: 141762306a36Sopenharmony_ci * a fixed length beginning part, an optional 141862306a36Sopenharmony_ci * SPNEGO blob (which can be zero length), and a 141962306a36Sopenharmony_ci * last part which will include the strings 142062306a36Sopenharmony_ci * and rest of bcc area. This allows us to avoid 142162306a36Sopenharmony_ci * a large buffer 17K allocation 142262306a36Sopenharmony_ci */ 142362306a36Sopenharmony_ci int buf0_type; 142462306a36Sopenharmony_ci struct kvec iov[2]; 142562306a36Sopenharmony_ci}; 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_cistatic int 142862306a36Sopenharmony_ciSMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data) 142962306a36Sopenharmony_ci{ 143062306a36Sopenharmony_ci int rc; 143162306a36Sopenharmony_ci struct cifs_ses *ses = sess_data->ses; 143262306a36Sopenharmony_ci struct TCP_Server_Info *server = sess_data->server; 143362306a36Sopenharmony_ci struct smb2_sess_setup_req *req; 143462306a36Sopenharmony_ci unsigned int total_len; 143562306a36Sopenharmony_ci bool is_binding = false; 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci rc = smb2_plain_req_init(SMB2_SESSION_SETUP, NULL, server, 143862306a36Sopenharmony_ci (void **) &req, 143962306a36Sopenharmony_ci &total_len); 144062306a36Sopenharmony_ci if (rc) 144162306a36Sopenharmony_ci return rc; 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci spin_lock(&ses->ses_lock); 144462306a36Sopenharmony_ci is_binding = (ses->ses_status == SES_GOOD); 144562306a36Sopenharmony_ci spin_unlock(&ses->ses_lock); 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci if (is_binding) { 144862306a36Sopenharmony_ci req->hdr.SessionId = cpu_to_le64(ses->Suid); 144962306a36Sopenharmony_ci req->hdr.Flags |= SMB2_FLAGS_SIGNED; 145062306a36Sopenharmony_ci req->PreviousSessionId = 0; 145162306a36Sopenharmony_ci req->Flags = SMB2_SESSION_REQ_FLAG_BINDING; 145262306a36Sopenharmony_ci cifs_dbg(FYI, "Binding to sess id: %llx\n", ses->Suid); 145362306a36Sopenharmony_ci } else { 145462306a36Sopenharmony_ci /* First session, not a reauthenticate */ 145562306a36Sopenharmony_ci req->hdr.SessionId = 0; 145662306a36Sopenharmony_ci /* 145762306a36Sopenharmony_ci * if reconnect, we need to send previous sess id 145862306a36Sopenharmony_ci * otherwise it is 0 145962306a36Sopenharmony_ci */ 146062306a36Sopenharmony_ci req->PreviousSessionId = cpu_to_le64(sess_data->previous_session); 146162306a36Sopenharmony_ci req->Flags = 0; /* MBZ */ 146262306a36Sopenharmony_ci cifs_dbg(FYI, "Fresh session. Previous: %llx\n", 146362306a36Sopenharmony_ci sess_data->previous_session); 146462306a36Sopenharmony_ci } 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci /* enough to enable echos and oplocks and one max size write */ 146762306a36Sopenharmony_ci if (server->credits >= server->max_credits) 146862306a36Sopenharmony_ci req->hdr.CreditRequest = cpu_to_le16(0); 146962306a36Sopenharmony_ci else 147062306a36Sopenharmony_ci req->hdr.CreditRequest = cpu_to_le16( 147162306a36Sopenharmony_ci min_t(int, server->max_credits - 147262306a36Sopenharmony_ci server->credits, 130)); 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci /* only one of SMB2 signing flags may be set in SMB2 request */ 147562306a36Sopenharmony_ci if (server->sign) 147662306a36Sopenharmony_ci req->SecurityMode = SMB2_NEGOTIATE_SIGNING_REQUIRED; 147762306a36Sopenharmony_ci else if (global_secflags & CIFSSEC_MAY_SIGN) /* one flag unlike MUST_ */ 147862306a36Sopenharmony_ci req->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED; 147962306a36Sopenharmony_ci else 148062306a36Sopenharmony_ci req->SecurityMode = 0; 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci#ifdef CONFIG_CIFS_DFS_UPCALL 148362306a36Sopenharmony_ci req->Capabilities = cpu_to_le32(SMB2_GLOBAL_CAP_DFS); 148462306a36Sopenharmony_ci#else 148562306a36Sopenharmony_ci req->Capabilities = 0; 148662306a36Sopenharmony_ci#endif /* DFS_UPCALL */ 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci req->Channel = 0; /* MBZ */ 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci sess_data->iov[0].iov_base = (char *)req; 149162306a36Sopenharmony_ci /* 1 for pad */ 149262306a36Sopenharmony_ci sess_data->iov[0].iov_len = total_len - 1; 149362306a36Sopenharmony_ci /* 149462306a36Sopenharmony_ci * This variable will be used to clear the buffer 149562306a36Sopenharmony_ci * allocated above in case of any error in the calling function. 149662306a36Sopenharmony_ci */ 149762306a36Sopenharmony_ci sess_data->buf0_type = CIFS_SMALL_BUFFER; 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci return 0; 150062306a36Sopenharmony_ci} 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_cistatic void 150362306a36Sopenharmony_ciSMB2_sess_free_buffer(struct SMB2_sess_data *sess_data) 150462306a36Sopenharmony_ci{ 150562306a36Sopenharmony_ci struct kvec *iov = sess_data->iov; 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ci /* iov[1] is already freed by caller */ 150862306a36Sopenharmony_ci if (sess_data->buf0_type != CIFS_NO_BUFFER && iov[0].iov_base) 150962306a36Sopenharmony_ci memzero_explicit(iov[0].iov_base, iov[0].iov_len); 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_ci free_rsp_buf(sess_data->buf0_type, iov[0].iov_base); 151262306a36Sopenharmony_ci sess_data->buf0_type = CIFS_NO_BUFFER; 151362306a36Sopenharmony_ci} 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_cistatic int 151662306a36Sopenharmony_ciSMB2_sess_sendreceive(struct SMB2_sess_data *sess_data) 151762306a36Sopenharmony_ci{ 151862306a36Sopenharmony_ci int rc; 151962306a36Sopenharmony_ci struct smb_rqst rqst; 152062306a36Sopenharmony_ci struct smb2_sess_setup_req *req = sess_data->iov[0].iov_base; 152162306a36Sopenharmony_ci struct kvec rsp_iov = { NULL, 0 }; 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci /* Testing shows that buffer offset must be at location of Buffer[0] */ 152462306a36Sopenharmony_ci req->SecurityBufferOffset = 152562306a36Sopenharmony_ci cpu_to_le16(sizeof(struct smb2_sess_setup_req)); 152662306a36Sopenharmony_ci req->SecurityBufferLength = cpu_to_le16(sess_data->iov[1].iov_len); 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 152962306a36Sopenharmony_ci rqst.rq_iov = sess_data->iov; 153062306a36Sopenharmony_ci rqst.rq_nvec = 2; 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci /* BB add code to build os and lm fields */ 153362306a36Sopenharmony_ci rc = cifs_send_recv(sess_data->xid, sess_data->ses, 153462306a36Sopenharmony_ci sess_data->server, 153562306a36Sopenharmony_ci &rqst, 153662306a36Sopenharmony_ci &sess_data->buf0_type, 153762306a36Sopenharmony_ci CIFS_LOG_ERROR | CIFS_SESS_OP, &rsp_iov); 153862306a36Sopenharmony_ci cifs_small_buf_release(sess_data->iov[0].iov_base); 153962306a36Sopenharmony_ci if (rc == 0) 154062306a36Sopenharmony_ci sess_data->ses->expired_pwd = false; 154162306a36Sopenharmony_ci else if ((rc == -EACCES) || (rc == -EKEYEXPIRED) || (rc == -EKEYREVOKED)) 154262306a36Sopenharmony_ci sess_data->ses->expired_pwd = true; 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_ci memcpy(&sess_data->iov[0], &rsp_iov, sizeof(struct kvec)); 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci return rc; 154762306a36Sopenharmony_ci} 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_cistatic int 155062306a36Sopenharmony_ciSMB2_sess_establish_session(struct SMB2_sess_data *sess_data) 155162306a36Sopenharmony_ci{ 155262306a36Sopenharmony_ci int rc = 0; 155362306a36Sopenharmony_ci struct cifs_ses *ses = sess_data->ses; 155462306a36Sopenharmony_ci struct TCP_Server_Info *server = sess_data->server; 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci cifs_server_lock(server); 155762306a36Sopenharmony_ci if (server->ops->generate_signingkey) { 155862306a36Sopenharmony_ci rc = server->ops->generate_signingkey(ses, server); 155962306a36Sopenharmony_ci if (rc) { 156062306a36Sopenharmony_ci cifs_dbg(FYI, 156162306a36Sopenharmony_ci "SMB3 session key generation failed\n"); 156262306a36Sopenharmony_ci cifs_server_unlock(server); 156362306a36Sopenharmony_ci return rc; 156462306a36Sopenharmony_ci } 156562306a36Sopenharmony_ci } 156662306a36Sopenharmony_ci if (!server->session_estab) { 156762306a36Sopenharmony_ci server->sequence_number = 0x2; 156862306a36Sopenharmony_ci server->session_estab = true; 156962306a36Sopenharmony_ci } 157062306a36Sopenharmony_ci cifs_server_unlock(server); 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci cifs_dbg(FYI, "SMB2/3 session established successfully\n"); 157362306a36Sopenharmony_ci return rc; 157462306a36Sopenharmony_ci} 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci#ifdef CONFIG_CIFS_UPCALL 157762306a36Sopenharmony_cistatic void 157862306a36Sopenharmony_ciSMB2_auth_kerberos(struct SMB2_sess_data *sess_data) 157962306a36Sopenharmony_ci{ 158062306a36Sopenharmony_ci int rc; 158162306a36Sopenharmony_ci struct cifs_ses *ses = sess_data->ses; 158262306a36Sopenharmony_ci struct TCP_Server_Info *server = sess_data->server; 158362306a36Sopenharmony_ci struct cifs_spnego_msg *msg; 158462306a36Sopenharmony_ci struct key *spnego_key = NULL; 158562306a36Sopenharmony_ci struct smb2_sess_setup_rsp *rsp = NULL; 158662306a36Sopenharmony_ci bool is_binding = false; 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci rc = SMB2_sess_alloc_buffer(sess_data); 158962306a36Sopenharmony_ci if (rc) 159062306a36Sopenharmony_ci goto out; 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci spnego_key = cifs_get_spnego_key(ses, server); 159362306a36Sopenharmony_ci if (IS_ERR(spnego_key)) { 159462306a36Sopenharmony_ci rc = PTR_ERR(spnego_key); 159562306a36Sopenharmony_ci if (rc == -ENOKEY) 159662306a36Sopenharmony_ci cifs_dbg(VFS, "Verify user has a krb5 ticket and keyutils is installed\n"); 159762306a36Sopenharmony_ci spnego_key = NULL; 159862306a36Sopenharmony_ci goto out; 159962306a36Sopenharmony_ci } 160062306a36Sopenharmony_ci 160162306a36Sopenharmony_ci msg = spnego_key->payload.data[0]; 160262306a36Sopenharmony_ci /* 160362306a36Sopenharmony_ci * check version field to make sure that cifs.upcall is 160462306a36Sopenharmony_ci * sending us a response in an expected form 160562306a36Sopenharmony_ci */ 160662306a36Sopenharmony_ci if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) { 160762306a36Sopenharmony_ci cifs_dbg(VFS, "bad cifs.upcall version. Expected %d got %d\n", 160862306a36Sopenharmony_ci CIFS_SPNEGO_UPCALL_VERSION, msg->version); 160962306a36Sopenharmony_ci rc = -EKEYREJECTED; 161062306a36Sopenharmony_ci goto out_put_spnego_key; 161162306a36Sopenharmony_ci } 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci spin_lock(&ses->ses_lock); 161462306a36Sopenharmony_ci is_binding = (ses->ses_status == SES_GOOD); 161562306a36Sopenharmony_ci spin_unlock(&ses->ses_lock); 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_ci /* keep session key if binding */ 161862306a36Sopenharmony_ci if (!is_binding) { 161962306a36Sopenharmony_ci kfree_sensitive(ses->auth_key.response); 162062306a36Sopenharmony_ci ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len, 162162306a36Sopenharmony_ci GFP_KERNEL); 162262306a36Sopenharmony_ci if (!ses->auth_key.response) { 162362306a36Sopenharmony_ci cifs_dbg(VFS, "Kerberos can't allocate (%u bytes) memory\n", 162462306a36Sopenharmony_ci msg->sesskey_len); 162562306a36Sopenharmony_ci rc = -ENOMEM; 162662306a36Sopenharmony_ci goto out_put_spnego_key; 162762306a36Sopenharmony_ci } 162862306a36Sopenharmony_ci ses->auth_key.len = msg->sesskey_len; 162962306a36Sopenharmony_ci } 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_ci sess_data->iov[1].iov_base = msg->data + msg->sesskey_len; 163262306a36Sopenharmony_ci sess_data->iov[1].iov_len = msg->secblob_len; 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ci rc = SMB2_sess_sendreceive(sess_data); 163562306a36Sopenharmony_ci if (rc) 163662306a36Sopenharmony_ci goto out_put_spnego_key; 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; 163962306a36Sopenharmony_ci /* keep session id and flags if binding */ 164062306a36Sopenharmony_ci if (!is_binding) { 164162306a36Sopenharmony_ci ses->Suid = le64_to_cpu(rsp->hdr.SessionId); 164262306a36Sopenharmony_ci ses->session_flags = le16_to_cpu(rsp->SessionFlags); 164362306a36Sopenharmony_ci } 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci rc = SMB2_sess_establish_session(sess_data); 164662306a36Sopenharmony_ciout_put_spnego_key: 164762306a36Sopenharmony_ci key_invalidate(spnego_key); 164862306a36Sopenharmony_ci key_put(spnego_key); 164962306a36Sopenharmony_ci if (rc) { 165062306a36Sopenharmony_ci kfree_sensitive(ses->auth_key.response); 165162306a36Sopenharmony_ci ses->auth_key.response = NULL; 165262306a36Sopenharmony_ci ses->auth_key.len = 0; 165362306a36Sopenharmony_ci } 165462306a36Sopenharmony_ciout: 165562306a36Sopenharmony_ci sess_data->result = rc; 165662306a36Sopenharmony_ci sess_data->func = NULL; 165762306a36Sopenharmony_ci SMB2_sess_free_buffer(sess_data); 165862306a36Sopenharmony_ci} 165962306a36Sopenharmony_ci#else 166062306a36Sopenharmony_cistatic void 166162306a36Sopenharmony_ciSMB2_auth_kerberos(struct SMB2_sess_data *sess_data) 166262306a36Sopenharmony_ci{ 166362306a36Sopenharmony_ci cifs_dbg(VFS, "Kerberos negotiated but upcall support disabled!\n"); 166462306a36Sopenharmony_ci sess_data->result = -EOPNOTSUPP; 166562306a36Sopenharmony_ci sess_data->func = NULL; 166662306a36Sopenharmony_ci} 166762306a36Sopenharmony_ci#endif 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_cistatic void 167062306a36Sopenharmony_ciSMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data); 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_cistatic void 167362306a36Sopenharmony_ciSMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data) 167462306a36Sopenharmony_ci{ 167562306a36Sopenharmony_ci int rc; 167662306a36Sopenharmony_ci struct cifs_ses *ses = sess_data->ses; 167762306a36Sopenharmony_ci struct TCP_Server_Info *server = sess_data->server; 167862306a36Sopenharmony_ci struct smb2_sess_setup_rsp *rsp = NULL; 167962306a36Sopenharmony_ci unsigned char *ntlmssp_blob = NULL; 168062306a36Sopenharmony_ci bool use_spnego = false; /* else use raw ntlmssp */ 168162306a36Sopenharmony_ci u16 blob_length = 0; 168262306a36Sopenharmony_ci bool is_binding = false; 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_ci /* 168562306a36Sopenharmony_ci * If memory allocation is successful, caller of this function 168662306a36Sopenharmony_ci * frees it. 168762306a36Sopenharmony_ci */ 168862306a36Sopenharmony_ci ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL); 168962306a36Sopenharmony_ci if (!ses->ntlmssp) { 169062306a36Sopenharmony_ci rc = -ENOMEM; 169162306a36Sopenharmony_ci goto out_err; 169262306a36Sopenharmony_ci } 169362306a36Sopenharmony_ci ses->ntlmssp->sesskey_per_smbsess = true; 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci rc = SMB2_sess_alloc_buffer(sess_data); 169662306a36Sopenharmony_ci if (rc) 169762306a36Sopenharmony_ci goto out_err; 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_ci rc = build_ntlmssp_smb3_negotiate_blob(&ntlmssp_blob, 170062306a36Sopenharmony_ci &blob_length, ses, server, 170162306a36Sopenharmony_ci sess_data->nls_cp); 170262306a36Sopenharmony_ci if (rc) 170362306a36Sopenharmony_ci goto out; 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_ci if (use_spnego) { 170662306a36Sopenharmony_ci /* BB eventually need to add this */ 170762306a36Sopenharmony_ci cifs_dbg(VFS, "spnego not supported for SMB2 yet\n"); 170862306a36Sopenharmony_ci rc = -EOPNOTSUPP; 170962306a36Sopenharmony_ci goto out; 171062306a36Sopenharmony_ci } 171162306a36Sopenharmony_ci sess_data->iov[1].iov_base = ntlmssp_blob; 171262306a36Sopenharmony_ci sess_data->iov[1].iov_len = blob_length; 171362306a36Sopenharmony_ci 171462306a36Sopenharmony_ci rc = SMB2_sess_sendreceive(sess_data); 171562306a36Sopenharmony_ci rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_ci /* If true, rc here is expected and not an error */ 171862306a36Sopenharmony_ci if (sess_data->buf0_type != CIFS_NO_BUFFER && 171962306a36Sopenharmony_ci rsp->hdr.Status == STATUS_MORE_PROCESSING_REQUIRED) 172062306a36Sopenharmony_ci rc = 0; 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ci if (rc) 172362306a36Sopenharmony_ci goto out; 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_ci if (offsetof(struct smb2_sess_setup_rsp, Buffer) != 172662306a36Sopenharmony_ci le16_to_cpu(rsp->SecurityBufferOffset)) { 172762306a36Sopenharmony_ci cifs_dbg(VFS, "Invalid security buffer offset %d\n", 172862306a36Sopenharmony_ci le16_to_cpu(rsp->SecurityBufferOffset)); 172962306a36Sopenharmony_ci rc = -EIO; 173062306a36Sopenharmony_ci goto out; 173162306a36Sopenharmony_ci } 173262306a36Sopenharmony_ci rc = decode_ntlmssp_challenge(rsp->Buffer, 173362306a36Sopenharmony_ci le16_to_cpu(rsp->SecurityBufferLength), ses); 173462306a36Sopenharmony_ci if (rc) 173562306a36Sopenharmony_ci goto out; 173662306a36Sopenharmony_ci 173762306a36Sopenharmony_ci cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n"); 173862306a36Sopenharmony_ci 173962306a36Sopenharmony_ci spin_lock(&ses->ses_lock); 174062306a36Sopenharmony_ci is_binding = (ses->ses_status == SES_GOOD); 174162306a36Sopenharmony_ci spin_unlock(&ses->ses_lock); 174262306a36Sopenharmony_ci 174362306a36Sopenharmony_ci /* keep existing ses id and flags if binding */ 174462306a36Sopenharmony_ci if (!is_binding) { 174562306a36Sopenharmony_ci ses->Suid = le64_to_cpu(rsp->hdr.SessionId); 174662306a36Sopenharmony_ci ses->session_flags = le16_to_cpu(rsp->SessionFlags); 174762306a36Sopenharmony_ci } 174862306a36Sopenharmony_ci 174962306a36Sopenharmony_ciout: 175062306a36Sopenharmony_ci kfree_sensitive(ntlmssp_blob); 175162306a36Sopenharmony_ci SMB2_sess_free_buffer(sess_data); 175262306a36Sopenharmony_ci if (!rc) { 175362306a36Sopenharmony_ci sess_data->result = 0; 175462306a36Sopenharmony_ci sess_data->func = SMB2_sess_auth_rawntlmssp_authenticate; 175562306a36Sopenharmony_ci return; 175662306a36Sopenharmony_ci } 175762306a36Sopenharmony_ciout_err: 175862306a36Sopenharmony_ci kfree_sensitive(ses->ntlmssp); 175962306a36Sopenharmony_ci ses->ntlmssp = NULL; 176062306a36Sopenharmony_ci sess_data->result = rc; 176162306a36Sopenharmony_ci sess_data->func = NULL; 176262306a36Sopenharmony_ci} 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_cistatic void 176562306a36Sopenharmony_ciSMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data) 176662306a36Sopenharmony_ci{ 176762306a36Sopenharmony_ci int rc; 176862306a36Sopenharmony_ci struct cifs_ses *ses = sess_data->ses; 176962306a36Sopenharmony_ci struct TCP_Server_Info *server = sess_data->server; 177062306a36Sopenharmony_ci struct smb2_sess_setup_req *req; 177162306a36Sopenharmony_ci struct smb2_sess_setup_rsp *rsp = NULL; 177262306a36Sopenharmony_ci unsigned char *ntlmssp_blob = NULL; 177362306a36Sopenharmony_ci bool use_spnego = false; /* else use raw ntlmssp */ 177462306a36Sopenharmony_ci u16 blob_length = 0; 177562306a36Sopenharmony_ci bool is_binding = false; 177662306a36Sopenharmony_ci 177762306a36Sopenharmony_ci rc = SMB2_sess_alloc_buffer(sess_data); 177862306a36Sopenharmony_ci if (rc) 177962306a36Sopenharmony_ci goto out; 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci req = (struct smb2_sess_setup_req *) sess_data->iov[0].iov_base; 178262306a36Sopenharmony_ci req->hdr.SessionId = cpu_to_le64(ses->Suid); 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_ci rc = build_ntlmssp_auth_blob(&ntlmssp_blob, &blob_length, 178562306a36Sopenharmony_ci ses, server, 178662306a36Sopenharmony_ci sess_data->nls_cp); 178762306a36Sopenharmony_ci if (rc) { 178862306a36Sopenharmony_ci cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n", rc); 178962306a36Sopenharmony_ci goto out; 179062306a36Sopenharmony_ci } 179162306a36Sopenharmony_ci 179262306a36Sopenharmony_ci if (use_spnego) { 179362306a36Sopenharmony_ci /* BB eventually need to add this */ 179462306a36Sopenharmony_ci cifs_dbg(VFS, "spnego not supported for SMB2 yet\n"); 179562306a36Sopenharmony_ci rc = -EOPNOTSUPP; 179662306a36Sopenharmony_ci goto out; 179762306a36Sopenharmony_ci } 179862306a36Sopenharmony_ci sess_data->iov[1].iov_base = ntlmssp_blob; 179962306a36Sopenharmony_ci sess_data->iov[1].iov_len = blob_length; 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_ci rc = SMB2_sess_sendreceive(sess_data); 180262306a36Sopenharmony_ci if (rc) 180362306a36Sopenharmony_ci goto out; 180462306a36Sopenharmony_ci 180562306a36Sopenharmony_ci rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; 180662306a36Sopenharmony_ci 180762306a36Sopenharmony_ci spin_lock(&ses->ses_lock); 180862306a36Sopenharmony_ci is_binding = (ses->ses_status == SES_GOOD); 180962306a36Sopenharmony_ci spin_unlock(&ses->ses_lock); 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_ci /* keep existing ses id and flags if binding */ 181262306a36Sopenharmony_ci if (!is_binding) { 181362306a36Sopenharmony_ci ses->Suid = le64_to_cpu(rsp->hdr.SessionId); 181462306a36Sopenharmony_ci ses->session_flags = le16_to_cpu(rsp->SessionFlags); 181562306a36Sopenharmony_ci } 181662306a36Sopenharmony_ci 181762306a36Sopenharmony_ci rc = SMB2_sess_establish_session(sess_data); 181862306a36Sopenharmony_ci#ifdef CONFIG_CIFS_DEBUG_DUMP_KEYS 181962306a36Sopenharmony_ci if (ses->server->dialect < SMB30_PROT_ID) { 182062306a36Sopenharmony_ci cifs_dbg(VFS, "%s: dumping generated SMB2 session keys\n", __func__); 182162306a36Sopenharmony_ci /* 182262306a36Sopenharmony_ci * The session id is opaque in terms of endianness, so we can't 182362306a36Sopenharmony_ci * print it as a long long. we dump it as we got it on the wire 182462306a36Sopenharmony_ci */ 182562306a36Sopenharmony_ci cifs_dbg(VFS, "Session Id %*ph\n", (int)sizeof(ses->Suid), 182662306a36Sopenharmony_ci &ses->Suid); 182762306a36Sopenharmony_ci cifs_dbg(VFS, "Session Key %*ph\n", 182862306a36Sopenharmony_ci SMB2_NTLMV2_SESSKEY_SIZE, ses->auth_key.response); 182962306a36Sopenharmony_ci cifs_dbg(VFS, "Signing Key %*ph\n", 183062306a36Sopenharmony_ci SMB3_SIGN_KEY_SIZE, ses->auth_key.response); 183162306a36Sopenharmony_ci } 183262306a36Sopenharmony_ci#endif 183362306a36Sopenharmony_ciout: 183462306a36Sopenharmony_ci kfree_sensitive(ntlmssp_blob); 183562306a36Sopenharmony_ci SMB2_sess_free_buffer(sess_data); 183662306a36Sopenharmony_ci kfree_sensitive(ses->ntlmssp); 183762306a36Sopenharmony_ci ses->ntlmssp = NULL; 183862306a36Sopenharmony_ci sess_data->result = rc; 183962306a36Sopenharmony_ci sess_data->func = NULL; 184062306a36Sopenharmony_ci} 184162306a36Sopenharmony_ci 184262306a36Sopenharmony_cistatic int 184362306a36Sopenharmony_ciSMB2_select_sec(struct SMB2_sess_data *sess_data) 184462306a36Sopenharmony_ci{ 184562306a36Sopenharmony_ci int type; 184662306a36Sopenharmony_ci struct cifs_ses *ses = sess_data->ses; 184762306a36Sopenharmony_ci struct TCP_Server_Info *server = sess_data->server; 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_ci type = smb2_select_sectype(server, ses->sectype); 185062306a36Sopenharmony_ci cifs_dbg(FYI, "sess setup type %d\n", type); 185162306a36Sopenharmony_ci if (type == Unspecified) { 185262306a36Sopenharmony_ci cifs_dbg(VFS, "Unable to select appropriate authentication method!\n"); 185362306a36Sopenharmony_ci return -EINVAL; 185462306a36Sopenharmony_ci } 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_ci switch (type) { 185762306a36Sopenharmony_ci case Kerberos: 185862306a36Sopenharmony_ci sess_data->func = SMB2_auth_kerberos; 185962306a36Sopenharmony_ci break; 186062306a36Sopenharmony_ci case RawNTLMSSP: 186162306a36Sopenharmony_ci sess_data->func = SMB2_sess_auth_rawntlmssp_negotiate; 186262306a36Sopenharmony_ci break; 186362306a36Sopenharmony_ci default: 186462306a36Sopenharmony_ci cifs_dbg(VFS, "secType %d not supported!\n", type); 186562306a36Sopenharmony_ci return -EOPNOTSUPP; 186662306a36Sopenharmony_ci } 186762306a36Sopenharmony_ci 186862306a36Sopenharmony_ci return 0; 186962306a36Sopenharmony_ci} 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_ciint 187262306a36Sopenharmony_ciSMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, 187362306a36Sopenharmony_ci struct TCP_Server_Info *server, 187462306a36Sopenharmony_ci const struct nls_table *nls_cp) 187562306a36Sopenharmony_ci{ 187662306a36Sopenharmony_ci int rc = 0; 187762306a36Sopenharmony_ci struct SMB2_sess_data *sess_data; 187862306a36Sopenharmony_ci 187962306a36Sopenharmony_ci cifs_dbg(FYI, "Session Setup\n"); 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci if (!server) { 188262306a36Sopenharmony_ci WARN(1, "%s: server is NULL!\n", __func__); 188362306a36Sopenharmony_ci return -EIO; 188462306a36Sopenharmony_ci } 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci sess_data = kzalloc(sizeof(struct SMB2_sess_data), GFP_KERNEL); 188762306a36Sopenharmony_ci if (!sess_data) 188862306a36Sopenharmony_ci return -ENOMEM; 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_ci sess_data->xid = xid; 189162306a36Sopenharmony_ci sess_data->ses = ses; 189262306a36Sopenharmony_ci sess_data->server = server; 189362306a36Sopenharmony_ci sess_data->buf0_type = CIFS_NO_BUFFER; 189462306a36Sopenharmony_ci sess_data->nls_cp = (struct nls_table *) nls_cp; 189562306a36Sopenharmony_ci sess_data->previous_session = ses->Suid; 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_ci rc = SMB2_select_sec(sess_data); 189862306a36Sopenharmony_ci if (rc) 189962306a36Sopenharmony_ci goto out; 190062306a36Sopenharmony_ci 190162306a36Sopenharmony_ci /* 190262306a36Sopenharmony_ci * Initialize the session hash with the server one. 190362306a36Sopenharmony_ci */ 190462306a36Sopenharmony_ci memcpy(ses->preauth_sha_hash, server->preauth_sha_hash, 190562306a36Sopenharmony_ci SMB2_PREAUTH_HASH_SIZE); 190662306a36Sopenharmony_ci 190762306a36Sopenharmony_ci while (sess_data->func) 190862306a36Sopenharmony_ci sess_data->func(sess_data); 190962306a36Sopenharmony_ci 191062306a36Sopenharmony_ci if ((ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST) && (ses->sign)) 191162306a36Sopenharmony_ci cifs_server_dbg(VFS, "signing requested but authenticated as guest\n"); 191262306a36Sopenharmony_ci rc = sess_data->result; 191362306a36Sopenharmony_ciout: 191462306a36Sopenharmony_ci kfree_sensitive(sess_data); 191562306a36Sopenharmony_ci return rc; 191662306a36Sopenharmony_ci} 191762306a36Sopenharmony_ci 191862306a36Sopenharmony_ciint 191962306a36Sopenharmony_ciSMB2_logoff(const unsigned int xid, struct cifs_ses *ses) 192062306a36Sopenharmony_ci{ 192162306a36Sopenharmony_ci struct smb_rqst rqst; 192262306a36Sopenharmony_ci struct smb2_logoff_req *req; /* response is also trivial struct */ 192362306a36Sopenharmony_ci int rc = 0; 192462306a36Sopenharmony_ci struct TCP_Server_Info *server; 192562306a36Sopenharmony_ci int flags = 0; 192662306a36Sopenharmony_ci unsigned int total_len; 192762306a36Sopenharmony_ci struct kvec iov[1]; 192862306a36Sopenharmony_ci struct kvec rsp_iov; 192962306a36Sopenharmony_ci int resp_buf_type; 193062306a36Sopenharmony_ci 193162306a36Sopenharmony_ci cifs_dbg(FYI, "disconnect session %p\n", ses); 193262306a36Sopenharmony_ci 193362306a36Sopenharmony_ci if (ses && (ses->server)) 193462306a36Sopenharmony_ci server = ses->server; 193562306a36Sopenharmony_ci else 193662306a36Sopenharmony_ci return -EIO; 193762306a36Sopenharmony_ci 193862306a36Sopenharmony_ci /* no need to send SMB logoff if uid already closed due to reconnect */ 193962306a36Sopenharmony_ci spin_lock(&ses->chan_lock); 194062306a36Sopenharmony_ci if (CIFS_ALL_CHANS_NEED_RECONNECT(ses)) { 194162306a36Sopenharmony_ci spin_unlock(&ses->chan_lock); 194262306a36Sopenharmony_ci goto smb2_session_already_dead; 194362306a36Sopenharmony_ci } 194462306a36Sopenharmony_ci spin_unlock(&ses->chan_lock); 194562306a36Sopenharmony_ci 194662306a36Sopenharmony_ci rc = smb2_plain_req_init(SMB2_LOGOFF, NULL, ses->server, 194762306a36Sopenharmony_ci (void **) &req, &total_len); 194862306a36Sopenharmony_ci if (rc) 194962306a36Sopenharmony_ci return rc; 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_ci /* since no tcon, smb2_init can not do this, so do here */ 195262306a36Sopenharmony_ci req->hdr.SessionId = cpu_to_le64(ses->Suid); 195362306a36Sopenharmony_ci 195462306a36Sopenharmony_ci if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) 195562306a36Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 195662306a36Sopenharmony_ci else if (server->sign) 195762306a36Sopenharmony_ci req->hdr.Flags |= SMB2_FLAGS_SIGNED; 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_ci flags |= CIFS_NO_RSP_BUF; 196062306a36Sopenharmony_ci 196162306a36Sopenharmony_ci iov[0].iov_base = (char *)req; 196262306a36Sopenharmony_ci iov[0].iov_len = total_len; 196362306a36Sopenharmony_ci 196462306a36Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 196562306a36Sopenharmony_ci rqst.rq_iov = iov; 196662306a36Sopenharmony_ci rqst.rq_nvec = 1; 196762306a36Sopenharmony_ci 196862306a36Sopenharmony_ci rc = cifs_send_recv(xid, ses, ses->server, 196962306a36Sopenharmony_ci &rqst, &resp_buf_type, flags, &rsp_iov); 197062306a36Sopenharmony_ci cifs_small_buf_release(req); 197162306a36Sopenharmony_ci /* 197262306a36Sopenharmony_ci * No tcon so can't do 197362306a36Sopenharmony_ci * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]); 197462306a36Sopenharmony_ci */ 197562306a36Sopenharmony_ci 197662306a36Sopenharmony_cismb2_session_already_dead: 197762306a36Sopenharmony_ci return rc; 197862306a36Sopenharmony_ci} 197962306a36Sopenharmony_ci 198062306a36Sopenharmony_cistatic inline void cifs_stats_fail_inc(struct cifs_tcon *tcon, uint16_t code) 198162306a36Sopenharmony_ci{ 198262306a36Sopenharmony_ci cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_failed[code]); 198362306a36Sopenharmony_ci} 198462306a36Sopenharmony_ci 198562306a36Sopenharmony_ci#define MAX_SHARENAME_LENGTH (255 /* server */ + 80 /* share */ + 1 /* NULL */) 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci/* These are similar values to what Windows uses */ 198862306a36Sopenharmony_cistatic inline void init_copy_chunk_defaults(struct cifs_tcon *tcon) 198962306a36Sopenharmony_ci{ 199062306a36Sopenharmony_ci tcon->max_chunks = 256; 199162306a36Sopenharmony_ci tcon->max_bytes_chunk = 1048576; 199262306a36Sopenharmony_ci tcon->max_bytes_copy = 16777216; 199362306a36Sopenharmony_ci} 199462306a36Sopenharmony_ci 199562306a36Sopenharmony_ciint 199662306a36Sopenharmony_ciSMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, 199762306a36Sopenharmony_ci struct cifs_tcon *tcon, const struct nls_table *cp) 199862306a36Sopenharmony_ci{ 199962306a36Sopenharmony_ci struct smb_rqst rqst; 200062306a36Sopenharmony_ci struct smb2_tree_connect_req *req; 200162306a36Sopenharmony_ci struct smb2_tree_connect_rsp *rsp = NULL; 200262306a36Sopenharmony_ci struct kvec iov[2]; 200362306a36Sopenharmony_ci struct kvec rsp_iov = { NULL, 0 }; 200462306a36Sopenharmony_ci int rc = 0; 200562306a36Sopenharmony_ci int resp_buftype; 200662306a36Sopenharmony_ci int unc_path_len; 200762306a36Sopenharmony_ci __le16 *unc_path = NULL; 200862306a36Sopenharmony_ci int flags = 0; 200962306a36Sopenharmony_ci unsigned int total_len; 201062306a36Sopenharmony_ci struct TCP_Server_Info *server; 201162306a36Sopenharmony_ci 201262306a36Sopenharmony_ci /* always use master channel */ 201362306a36Sopenharmony_ci server = ses->server; 201462306a36Sopenharmony_ci 201562306a36Sopenharmony_ci cifs_dbg(FYI, "TCON\n"); 201662306a36Sopenharmony_ci 201762306a36Sopenharmony_ci if (!server || !tree) 201862306a36Sopenharmony_ci return -EIO; 201962306a36Sopenharmony_ci 202062306a36Sopenharmony_ci unc_path = kmalloc(MAX_SHARENAME_LENGTH * 2, GFP_KERNEL); 202162306a36Sopenharmony_ci if (unc_path == NULL) 202262306a36Sopenharmony_ci return -ENOMEM; 202362306a36Sopenharmony_ci 202462306a36Sopenharmony_ci unc_path_len = cifs_strtoUTF16(unc_path, tree, strlen(tree), cp); 202562306a36Sopenharmony_ci if (unc_path_len <= 0) { 202662306a36Sopenharmony_ci kfree(unc_path); 202762306a36Sopenharmony_ci return -EINVAL; 202862306a36Sopenharmony_ci } 202962306a36Sopenharmony_ci unc_path_len *= 2; 203062306a36Sopenharmony_ci 203162306a36Sopenharmony_ci /* SMB2 TREE_CONNECT request must be called with TreeId == 0 */ 203262306a36Sopenharmony_ci tcon->tid = 0; 203362306a36Sopenharmony_ci atomic_set(&tcon->num_remote_opens, 0); 203462306a36Sopenharmony_ci rc = smb2_plain_req_init(SMB2_TREE_CONNECT, tcon, server, 203562306a36Sopenharmony_ci (void **) &req, &total_len); 203662306a36Sopenharmony_ci if (rc) { 203762306a36Sopenharmony_ci kfree(unc_path); 203862306a36Sopenharmony_ci return rc; 203962306a36Sopenharmony_ci } 204062306a36Sopenharmony_ci 204162306a36Sopenharmony_ci if (smb3_encryption_required(tcon)) 204262306a36Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 204362306a36Sopenharmony_ci 204462306a36Sopenharmony_ci iov[0].iov_base = (char *)req; 204562306a36Sopenharmony_ci /* 1 for pad */ 204662306a36Sopenharmony_ci iov[0].iov_len = total_len - 1; 204762306a36Sopenharmony_ci 204862306a36Sopenharmony_ci /* Testing shows that buffer offset must be at location of Buffer[0] */ 204962306a36Sopenharmony_ci req->PathOffset = cpu_to_le16(sizeof(struct smb2_tree_connect_req)); 205062306a36Sopenharmony_ci req->PathLength = cpu_to_le16(unc_path_len); 205162306a36Sopenharmony_ci iov[1].iov_base = unc_path; 205262306a36Sopenharmony_ci iov[1].iov_len = unc_path_len; 205362306a36Sopenharmony_ci 205462306a36Sopenharmony_ci /* 205562306a36Sopenharmony_ci * 3.11 tcon req must be signed if not encrypted. See MS-SMB2 3.2.4.1.1 205662306a36Sopenharmony_ci * unless it is guest or anonymous user. See MS-SMB2 3.2.5.3.1 205762306a36Sopenharmony_ci * (Samba servers don't always set the flag so also check if null user) 205862306a36Sopenharmony_ci */ 205962306a36Sopenharmony_ci if ((server->dialect == SMB311_PROT_ID) && 206062306a36Sopenharmony_ci !smb3_encryption_required(tcon) && 206162306a36Sopenharmony_ci !(ses->session_flags & 206262306a36Sopenharmony_ci (SMB2_SESSION_FLAG_IS_GUEST|SMB2_SESSION_FLAG_IS_NULL)) && 206362306a36Sopenharmony_ci ((ses->user_name != NULL) || (ses->sectype == Kerberos))) 206462306a36Sopenharmony_ci req->hdr.Flags |= SMB2_FLAGS_SIGNED; 206562306a36Sopenharmony_ci 206662306a36Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 206762306a36Sopenharmony_ci rqst.rq_iov = iov; 206862306a36Sopenharmony_ci rqst.rq_nvec = 2; 206962306a36Sopenharmony_ci 207062306a36Sopenharmony_ci /* Need 64 for max size write so ask for more in case not there yet */ 207162306a36Sopenharmony_ci if (server->credits >= server->max_credits) 207262306a36Sopenharmony_ci req->hdr.CreditRequest = cpu_to_le16(0); 207362306a36Sopenharmony_ci else 207462306a36Sopenharmony_ci req->hdr.CreditRequest = cpu_to_le16( 207562306a36Sopenharmony_ci min_t(int, server->max_credits - 207662306a36Sopenharmony_ci server->credits, 64)); 207762306a36Sopenharmony_ci 207862306a36Sopenharmony_ci rc = cifs_send_recv(xid, ses, server, 207962306a36Sopenharmony_ci &rqst, &resp_buftype, flags, &rsp_iov); 208062306a36Sopenharmony_ci cifs_small_buf_release(req); 208162306a36Sopenharmony_ci rsp = (struct smb2_tree_connect_rsp *)rsp_iov.iov_base; 208262306a36Sopenharmony_ci trace_smb3_tcon(xid, tcon->tid, ses->Suid, tree, rc); 208362306a36Sopenharmony_ci if ((rc != 0) || (rsp == NULL)) { 208462306a36Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_TREE_CONNECT_HE); 208562306a36Sopenharmony_ci tcon->need_reconnect = true; 208662306a36Sopenharmony_ci goto tcon_error_exit; 208762306a36Sopenharmony_ci } 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_ci switch (rsp->ShareType) { 209062306a36Sopenharmony_ci case SMB2_SHARE_TYPE_DISK: 209162306a36Sopenharmony_ci cifs_dbg(FYI, "connection to disk share\n"); 209262306a36Sopenharmony_ci break; 209362306a36Sopenharmony_ci case SMB2_SHARE_TYPE_PIPE: 209462306a36Sopenharmony_ci tcon->pipe = true; 209562306a36Sopenharmony_ci cifs_dbg(FYI, "connection to pipe share\n"); 209662306a36Sopenharmony_ci break; 209762306a36Sopenharmony_ci case SMB2_SHARE_TYPE_PRINT: 209862306a36Sopenharmony_ci tcon->print = true; 209962306a36Sopenharmony_ci cifs_dbg(FYI, "connection to printer\n"); 210062306a36Sopenharmony_ci break; 210162306a36Sopenharmony_ci default: 210262306a36Sopenharmony_ci cifs_server_dbg(VFS, "unknown share type %d\n", rsp->ShareType); 210362306a36Sopenharmony_ci rc = -EOPNOTSUPP; 210462306a36Sopenharmony_ci goto tcon_error_exit; 210562306a36Sopenharmony_ci } 210662306a36Sopenharmony_ci 210762306a36Sopenharmony_ci tcon->share_flags = le32_to_cpu(rsp->ShareFlags); 210862306a36Sopenharmony_ci tcon->capabilities = rsp->Capabilities; /* we keep caps little endian */ 210962306a36Sopenharmony_ci tcon->maximal_access = le32_to_cpu(rsp->MaximalAccess); 211062306a36Sopenharmony_ci tcon->tid = le32_to_cpu(rsp->hdr.Id.SyncId.TreeId); 211162306a36Sopenharmony_ci strscpy(tcon->tree_name, tree, sizeof(tcon->tree_name)); 211262306a36Sopenharmony_ci 211362306a36Sopenharmony_ci if ((rsp->Capabilities & SMB2_SHARE_CAP_DFS) && 211462306a36Sopenharmony_ci ((tcon->share_flags & SHI1005_FLAGS_DFS) == 0)) 211562306a36Sopenharmony_ci cifs_tcon_dbg(VFS, "DFS capability contradicts DFS flag\n"); 211662306a36Sopenharmony_ci 211762306a36Sopenharmony_ci if (tcon->seal && 211862306a36Sopenharmony_ci !(server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) 211962306a36Sopenharmony_ci cifs_tcon_dbg(VFS, "Encryption is requested but not supported\n"); 212062306a36Sopenharmony_ci 212162306a36Sopenharmony_ci init_copy_chunk_defaults(tcon); 212262306a36Sopenharmony_ci if (server->ops->validate_negotiate) 212362306a36Sopenharmony_ci rc = server->ops->validate_negotiate(xid, tcon); 212462306a36Sopenharmony_ci if (rc == 0) /* See MS-SMB2 2.2.10 and 3.2.5.5 */ 212562306a36Sopenharmony_ci if (tcon->share_flags & SMB2_SHAREFLAG_ISOLATED_TRANSPORT) 212662306a36Sopenharmony_ci server->nosharesock = true; 212762306a36Sopenharmony_citcon_exit: 212862306a36Sopenharmony_ci 212962306a36Sopenharmony_ci free_rsp_buf(resp_buftype, rsp); 213062306a36Sopenharmony_ci kfree(unc_path); 213162306a36Sopenharmony_ci return rc; 213262306a36Sopenharmony_ci 213362306a36Sopenharmony_citcon_error_exit: 213462306a36Sopenharmony_ci if (rsp && rsp->hdr.Status == STATUS_BAD_NETWORK_NAME) 213562306a36Sopenharmony_ci cifs_tcon_dbg(VFS, "BAD_NETWORK_NAME: %s\n", tree); 213662306a36Sopenharmony_ci goto tcon_exit; 213762306a36Sopenharmony_ci} 213862306a36Sopenharmony_ci 213962306a36Sopenharmony_ciint 214062306a36Sopenharmony_ciSMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon) 214162306a36Sopenharmony_ci{ 214262306a36Sopenharmony_ci struct smb_rqst rqst; 214362306a36Sopenharmony_ci struct smb2_tree_disconnect_req *req; /* response is trivial */ 214462306a36Sopenharmony_ci int rc = 0; 214562306a36Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 214662306a36Sopenharmony_ci int flags = 0; 214762306a36Sopenharmony_ci unsigned int total_len; 214862306a36Sopenharmony_ci struct kvec iov[1]; 214962306a36Sopenharmony_ci struct kvec rsp_iov; 215062306a36Sopenharmony_ci int resp_buf_type; 215162306a36Sopenharmony_ci 215262306a36Sopenharmony_ci cifs_dbg(FYI, "Tree Disconnect\n"); 215362306a36Sopenharmony_ci 215462306a36Sopenharmony_ci if (!ses || !(ses->server)) 215562306a36Sopenharmony_ci return -EIO; 215662306a36Sopenharmony_ci 215762306a36Sopenharmony_ci trace_smb3_tdis_enter(xid, tcon->tid, ses->Suid, tcon->tree_name); 215862306a36Sopenharmony_ci spin_lock(&ses->chan_lock); 215962306a36Sopenharmony_ci if ((tcon->need_reconnect) || 216062306a36Sopenharmony_ci (CIFS_ALL_CHANS_NEED_RECONNECT(tcon->ses))) { 216162306a36Sopenharmony_ci spin_unlock(&ses->chan_lock); 216262306a36Sopenharmony_ci return 0; 216362306a36Sopenharmony_ci } 216462306a36Sopenharmony_ci spin_unlock(&ses->chan_lock); 216562306a36Sopenharmony_ci 216662306a36Sopenharmony_ci invalidate_all_cached_dirs(tcon); 216762306a36Sopenharmony_ci 216862306a36Sopenharmony_ci rc = smb2_plain_req_init(SMB2_TREE_DISCONNECT, tcon, ses->server, 216962306a36Sopenharmony_ci (void **) &req, 217062306a36Sopenharmony_ci &total_len); 217162306a36Sopenharmony_ci if (rc) 217262306a36Sopenharmony_ci return rc; 217362306a36Sopenharmony_ci 217462306a36Sopenharmony_ci if (smb3_encryption_required(tcon)) 217562306a36Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 217662306a36Sopenharmony_ci 217762306a36Sopenharmony_ci flags |= CIFS_NO_RSP_BUF; 217862306a36Sopenharmony_ci 217962306a36Sopenharmony_ci iov[0].iov_base = (char *)req; 218062306a36Sopenharmony_ci iov[0].iov_len = total_len; 218162306a36Sopenharmony_ci 218262306a36Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 218362306a36Sopenharmony_ci rqst.rq_iov = iov; 218462306a36Sopenharmony_ci rqst.rq_nvec = 1; 218562306a36Sopenharmony_ci 218662306a36Sopenharmony_ci rc = cifs_send_recv(xid, ses, ses->server, 218762306a36Sopenharmony_ci &rqst, &resp_buf_type, flags, &rsp_iov); 218862306a36Sopenharmony_ci cifs_small_buf_release(req); 218962306a36Sopenharmony_ci if (rc) { 219062306a36Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_TREE_DISCONNECT_HE); 219162306a36Sopenharmony_ci trace_smb3_tdis_err(xid, tcon->tid, ses->Suid, rc); 219262306a36Sopenharmony_ci } 219362306a36Sopenharmony_ci trace_smb3_tdis_done(xid, tcon->tid, ses->Suid); 219462306a36Sopenharmony_ci 219562306a36Sopenharmony_ci return rc; 219662306a36Sopenharmony_ci} 219762306a36Sopenharmony_ci 219862306a36Sopenharmony_ci 219962306a36Sopenharmony_cistatic struct create_durable * 220062306a36Sopenharmony_cicreate_durable_buf(void) 220162306a36Sopenharmony_ci{ 220262306a36Sopenharmony_ci struct create_durable *buf; 220362306a36Sopenharmony_ci 220462306a36Sopenharmony_ci buf = kzalloc(sizeof(struct create_durable), GFP_KERNEL); 220562306a36Sopenharmony_ci if (!buf) 220662306a36Sopenharmony_ci return NULL; 220762306a36Sopenharmony_ci 220862306a36Sopenharmony_ci buf->ccontext.DataOffset = cpu_to_le16(offsetof 220962306a36Sopenharmony_ci (struct create_durable, Data)); 221062306a36Sopenharmony_ci buf->ccontext.DataLength = cpu_to_le32(16); 221162306a36Sopenharmony_ci buf->ccontext.NameOffset = cpu_to_le16(offsetof 221262306a36Sopenharmony_ci (struct create_durable, Name)); 221362306a36Sopenharmony_ci buf->ccontext.NameLength = cpu_to_le16(4); 221462306a36Sopenharmony_ci /* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DHnQ" */ 221562306a36Sopenharmony_ci buf->Name[0] = 'D'; 221662306a36Sopenharmony_ci buf->Name[1] = 'H'; 221762306a36Sopenharmony_ci buf->Name[2] = 'n'; 221862306a36Sopenharmony_ci buf->Name[3] = 'Q'; 221962306a36Sopenharmony_ci return buf; 222062306a36Sopenharmony_ci} 222162306a36Sopenharmony_ci 222262306a36Sopenharmony_cistatic struct create_durable * 222362306a36Sopenharmony_cicreate_reconnect_durable_buf(struct cifs_fid *fid) 222462306a36Sopenharmony_ci{ 222562306a36Sopenharmony_ci struct create_durable *buf; 222662306a36Sopenharmony_ci 222762306a36Sopenharmony_ci buf = kzalloc(sizeof(struct create_durable), GFP_KERNEL); 222862306a36Sopenharmony_ci if (!buf) 222962306a36Sopenharmony_ci return NULL; 223062306a36Sopenharmony_ci 223162306a36Sopenharmony_ci buf->ccontext.DataOffset = cpu_to_le16(offsetof 223262306a36Sopenharmony_ci (struct create_durable, Data)); 223362306a36Sopenharmony_ci buf->ccontext.DataLength = cpu_to_le32(16); 223462306a36Sopenharmony_ci buf->ccontext.NameOffset = cpu_to_le16(offsetof 223562306a36Sopenharmony_ci (struct create_durable, Name)); 223662306a36Sopenharmony_ci buf->ccontext.NameLength = cpu_to_le16(4); 223762306a36Sopenharmony_ci buf->Data.Fid.PersistentFileId = fid->persistent_fid; 223862306a36Sopenharmony_ci buf->Data.Fid.VolatileFileId = fid->volatile_fid; 223962306a36Sopenharmony_ci /* SMB2_CREATE_DURABLE_HANDLE_RECONNECT is "DHnC" */ 224062306a36Sopenharmony_ci buf->Name[0] = 'D'; 224162306a36Sopenharmony_ci buf->Name[1] = 'H'; 224262306a36Sopenharmony_ci buf->Name[2] = 'n'; 224362306a36Sopenharmony_ci buf->Name[3] = 'C'; 224462306a36Sopenharmony_ci return buf; 224562306a36Sopenharmony_ci} 224662306a36Sopenharmony_ci 224762306a36Sopenharmony_cistatic void 224862306a36Sopenharmony_ciparse_query_id_ctxt(struct create_context *cc, struct smb2_file_all_info *buf) 224962306a36Sopenharmony_ci{ 225062306a36Sopenharmony_ci struct create_disk_id_rsp *pdisk_id = (struct create_disk_id_rsp *)cc; 225162306a36Sopenharmony_ci 225262306a36Sopenharmony_ci cifs_dbg(FYI, "parse query id context 0x%llx 0x%llx\n", 225362306a36Sopenharmony_ci pdisk_id->DiskFileId, pdisk_id->VolumeId); 225462306a36Sopenharmony_ci buf->IndexNumber = pdisk_id->DiskFileId; 225562306a36Sopenharmony_ci} 225662306a36Sopenharmony_ci 225762306a36Sopenharmony_cistatic void 225862306a36Sopenharmony_ciparse_posix_ctxt(struct create_context *cc, struct smb2_file_all_info *info, 225962306a36Sopenharmony_ci struct create_posix_rsp *posix) 226062306a36Sopenharmony_ci{ 226162306a36Sopenharmony_ci int sid_len; 226262306a36Sopenharmony_ci u8 *beg = (u8 *)cc + le16_to_cpu(cc->DataOffset); 226362306a36Sopenharmony_ci u8 *end = beg + le32_to_cpu(cc->DataLength); 226462306a36Sopenharmony_ci u8 *sid; 226562306a36Sopenharmony_ci 226662306a36Sopenharmony_ci memset(posix, 0, sizeof(*posix)); 226762306a36Sopenharmony_ci 226862306a36Sopenharmony_ci posix->nlink = le32_to_cpu(*(__le32 *)(beg + 0)); 226962306a36Sopenharmony_ci posix->reparse_tag = le32_to_cpu(*(__le32 *)(beg + 4)); 227062306a36Sopenharmony_ci posix->mode = le32_to_cpu(*(__le32 *)(beg + 8)); 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_ci sid = beg + 12; 227362306a36Sopenharmony_ci sid_len = posix_info_sid_size(sid, end); 227462306a36Sopenharmony_ci if (sid_len < 0) { 227562306a36Sopenharmony_ci cifs_dbg(VFS, "bad owner sid in posix create response\n"); 227662306a36Sopenharmony_ci return; 227762306a36Sopenharmony_ci } 227862306a36Sopenharmony_ci memcpy(&posix->owner, sid, sid_len); 227962306a36Sopenharmony_ci 228062306a36Sopenharmony_ci sid = sid + sid_len; 228162306a36Sopenharmony_ci sid_len = posix_info_sid_size(sid, end); 228262306a36Sopenharmony_ci if (sid_len < 0) { 228362306a36Sopenharmony_ci cifs_dbg(VFS, "bad group sid in posix create response\n"); 228462306a36Sopenharmony_ci return; 228562306a36Sopenharmony_ci } 228662306a36Sopenharmony_ci memcpy(&posix->group, sid, sid_len); 228762306a36Sopenharmony_ci 228862306a36Sopenharmony_ci cifs_dbg(FYI, "nlink=%d mode=%o reparse_tag=%x\n", 228962306a36Sopenharmony_ci posix->nlink, posix->mode, posix->reparse_tag); 229062306a36Sopenharmony_ci} 229162306a36Sopenharmony_ci 229262306a36Sopenharmony_ciint smb2_parse_contexts(struct TCP_Server_Info *server, 229362306a36Sopenharmony_ci struct kvec *rsp_iov, 229462306a36Sopenharmony_ci unsigned int *epoch, 229562306a36Sopenharmony_ci char *lease_key, __u8 *oplock, 229662306a36Sopenharmony_ci struct smb2_file_all_info *buf, 229762306a36Sopenharmony_ci struct create_posix_rsp *posix) 229862306a36Sopenharmony_ci{ 229962306a36Sopenharmony_ci struct smb2_create_rsp *rsp = rsp_iov->iov_base; 230062306a36Sopenharmony_ci struct create_context *cc; 230162306a36Sopenharmony_ci size_t rem, off, len; 230262306a36Sopenharmony_ci size_t doff, dlen; 230362306a36Sopenharmony_ci size_t noff, nlen; 230462306a36Sopenharmony_ci char *name; 230562306a36Sopenharmony_ci static const char smb3_create_tag_posix[] = { 230662306a36Sopenharmony_ci 0x93, 0xAD, 0x25, 0x50, 0x9C, 230762306a36Sopenharmony_ci 0xB4, 0x11, 0xE7, 0xB4, 0x23, 0x83, 230862306a36Sopenharmony_ci 0xDE, 0x96, 0x8B, 0xCD, 0x7C 230962306a36Sopenharmony_ci }; 231062306a36Sopenharmony_ci 231162306a36Sopenharmony_ci *oplock = 0; 231262306a36Sopenharmony_ci 231362306a36Sopenharmony_ci off = le32_to_cpu(rsp->CreateContextsOffset); 231462306a36Sopenharmony_ci rem = le32_to_cpu(rsp->CreateContextsLength); 231562306a36Sopenharmony_ci if (check_add_overflow(off, rem, &len) || len > rsp_iov->iov_len) 231662306a36Sopenharmony_ci return -EINVAL; 231762306a36Sopenharmony_ci cc = (struct create_context *)((u8 *)rsp + off); 231862306a36Sopenharmony_ci 231962306a36Sopenharmony_ci /* Initialize inode number to 0 in case no valid data in qfid context */ 232062306a36Sopenharmony_ci if (buf) 232162306a36Sopenharmony_ci buf->IndexNumber = 0; 232262306a36Sopenharmony_ci 232362306a36Sopenharmony_ci while (rem >= sizeof(*cc)) { 232462306a36Sopenharmony_ci doff = le16_to_cpu(cc->DataOffset); 232562306a36Sopenharmony_ci dlen = le32_to_cpu(cc->DataLength); 232662306a36Sopenharmony_ci if (check_add_overflow(doff, dlen, &len) || len > rem) 232762306a36Sopenharmony_ci return -EINVAL; 232862306a36Sopenharmony_ci 232962306a36Sopenharmony_ci noff = le16_to_cpu(cc->NameOffset); 233062306a36Sopenharmony_ci nlen = le16_to_cpu(cc->NameLength); 233162306a36Sopenharmony_ci if (noff + nlen > doff) 233262306a36Sopenharmony_ci return -EINVAL; 233362306a36Sopenharmony_ci 233462306a36Sopenharmony_ci name = (char *)cc + noff; 233562306a36Sopenharmony_ci switch (nlen) { 233662306a36Sopenharmony_ci case 4: 233762306a36Sopenharmony_ci if (!strncmp(name, SMB2_CREATE_REQUEST_LEASE, 4)) { 233862306a36Sopenharmony_ci *oplock = server->ops->parse_lease_buf(cc, epoch, 233962306a36Sopenharmony_ci lease_key); 234062306a36Sopenharmony_ci } else if (buf && 234162306a36Sopenharmony_ci !strncmp(name, SMB2_CREATE_QUERY_ON_DISK_ID, 4)) { 234262306a36Sopenharmony_ci parse_query_id_ctxt(cc, buf); 234362306a36Sopenharmony_ci } 234462306a36Sopenharmony_ci break; 234562306a36Sopenharmony_ci case 16: 234662306a36Sopenharmony_ci if (posix && !memcmp(name, smb3_create_tag_posix, 16)) 234762306a36Sopenharmony_ci parse_posix_ctxt(cc, buf, posix); 234862306a36Sopenharmony_ci break; 234962306a36Sopenharmony_ci default: 235062306a36Sopenharmony_ci cifs_dbg(FYI, "%s: unhandled context (nlen=%zu dlen=%zu)\n", 235162306a36Sopenharmony_ci __func__, nlen, dlen); 235262306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_CIFS_DEBUG2)) 235362306a36Sopenharmony_ci cifs_dump_mem("context data: ", cc, dlen); 235462306a36Sopenharmony_ci break; 235562306a36Sopenharmony_ci } 235662306a36Sopenharmony_ci 235762306a36Sopenharmony_ci off = le32_to_cpu(cc->Next); 235862306a36Sopenharmony_ci if (!off) 235962306a36Sopenharmony_ci break; 236062306a36Sopenharmony_ci if (check_sub_overflow(rem, off, &rem)) 236162306a36Sopenharmony_ci return -EINVAL; 236262306a36Sopenharmony_ci cc = (struct create_context *)((u8 *)cc + off); 236362306a36Sopenharmony_ci } 236462306a36Sopenharmony_ci 236562306a36Sopenharmony_ci if (rsp->OplockLevel != SMB2_OPLOCK_LEVEL_LEASE) 236662306a36Sopenharmony_ci *oplock = rsp->OplockLevel; 236762306a36Sopenharmony_ci 236862306a36Sopenharmony_ci return 0; 236962306a36Sopenharmony_ci} 237062306a36Sopenharmony_ci 237162306a36Sopenharmony_cistatic int 237262306a36Sopenharmony_ciadd_lease_context(struct TCP_Server_Info *server, 237362306a36Sopenharmony_ci struct smb2_create_req *req, 237462306a36Sopenharmony_ci struct kvec *iov, 237562306a36Sopenharmony_ci unsigned int *num_iovec, u8 *lease_key, __u8 *oplock) 237662306a36Sopenharmony_ci{ 237762306a36Sopenharmony_ci unsigned int num = *num_iovec; 237862306a36Sopenharmony_ci 237962306a36Sopenharmony_ci iov[num].iov_base = server->ops->create_lease_buf(lease_key, *oplock); 238062306a36Sopenharmony_ci if (iov[num].iov_base == NULL) 238162306a36Sopenharmony_ci return -ENOMEM; 238262306a36Sopenharmony_ci iov[num].iov_len = server->vals->create_lease_size; 238362306a36Sopenharmony_ci req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_LEASE; 238462306a36Sopenharmony_ci *num_iovec = num + 1; 238562306a36Sopenharmony_ci return 0; 238662306a36Sopenharmony_ci} 238762306a36Sopenharmony_ci 238862306a36Sopenharmony_cistatic struct create_durable_v2 * 238962306a36Sopenharmony_cicreate_durable_v2_buf(struct cifs_open_parms *oparms) 239062306a36Sopenharmony_ci{ 239162306a36Sopenharmony_ci struct cifs_fid *pfid = oparms->fid; 239262306a36Sopenharmony_ci struct create_durable_v2 *buf; 239362306a36Sopenharmony_ci 239462306a36Sopenharmony_ci buf = kzalloc(sizeof(struct create_durable_v2), GFP_KERNEL); 239562306a36Sopenharmony_ci if (!buf) 239662306a36Sopenharmony_ci return NULL; 239762306a36Sopenharmony_ci 239862306a36Sopenharmony_ci buf->ccontext.DataOffset = cpu_to_le16(offsetof 239962306a36Sopenharmony_ci (struct create_durable_v2, dcontext)); 240062306a36Sopenharmony_ci buf->ccontext.DataLength = cpu_to_le32(sizeof(struct durable_context_v2)); 240162306a36Sopenharmony_ci buf->ccontext.NameOffset = cpu_to_le16(offsetof 240262306a36Sopenharmony_ci (struct create_durable_v2, Name)); 240362306a36Sopenharmony_ci buf->ccontext.NameLength = cpu_to_le16(4); 240462306a36Sopenharmony_ci 240562306a36Sopenharmony_ci /* 240662306a36Sopenharmony_ci * NB: Handle timeout defaults to 0, which allows server to choose 240762306a36Sopenharmony_ci * (most servers default to 120 seconds) and most clients default to 0. 240862306a36Sopenharmony_ci * This can be overridden at mount ("handletimeout=") if the user wants 240962306a36Sopenharmony_ci * a different persistent (or resilient) handle timeout for all opens 241062306a36Sopenharmony_ci * on a particular SMB3 mount. 241162306a36Sopenharmony_ci */ 241262306a36Sopenharmony_ci buf->dcontext.Timeout = cpu_to_le32(oparms->tcon->handle_timeout); 241362306a36Sopenharmony_ci buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT); 241462306a36Sopenharmony_ci generate_random_uuid(buf->dcontext.CreateGuid); 241562306a36Sopenharmony_ci memcpy(pfid->create_guid, buf->dcontext.CreateGuid, 16); 241662306a36Sopenharmony_ci 241762306a36Sopenharmony_ci /* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DH2Q" */ 241862306a36Sopenharmony_ci buf->Name[0] = 'D'; 241962306a36Sopenharmony_ci buf->Name[1] = 'H'; 242062306a36Sopenharmony_ci buf->Name[2] = '2'; 242162306a36Sopenharmony_ci buf->Name[3] = 'Q'; 242262306a36Sopenharmony_ci return buf; 242362306a36Sopenharmony_ci} 242462306a36Sopenharmony_ci 242562306a36Sopenharmony_cistatic struct create_durable_handle_reconnect_v2 * 242662306a36Sopenharmony_cicreate_reconnect_durable_v2_buf(struct cifs_fid *fid) 242762306a36Sopenharmony_ci{ 242862306a36Sopenharmony_ci struct create_durable_handle_reconnect_v2 *buf; 242962306a36Sopenharmony_ci 243062306a36Sopenharmony_ci buf = kzalloc(sizeof(struct create_durable_handle_reconnect_v2), 243162306a36Sopenharmony_ci GFP_KERNEL); 243262306a36Sopenharmony_ci if (!buf) 243362306a36Sopenharmony_ci return NULL; 243462306a36Sopenharmony_ci 243562306a36Sopenharmony_ci buf->ccontext.DataOffset = 243662306a36Sopenharmony_ci cpu_to_le16(offsetof(struct create_durable_handle_reconnect_v2, 243762306a36Sopenharmony_ci dcontext)); 243862306a36Sopenharmony_ci buf->ccontext.DataLength = 243962306a36Sopenharmony_ci cpu_to_le32(sizeof(struct durable_reconnect_context_v2)); 244062306a36Sopenharmony_ci buf->ccontext.NameOffset = 244162306a36Sopenharmony_ci cpu_to_le16(offsetof(struct create_durable_handle_reconnect_v2, 244262306a36Sopenharmony_ci Name)); 244362306a36Sopenharmony_ci buf->ccontext.NameLength = cpu_to_le16(4); 244462306a36Sopenharmony_ci 244562306a36Sopenharmony_ci buf->dcontext.Fid.PersistentFileId = fid->persistent_fid; 244662306a36Sopenharmony_ci buf->dcontext.Fid.VolatileFileId = fid->volatile_fid; 244762306a36Sopenharmony_ci buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT); 244862306a36Sopenharmony_ci memcpy(buf->dcontext.CreateGuid, fid->create_guid, 16); 244962306a36Sopenharmony_ci 245062306a36Sopenharmony_ci /* SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 is "DH2C" */ 245162306a36Sopenharmony_ci buf->Name[0] = 'D'; 245262306a36Sopenharmony_ci buf->Name[1] = 'H'; 245362306a36Sopenharmony_ci buf->Name[2] = '2'; 245462306a36Sopenharmony_ci buf->Name[3] = 'C'; 245562306a36Sopenharmony_ci return buf; 245662306a36Sopenharmony_ci} 245762306a36Sopenharmony_ci 245862306a36Sopenharmony_cistatic int 245962306a36Sopenharmony_ciadd_durable_v2_context(struct kvec *iov, unsigned int *num_iovec, 246062306a36Sopenharmony_ci struct cifs_open_parms *oparms) 246162306a36Sopenharmony_ci{ 246262306a36Sopenharmony_ci unsigned int num = *num_iovec; 246362306a36Sopenharmony_ci 246462306a36Sopenharmony_ci iov[num].iov_base = create_durable_v2_buf(oparms); 246562306a36Sopenharmony_ci if (iov[num].iov_base == NULL) 246662306a36Sopenharmony_ci return -ENOMEM; 246762306a36Sopenharmony_ci iov[num].iov_len = sizeof(struct create_durable_v2); 246862306a36Sopenharmony_ci *num_iovec = num + 1; 246962306a36Sopenharmony_ci return 0; 247062306a36Sopenharmony_ci} 247162306a36Sopenharmony_ci 247262306a36Sopenharmony_cistatic int 247362306a36Sopenharmony_ciadd_durable_reconnect_v2_context(struct kvec *iov, unsigned int *num_iovec, 247462306a36Sopenharmony_ci struct cifs_open_parms *oparms) 247562306a36Sopenharmony_ci{ 247662306a36Sopenharmony_ci unsigned int num = *num_iovec; 247762306a36Sopenharmony_ci 247862306a36Sopenharmony_ci /* indicate that we don't need to relock the file */ 247962306a36Sopenharmony_ci oparms->reconnect = false; 248062306a36Sopenharmony_ci 248162306a36Sopenharmony_ci iov[num].iov_base = create_reconnect_durable_v2_buf(oparms->fid); 248262306a36Sopenharmony_ci if (iov[num].iov_base == NULL) 248362306a36Sopenharmony_ci return -ENOMEM; 248462306a36Sopenharmony_ci iov[num].iov_len = sizeof(struct create_durable_handle_reconnect_v2); 248562306a36Sopenharmony_ci *num_iovec = num + 1; 248662306a36Sopenharmony_ci return 0; 248762306a36Sopenharmony_ci} 248862306a36Sopenharmony_ci 248962306a36Sopenharmony_cistatic int 249062306a36Sopenharmony_ciadd_durable_context(struct kvec *iov, unsigned int *num_iovec, 249162306a36Sopenharmony_ci struct cifs_open_parms *oparms, bool use_persistent) 249262306a36Sopenharmony_ci{ 249362306a36Sopenharmony_ci unsigned int num = *num_iovec; 249462306a36Sopenharmony_ci 249562306a36Sopenharmony_ci if (use_persistent) { 249662306a36Sopenharmony_ci if (oparms->reconnect) 249762306a36Sopenharmony_ci return add_durable_reconnect_v2_context(iov, num_iovec, 249862306a36Sopenharmony_ci oparms); 249962306a36Sopenharmony_ci else 250062306a36Sopenharmony_ci return add_durable_v2_context(iov, num_iovec, oparms); 250162306a36Sopenharmony_ci } 250262306a36Sopenharmony_ci 250362306a36Sopenharmony_ci if (oparms->reconnect) { 250462306a36Sopenharmony_ci iov[num].iov_base = create_reconnect_durable_buf(oparms->fid); 250562306a36Sopenharmony_ci /* indicate that we don't need to relock the file */ 250662306a36Sopenharmony_ci oparms->reconnect = false; 250762306a36Sopenharmony_ci } else 250862306a36Sopenharmony_ci iov[num].iov_base = create_durable_buf(); 250962306a36Sopenharmony_ci if (iov[num].iov_base == NULL) 251062306a36Sopenharmony_ci return -ENOMEM; 251162306a36Sopenharmony_ci iov[num].iov_len = sizeof(struct create_durable); 251262306a36Sopenharmony_ci *num_iovec = num + 1; 251362306a36Sopenharmony_ci return 0; 251462306a36Sopenharmony_ci} 251562306a36Sopenharmony_ci 251662306a36Sopenharmony_ci/* See MS-SMB2 2.2.13.2.7 */ 251762306a36Sopenharmony_cistatic struct crt_twarp_ctxt * 251862306a36Sopenharmony_cicreate_twarp_buf(__u64 timewarp) 251962306a36Sopenharmony_ci{ 252062306a36Sopenharmony_ci struct crt_twarp_ctxt *buf; 252162306a36Sopenharmony_ci 252262306a36Sopenharmony_ci buf = kzalloc(sizeof(struct crt_twarp_ctxt), GFP_KERNEL); 252362306a36Sopenharmony_ci if (!buf) 252462306a36Sopenharmony_ci return NULL; 252562306a36Sopenharmony_ci 252662306a36Sopenharmony_ci buf->ccontext.DataOffset = cpu_to_le16(offsetof 252762306a36Sopenharmony_ci (struct crt_twarp_ctxt, Timestamp)); 252862306a36Sopenharmony_ci buf->ccontext.DataLength = cpu_to_le32(8); 252962306a36Sopenharmony_ci buf->ccontext.NameOffset = cpu_to_le16(offsetof 253062306a36Sopenharmony_ci (struct crt_twarp_ctxt, Name)); 253162306a36Sopenharmony_ci buf->ccontext.NameLength = cpu_to_le16(4); 253262306a36Sopenharmony_ci /* SMB2_CREATE_TIMEWARP_TOKEN is "TWrp" */ 253362306a36Sopenharmony_ci buf->Name[0] = 'T'; 253462306a36Sopenharmony_ci buf->Name[1] = 'W'; 253562306a36Sopenharmony_ci buf->Name[2] = 'r'; 253662306a36Sopenharmony_ci buf->Name[3] = 'p'; 253762306a36Sopenharmony_ci buf->Timestamp = cpu_to_le64(timewarp); 253862306a36Sopenharmony_ci return buf; 253962306a36Sopenharmony_ci} 254062306a36Sopenharmony_ci 254162306a36Sopenharmony_ci/* See MS-SMB2 2.2.13.2.7 */ 254262306a36Sopenharmony_cistatic int 254362306a36Sopenharmony_ciadd_twarp_context(struct kvec *iov, unsigned int *num_iovec, __u64 timewarp) 254462306a36Sopenharmony_ci{ 254562306a36Sopenharmony_ci unsigned int num = *num_iovec; 254662306a36Sopenharmony_ci 254762306a36Sopenharmony_ci iov[num].iov_base = create_twarp_buf(timewarp); 254862306a36Sopenharmony_ci if (iov[num].iov_base == NULL) 254962306a36Sopenharmony_ci return -ENOMEM; 255062306a36Sopenharmony_ci iov[num].iov_len = sizeof(struct crt_twarp_ctxt); 255162306a36Sopenharmony_ci *num_iovec = num + 1; 255262306a36Sopenharmony_ci return 0; 255362306a36Sopenharmony_ci} 255462306a36Sopenharmony_ci 255562306a36Sopenharmony_ci/* See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx */ 255662306a36Sopenharmony_cistatic void setup_owner_group_sids(char *buf) 255762306a36Sopenharmony_ci{ 255862306a36Sopenharmony_ci struct owner_group_sids *sids = (struct owner_group_sids *)buf; 255962306a36Sopenharmony_ci 256062306a36Sopenharmony_ci /* Populate the user ownership fields S-1-5-88-1 */ 256162306a36Sopenharmony_ci sids->owner.Revision = 1; 256262306a36Sopenharmony_ci sids->owner.NumAuth = 3; 256362306a36Sopenharmony_ci sids->owner.Authority[5] = 5; 256462306a36Sopenharmony_ci sids->owner.SubAuthorities[0] = cpu_to_le32(88); 256562306a36Sopenharmony_ci sids->owner.SubAuthorities[1] = cpu_to_le32(1); 256662306a36Sopenharmony_ci sids->owner.SubAuthorities[2] = cpu_to_le32(current_fsuid().val); 256762306a36Sopenharmony_ci 256862306a36Sopenharmony_ci /* Populate the group ownership fields S-1-5-88-2 */ 256962306a36Sopenharmony_ci sids->group.Revision = 1; 257062306a36Sopenharmony_ci sids->group.NumAuth = 3; 257162306a36Sopenharmony_ci sids->group.Authority[5] = 5; 257262306a36Sopenharmony_ci sids->group.SubAuthorities[0] = cpu_to_le32(88); 257362306a36Sopenharmony_ci sids->group.SubAuthorities[1] = cpu_to_le32(2); 257462306a36Sopenharmony_ci sids->group.SubAuthorities[2] = cpu_to_le32(current_fsgid().val); 257562306a36Sopenharmony_ci 257662306a36Sopenharmony_ci cifs_dbg(FYI, "owner S-1-5-88-1-%d, group S-1-5-88-2-%d\n", current_fsuid().val, current_fsgid().val); 257762306a36Sopenharmony_ci} 257862306a36Sopenharmony_ci 257962306a36Sopenharmony_ci/* See MS-SMB2 2.2.13.2.2 and MS-DTYP 2.4.6 */ 258062306a36Sopenharmony_cistatic struct crt_sd_ctxt * 258162306a36Sopenharmony_cicreate_sd_buf(umode_t mode, bool set_owner, unsigned int *len) 258262306a36Sopenharmony_ci{ 258362306a36Sopenharmony_ci struct crt_sd_ctxt *buf; 258462306a36Sopenharmony_ci __u8 *ptr, *aclptr; 258562306a36Sopenharmony_ci unsigned int acelen, acl_size, ace_count; 258662306a36Sopenharmony_ci unsigned int owner_offset = 0; 258762306a36Sopenharmony_ci unsigned int group_offset = 0; 258862306a36Sopenharmony_ci struct smb3_acl acl = {}; 258962306a36Sopenharmony_ci 259062306a36Sopenharmony_ci *len = round_up(sizeof(struct crt_sd_ctxt) + (sizeof(struct cifs_ace) * 4), 8); 259162306a36Sopenharmony_ci 259262306a36Sopenharmony_ci if (set_owner) { 259362306a36Sopenharmony_ci /* sizeof(struct owner_group_sids) is already multiple of 8 so no need to round */ 259462306a36Sopenharmony_ci *len += sizeof(struct owner_group_sids); 259562306a36Sopenharmony_ci } 259662306a36Sopenharmony_ci 259762306a36Sopenharmony_ci buf = kzalloc(*len, GFP_KERNEL); 259862306a36Sopenharmony_ci if (buf == NULL) 259962306a36Sopenharmony_ci return buf; 260062306a36Sopenharmony_ci 260162306a36Sopenharmony_ci ptr = (__u8 *)&buf[1]; 260262306a36Sopenharmony_ci if (set_owner) { 260362306a36Sopenharmony_ci /* offset fields are from beginning of security descriptor not of create context */ 260462306a36Sopenharmony_ci owner_offset = ptr - (__u8 *)&buf->sd; 260562306a36Sopenharmony_ci buf->sd.OffsetOwner = cpu_to_le32(owner_offset); 260662306a36Sopenharmony_ci group_offset = owner_offset + offsetof(struct owner_group_sids, group); 260762306a36Sopenharmony_ci buf->sd.OffsetGroup = cpu_to_le32(group_offset); 260862306a36Sopenharmony_ci 260962306a36Sopenharmony_ci setup_owner_group_sids(ptr); 261062306a36Sopenharmony_ci ptr += sizeof(struct owner_group_sids); 261162306a36Sopenharmony_ci } else { 261262306a36Sopenharmony_ci buf->sd.OffsetOwner = 0; 261362306a36Sopenharmony_ci buf->sd.OffsetGroup = 0; 261462306a36Sopenharmony_ci } 261562306a36Sopenharmony_ci 261662306a36Sopenharmony_ci buf->ccontext.DataOffset = cpu_to_le16(offsetof(struct crt_sd_ctxt, sd)); 261762306a36Sopenharmony_ci buf->ccontext.NameOffset = cpu_to_le16(offsetof(struct crt_sd_ctxt, Name)); 261862306a36Sopenharmony_ci buf->ccontext.NameLength = cpu_to_le16(4); 261962306a36Sopenharmony_ci /* SMB2_CREATE_SD_BUFFER_TOKEN is "SecD" */ 262062306a36Sopenharmony_ci buf->Name[0] = 'S'; 262162306a36Sopenharmony_ci buf->Name[1] = 'e'; 262262306a36Sopenharmony_ci buf->Name[2] = 'c'; 262362306a36Sopenharmony_ci buf->Name[3] = 'D'; 262462306a36Sopenharmony_ci buf->sd.Revision = 1; /* Must be one see MS-DTYP 2.4.6 */ 262562306a36Sopenharmony_ci 262662306a36Sopenharmony_ci /* 262762306a36Sopenharmony_ci * ACL is "self relative" ie ACL is stored in contiguous block of memory 262862306a36Sopenharmony_ci * and "DP" ie the DACL is present 262962306a36Sopenharmony_ci */ 263062306a36Sopenharmony_ci buf->sd.Control = cpu_to_le16(ACL_CONTROL_SR | ACL_CONTROL_DP); 263162306a36Sopenharmony_ci 263262306a36Sopenharmony_ci /* offset owner, group and Sbz1 and SACL are all zero */ 263362306a36Sopenharmony_ci buf->sd.OffsetDacl = cpu_to_le32(ptr - (__u8 *)&buf->sd); 263462306a36Sopenharmony_ci /* Ship the ACL for now. we will copy it into buf later. */ 263562306a36Sopenharmony_ci aclptr = ptr; 263662306a36Sopenharmony_ci ptr += sizeof(struct smb3_acl); 263762306a36Sopenharmony_ci 263862306a36Sopenharmony_ci /* create one ACE to hold the mode embedded in reserved special SID */ 263962306a36Sopenharmony_ci acelen = setup_special_mode_ACE((struct cifs_ace *)ptr, (__u64)mode); 264062306a36Sopenharmony_ci ptr += acelen; 264162306a36Sopenharmony_ci acl_size = acelen + sizeof(struct smb3_acl); 264262306a36Sopenharmony_ci ace_count = 1; 264362306a36Sopenharmony_ci 264462306a36Sopenharmony_ci if (set_owner) { 264562306a36Sopenharmony_ci /* we do not need to reallocate buffer to add the two more ACEs. plenty of space */ 264662306a36Sopenharmony_ci acelen = setup_special_user_owner_ACE((struct cifs_ace *)ptr); 264762306a36Sopenharmony_ci ptr += acelen; 264862306a36Sopenharmony_ci acl_size += acelen; 264962306a36Sopenharmony_ci ace_count += 1; 265062306a36Sopenharmony_ci } 265162306a36Sopenharmony_ci 265262306a36Sopenharmony_ci /* and one more ACE to allow access for authenticated users */ 265362306a36Sopenharmony_ci acelen = setup_authusers_ACE((struct cifs_ace *)ptr); 265462306a36Sopenharmony_ci ptr += acelen; 265562306a36Sopenharmony_ci acl_size += acelen; 265662306a36Sopenharmony_ci ace_count += 1; 265762306a36Sopenharmony_ci 265862306a36Sopenharmony_ci acl.AclRevision = ACL_REVISION; /* See 2.4.4.1 of MS-DTYP */ 265962306a36Sopenharmony_ci acl.AclSize = cpu_to_le16(acl_size); 266062306a36Sopenharmony_ci acl.AceCount = cpu_to_le16(ace_count); 266162306a36Sopenharmony_ci /* acl.Sbz1 and Sbz2 MBZ so are not set here, but initialized above */ 266262306a36Sopenharmony_ci memcpy(aclptr, &acl, sizeof(struct smb3_acl)); 266362306a36Sopenharmony_ci 266462306a36Sopenharmony_ci buf->ccontext.DataLength = cpu_to_le32(ptr - (__u8 *)&buf->sd); 266562306a36Sopenharmony_ci *len = round_up((unsigned int)(ptr - (__u8 *)buf), 8); 266662306a36Sopenharmony_ci 266762306a36Sopenharmony_ci return buf; 266862306a36Sopenharmony_ci} 266962306a36Sopenharmony_ci 267062306a36Sopenharmony_cistatic int 267162306a36Sopenharmony_ciadd_sd_context(struct kvec *iov, unsigned int *num_iovec, umode_t mode, bool set_owner) 267262306a36Sopenharmony_ci{ 267362306a36Sopenharmony_ci unsigned int num = *num_iovec; 267462306a36Sopenharmony_ci unsigned int len = 0; 267562306a36Sopenharmony_ci 267662306a36Sopenharmony_ci iov[num].iov_base = create_sd_buf(mode, set_owner, &len); 267762306a36Sopenharmony_ci if (iov[num].iov_base == NULL) 267862306a36Sopenharmony_ci return -ENOMEM; 267962306a36Sopenharmony_ci iov[num].iov_len = len; 268062306a36Sopenharmony_ci *num_iovec = num + 1; 268162306a36Sopenharmony_ci return 0; 268262306a36Sopenharmony_ci} 268362306a36Sopenharmony_ci 268462306a36Sopenharmony_cistatic struct crt_query_id_ctxt * 268562306a36Sopenharmony_cicreate_query_id_buf(void) 268662306a36Sopenharmony_ci{ 268762306a36Sopenharmony_ci struct crt_query_id_ctxt *buf; 268862306a36Sopenharmony_ci 268962306a36Sopenharmony_ci buf = kzalloc(sizeof(struct crt_query_id_ctxt), GFP_KERNEL); 269062306a36Sopenharmony_ci if (!buf) 269162306a36Sopenharmony_ci return NULL; 269262306a36Sopenharmony_ci 269362306a36Sopenharmony_ci buf->ccontext.DataOffset = cpu_to_le16(0); 269462306a36Sopenharmony_ci buf->ccontext.DataLength = cpu_to_le32(0); 269562306a36Sopenharmony_ci buf->ccontext.NameOffset = cpu_to_le16(offsetof 269662306a36Sopenharmony_ci (struct crt_query_id_ctxt, Name)); 269762306a36Sopenharmony_ci buf->ccontext.NameLength = cpu_to_le16(4); 269862306a36Sopenharmony_ci /* SMB2_CREATE_QUERY_ON_DISK_ID is "QFid" */ 269962306a36Sopenharmony_ci buf->Name[0] = 'Q'; 270062306a36Sopenharmony_ci buf->Name[1] = 'F'; 270162306a36Sopenharmony_ci buf->Name[2] = 'i'; 270262306a36Sopenharmony_ci buf->Name[3] = 'd'; 270362306a36Sopenharmony_ci return buf; 270462306a36Sopenharmony_ci} 270562306a36Sopenharmony_ci 270662306a36Sopenharmony_ci/* See MS-SMB2 2.2.13.2.9 */ 270762306a36Sopenharmony_cistatic int 270862306a36Sopenharmony_ciadd_query_id_context(struct kvec *iov, unsigned int *num_iovec) 270962306a36Sopenharmony_ci{ 271062306a36Sopenharmony_ci unsigned int num = *num_iovec; 271162306a36Sopenharmony_ci 271262306a36Sopenharmony_ci iov[num].iov_base = create_query_id_buf(); 271362306a36Sopenharmony_ci if (iov[num].iov_base == NULL) 271462306a36Sopenharmony_ci return -ENOMEM; 271562306a36Sopenharmony_ci iov[num].iov_len = sizeof(struct crt_query_id_ctxt); 271662306a36Sopenharmony_ci *num_iovec = num + 1; 271762306a36Sopenharmony_ci return 0; 271862306a36Sopenharmony_ci} 271962306a36Sopenharmony_ci 272062306a36Sopenharmony_cistatic int 272162306a36Sopenharmony_cialloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len, 272262306a36Sopenharmony_ci const char *treename, const __le16 *path) 272362306a36Sopenharmony_ci{ 272462306a36Sopenharmony_ci int treename_len, path_len; 272562306a36Sopenharmony_ci struct nls_table *cp; 272662306a36Sopenharmony_ci const __le16 sep[] = {cpu_to_le16('\\'), cpu_to_le16(0x0000)}; 272762306a36Sopenharmony_ci 272862306a36Sopenharmony_ci /* 272962306a36Sopenharmony_ci * skip leading "\\" 273062306a36Sopenharmony_ci */ 273162306a36Sopenharmony_ci treename_len = strlen(treename); 273262306a36Sopenharmony_ci if (treename_len < 2 || !(treename[0] == '\\' && treename[1] == '\\')) 273362306a36Sopenharmony_ci return -EINVAL; 273462306a36Sopenharmony_ci 273562306a36Sopenharmony_ci treename += 2; 273662306a36Sopenharmony_ci treename_len -= 2; 273762306a36Sopenharmony_ci 273862306a36Sopenharmony_ci path_len = UniStrnlen((wchar_t *)path, PATH_MAX); 273962306a36Sopenharmony_ci 274062306a36Sopenharmony_ci /* make room for one path separator only if @path isn't empty */ 274162306a36Sopenharmony_ci *out_len = treename_len + (path[0] ? 1 : 0) + path_len; 274262306a36Sopenharmony_ci 274362306a36Sopenharmony_ci /* 274462306a36Sopenharmony_ci * final path needs to be 8-byte aligned as specified in 274562306a36Sopenharmony_ci * MS-SMB2 2.2.13 SMB2 CREATE Request. 274662306a36Sopenharmony_ci */ 274762306a36Sopenharmony_ci *out_size = round_up(*out_len * sizeof(__le16), 8); 274862306a36Sopenharmony_ci *out_path = kzalloc(*out_size + sizeof(__le16) /* null */, GFP_KERNEL); 274962306a36Sopenharmony_ci if (!*out_path) 275062306a36Sopenharmony_ci return -ENOMEM; 275162306a36Sopenharmony_ci 275262306a36Sopenharmony_ci cp = load_nls_default(); 275362306a36Sopenharmony_ci cifs_strtoUTF16(*out_path, treename, treename_len, cp); 275462306a36Sopenharmony_ci 275562306a36Sopenharmony_ci /* Do not append the separator if the path is empty */ 275662306a36Sopenharmony_ci if (path[0] != cpu_to_le16(0x0000)) { 275762306a36Sopenharmony_ci UniStrcat((wchar_t *)*out_path, (wchar_t *)sep); 275862306a36Sopenharmony_ci UniStrcat((wchar_t *)*out_path, (wchar_t *)path); 275962306a36Sopenharmony_ci } 276062306a36Sopenharmony_ci 276162306a36Sopenharmony_ci unload_nls(cp); 276262306a36Sopenharmony_ci 276362306a36Sopenharmony_ci return 0; 276462306a36Sopenharmony_ci} 276562306a36Sopenharmony_ci 276662306a36Sopenharmony_ciint smb311_posix_mkdir(const unsigned int xid, struct inode *inode, 276762306a36Sopenharmony_ci umode_t mode, struct cifs_tcon *tcon, 276862306a36Sopenharmony_ci const char *full_path, 276962306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb) 277062306a36Sopenharmony_ci{ 277162306a36Sopenharmony_ci struct smb_rqst rqst; 277262306a36Sopenharmony_ci struct smb2_create_req *req; 277362306a36Sopenharmony_ci struct smb2_create_rsp *rsp = NULL; 277462306a36Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 277562306a36Sopenharmony_ci struct kvec iov[3]; /* make sure at least one for each open context */ 277662306a36Sopenharmony_ci struct kvec rsp_iov = {NULL, 0}; 277762306a36Sopenharmony_ci int resp_buftype; 277862306a36Sopenharmony_ci int uni_path_len; 277962306a36Sopenharmony_ci __le16 *copy_path = NULL; 278062306a36Sopenharmony_ci int copy_size; 278162306a36Sopenharmony_ci int rc = 0; 278262306a36Sopenharmony_ci unsigned int n_iov = 2; 278362306a36Sopenharmony_ci __u32 file_attributes = 0; 278462306a36Sopenharmony_ci char *pc_buf = NULL; 278562306a36Sopenharmony_ci int flags = 0; 278662306a36Sopenharmony_ci unsigned int total_len; 278762306a36Sopenharmony_ci __le16 *utf16_path = NULL; 278862306a36Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(ses); 278962306a36Sopenharmony_ci 279062306a36Sopenharmony_ci cifs_dbg(FYI, "mkdir\n"); 279162306a36Sopenharmony_ci 279262306a36Sopenharmony_ci /* resource #1: path allocation */ 279362306a36Sopenharmony_ci utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); 279462306a36Sopenharmony_ci if (!utf16_path) 279562306a36Sopenharmony_ci return -ENOMEM; 279662306a36Sopenharmony_ci 279762306a36Sopenharmony_ci if (!ses || !server) { 279862306a36Sopenharmony_ci rc = -EIO; 279962306a36Sopenharmony_ci goto err_free_path; 280062306a36Sopenharmony_ci } 280162306a36Sopenharmony_ci 280262306a36Sopenharmony_ci /* resource #2: request */ 280362306a36Sopenharmony_ci rc = smb2_plain_req_init(SMB2_CREATE, tcon, server, 280462306a36Sopenharmony_ci (void **) &req, &total_len); 280562306a36Sopenharmony_ci if (rc) 280662306a36Sopenharmony_ci goto err_free_path; 280762306a36Sopenharmony_ci 280862306a36Sopenharmony_ci 280962306a36Sopenharmony_ci if (smb3_encryption_required(tcon)) 281062306a36Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 281162306a36Sopenharmony_ci 281262306a36Sopenharmony_ci req->ImpersonationLevel = IL_IMPERSONATION; 281362306a36Sopenharmony_ci req->DesiredAccess = cpu_to_le32(FILE_WRITE_ATTRIBUTES); 281462306a36Sopenharmony_ci /* File attributes ignored on open (used in create though) */ 281562306a36Sopenharmony_ci req->FileAttributes = cpu_to_le32(file_attributes); 281662306a36Sopenharmony_ci req->ShareAccess = FILE_SHARE_ALL_LE; 281762306a36Sopenharmony_ci req->CreateDisposition = cpu_to_le32(FILE_CREATE); 281862306a36Sopenharmony_ci req->CreateOptions = cpu_to_le32(CREATE_NOT_FILE); 281962306a36Sopenharmony_ci 282062306a36Sopenharmony_ci iov[0].iov_base = (char *)req; 282162306a36Sopenharmony_ci /* -1 since last byte is buf[0] which is sent below (path) */ 282262306a36Sopenharmony_ci iov[0].iov_len = total_len - 1; 282362306a36Sopenharmony_ci 282462306a36Sopenharmony_ci req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req)); 282562306a36Sopenharmony_ci 282662306a36Sopenharmony_ci /* [MS-SMB2] 2.2.13 NameOffset: 282762306a36Sopenharmony_ci * If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of 282862306a36Sopenharmony_ci * the SMB2 header, the file name includes a prefix that will 282962306a36Sopenharmony_ci * be processed during DFS name normalization as specified in 283062306a36Sopenharmony_ci * section 3.3.5.9. Otherwise, the file name is relative to 283162306a36Sopenharmony_ci * the share that is identified by the TreeId in the SMB2 283262306a36Sopenharmony_ci * header. 283362306a36Sopenharmony_ci */ 283462306a36Sopenharmony_ci if (tcon->share_flags & SHI1005_FLAGS_DFS) { 283562306a36Sopenharmony_ci int name_len; 283662306a36Sopenharmony_ci 283762306a36Sopenharmony_ci req->hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS; 283862306a36Sopenharmony_ci rc = alloc_path_with_tree_prefix(©_path, ©_size, 283962306a36Sopenharmony_ci &name_len, 284062306a36Sopenharmony_ci tcon->tree_name, utf16_path); 284162306a36Sopenharmony_ci if (rc) 284262306a36Sopenharmony_ci goto err_free_req; 284362306a36Sopenharmony_ci 284462306a36Sopenharmony_ci req->NameLength = cpu_to_le16(name_len * 2); 284562306a36Sopenharmony_ci uni_path_len = copy_size; 284662306a36Sopenharmony_ci /* free before overwriting resource */ 284762306a36Sopenharmony_ci kfree(utf16_path); 284862306a36Sopenharmony_ci utf16_path = copy_path; 284962306a36Sopenharmony_ci } else { 285062306a36Sopenharmony_ci uni_path_len = (2 * UniStrnlen((wchar_t *)utf16_path, PATH_MAX)) + 2; 285162306a36Sopenharmony_ci /* MUST set path len (NameLength) to 0 opening root of share */ 285262306a36Sopenharmony_ci req->NameLength = cpu_to_le16(uni_path_len - 2); 285362306a36Sopenharmony_ci if (uni_path_len % 8 != 0) { 285462306a36Sopenharmony_ci copy_size = roundup(uni_path_len, 8); 285562306a36Sopenharmony_ci copy_path = kzalloc(copy_size, GFP_KERNEL); 285662306a36Sopenharmony_ci if (!copy_path) { 285762306a36Sopenharmony_ci rc = -ENOMEM; 285862306a36Sopenharmony_ci goto err_free_req; 285962306a36Sopenharmony_ci } 286062306a36Sopenharmony_ci memcpy((char *)copy_path, (const char *)utf16_path, 286162306a36Sopenharmony_ci uni_path_len); 286262306a36Sopenharmony_ci uni_path_len = copy_size; 286362306a36Sopenharmony_ci /* free before overwriting resource */ 286462306a36Sopenharmony_ci kfree(utf16_path); 286562306a36Sopenharmony_ci utf16_path = copy_path; 286662306a36Sopenharmony_ci } 286762306a36Sopenharmony_ci } 286862306a36Sopenharmony_ci 286962306a36Sopenharmony_ci iov[1].iov_len = uni_path_len; 287062306a36Sopenharmony_ci iov[1].iov_base = utf16_path; 287162306a36Sopenharmony_ci req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_NONE; 287262306a36Sopenharmony_ci 287362306a36Sopenharmony_ci if (tcon->posix_extensions) { 287462306a36Sopenharmony_ci /* resource #3: posix buf */ 287562306a36Sopenharmony_ci rc = add_posix_context(iov, &n_iov, mode); 287662306a36Sopenharmony_ci if (rc) 287762306a36Sopenharmony_ci goto err_free_req; 287862306a36Sopenharmony_ci req->CreateContextsOffset = cpu_to_le32( 287962306a36Sopenharmony_ci sizeof(struct smb2_create_req) + 288062306a36Sopenharmony_ci iov[1].iov_len); 288162306a36Sopenharmony_ci pc_buf = iov[n_iov-1].iov_base; 288262306a36Sopenharmony_ci } 288362306a36Sopenharmony_ci 288462306a36Sopenharmony_ci 288562306a36Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 288662306a36Sopenharmony_ci rqst.rq_iov = iov; 288762306a36Sopenharmony_ci rqst.rq_nvec = n_iov; 288862306a36Sopenharmony_ci 288962306a36Sopenharmony_ci /* no need to inc num_remote_opens because we close it just below */ 289062306a36Sopenharmony_ci trace_smb3_posix_mkdir_enter(xid, tcon->tid, ses->Suid, full_path, CREATE_NOT_FILE, 289162306a36Sopenharmony_ci FILE_WRITE_ATTRIBUTES); 289262306a36Sopenharmony_ci /* resource #4: response buffer */ 289362306a36Sopenharmony_ci rc = cifs_send_recv(xid, ses, server, 289462306a36Sopenharmony_ci &rqst, &resp_buftype, flags, &rsp_iov); 289562306a36Sopenharmony_ci if (rc) { 289662306a36Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_CREATE_HE); 289762306a36Sopenharmony_ci trace_smb3_posix_mkdir_err(xid, tcon->tid, ses->Suid, 289862306a36Sopenharmony_ci CREATE_NOT_FILE, 289962306a36Sopenharmony_ci FILE_WRITE_ATTRIBUTES, rc); 290062306a36Sopenharmony_ci goto err_free_rsp_buf; 290162306a36Sopenharmony_ci } 290262306a36Sopenharmony_ci 290362306a36Sopenharmony_ci /* 290462306a36Sopenharmony_ci * Although unlikely to be possible for rsp to be null and rc not set, 290562306a36Sopenharmony_ci * adding check below is slightly safer long term (and quiets Coverity 290662306a36Sopenharmony_ci * warning) 290762306a36Sopenharmony_ci */ 290862306a36Sopenharmony_ci rsp = (struct smb2_create_rsp *)rsp_iov.iov_base; 290962306a36Sopenharmony_ci if (rsp == NULL) { 291062306a36Sopenharmony_ci rc = -EIO; 291162306a36Sopenharmony_ci kfree(pc_buf); 291262306a36Sopenharmony_ci goto err_free_req; 291362306a36Sopenharmony_ci } 291462306a36Sopenharmony_ci 291562306a36Sopenharmony_ci trace_smb3_posix_mkdir_done(xid, rsp->PersistentFileId, tcon->tid, ses->Suid, 291662306a36Sopenharmony_ci CREATE_NOT_FILE, FILE_WRITE_ATTRIBUTES); 291762306a36Sopenharmony_ci 291862306a36Sopenharmony_ci SMB2_close(xid, tcon, rsp->PersistentFileId, rsp->VolatileFileId); 291962306a36Sopenharmony_ci 292062306a36Sopenharmony_ci /* Eventually save off posix specific response info and timestaps */ 292162306a36Sopenharmony_ci 292262306a36Sopenharmony_cierr_free_rsp_buf: 292362306a36Sopenharmony_ci free_rsp_buf(resp_buftype, rsp); 292462306a36Sopenharmony_ci kfree(pc_buf); 292562306a36Sopenharmony_cierr_free_req: 292662306a36Sopenharmony_ci cifs_small_buf_release(req); 292762306a36Sopenharmony_cierr_free_path: 292862306a36Sopenharmony_ci kfree(utf16_path); 292962306a36Sopenharmony_ci return rc; 293062306a36Sopenharmony_ci} 293162306a36Sopenharmony_ci 293262306a36Sopenharmony_ciint 293362306a36Sopenharmony_ciSMB2_open_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, 293462306a36Sopenharmony_ci struct smb_rqst *rqst, __u8 *oplock, 293562306a36Sopenharmony_ci struct cifs_open_parms *oparms, __le16 *path) 293662306a36Sopenharmony_ci{ 293762306a36Sopenharmony_ci struct smb2_create_req *req; 293862306a36Sopenharmony_ci unsigned int n_iov = 2; 293962306a36Sopenharmony_ci __u32 file_attributes = 0; 294062306a36Sopenharmony_ci int copy_size; 294162306a36Sopenharmony_ci int uni_path_len; 294262306a36Sopenharmony_ci unsigned int total_len; 294362306a36Sopenharmony_ci struct kvec *iov = rqst->rq_iov; 294462306a36Sopenharmony_ci __le16 *copy_path; 294562306a36Sopenharmony_ci int rc; 294662306a36Sopenharmony_ci 294762306a36Sopenharmony_ci rc = smb2_plain_req_init(SMB2_CREATE, tcon, server, 294862306a36Sopenharmony_ci (void **) &req, &total_len); 294962306a36Sopenharmony_ci if (rc) 295062306a36Sopenharmony_ci return rc; 295162306a36Sopenharmony_ci 295262306a36Sopenharmony_ci iov[0].iov_base = (char *)req; 295362306a36Sopenharmony_ci /* -1 since last byte is buf[0] which is sent below (path) */ 295462306a36Sopenharmony_ci iov[0].iov_len = total_len - 1; 295562306a36Sopenharmony_ci 295662306a36Sopenharmony_ci if (oparms->create_options & CREATE_OPTION_READONLY) 295762306a36Sopenharmony_ci file_attributes |= ATTR_READONLY; 295862306a36Sopenharmony_ci if (oparms->create_options & CREATE_OPTION_SPECIAL) 295962306a36Sopenharmony_ci file_attributes |= ATTR_SYSTEM; 296062306a36Sopenharmony_ci 296162306a36Sopenharmony_ci req->ImpersonationLevel = IL_IMPERSONATION; 296262306a36Sopenharmony_ci req->DesiredAccess = cpu_to_le32(oparms->desired_access); 296362306a36Sopenharmony_ci /* File attributes ignored on open (used in create though) */ 296462306a36Sopenharmony_ci req->FileAttributes = cpu_to_le32(file_attributes); 296562306a36Sopenharmony_ci req->ShareAccess = FILE_SHARE_ALL_LE; 296662306a36Sopenharmony_ci 296762306a36Sopenharmony_ci req->CreateDisposition = cpu_to_le32(oparms->disposition); 296862306a36Sopenharmony_ci req->CreateOptions = cpu_to_le32(oparms->create_options & CREATE_OPTIONS_MASK); 296962306a36Sopenharmony_ci req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req)); 297062306a36Sopenharmony_ci 297162306a36Sopenharmony_ci /* [MS-SMB2] 2.2.13 NameOffset: 297262306a36Sopenharmony_ci * If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of 297362306a36Sopenharmony_ci * the SMB2 header, the file name includes a prefix that will 297462306a36Sopenharmony_ci * be processed during DFS name normalization as specified in 297562306a36Sopenharmony_ci * section 3.3.5.9. Otherwise, the file name is relative to 297662306a36Sopenharmony_ci * the share that is identified by the TreeId in the SMB2 297762306a36Sopenharmony_ci * header. 297862306a36Sopenharmony_ci */ 297962306a36Sopenharmony_ci if (tcon->share_flags & SHI1005_FLAGS_DFS) { 298062306a36Sopenharmony_ci int name_len; 298162306a36Sopenharmony_ci 298262306a36Sopenharmony_ci req->hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS; 298362306a36Sopenharmony_ci rc = alloc_path_with_tree_prefix(©_path, ©_size, 298462306a36Sopenharmony_ci &name_len, 298562306a36Sopenharmony_ci tcon->tree_name, path); 298662306a36Sopenharmony_ci if (rc) 298762306a36Sopenharmony_ci return rc; 298862306a36Sopenharmony_ci req->NameLength = cpu_to_le16(name_len * 2); 298962306a36Sopenharmony_ci uni_path_len = copy_size; 299062306a36Sopenharmony_ci path = copy_path; 299162306a36Sopenharmony_ci } else { 299262306a36Sopenharmony_ci uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2; 299362306a36Sopenharmony_ci /* MUST set path len (NameLength) to 0 opening root of share */ 299462306a36Sopenharmony_ci req->NameLength = cpu_to_le16(uni_path_len - 2); 299562306a36Sopenharmony_ci copy_size = round_up(uni_path_len, 8); 299662306a36Sopenharmony_ci copy_path = kzalloc(copy_size, GFP_KERNEL); 299762306a36Sopenharmony_ci if (!copy_path) 299862306a36Sopenharmony_ci return -ENOMEM; 299962306a36Sopenharmony_ci memcpy((char *)copy_path, (const char *)path, 300062306a36Sopenharmony_ci uni_path_len); 300162306a36Sopenharmony_ci uni_path_len = copy_size; 300262306a36Sopenharmony_ci path = copy_path; 300362306a36Sopenharmony_ci } 300462306a36Sopenharmony_ci 300562306a36Sopenharmony_ci iov[1].iov_len = uni_path_len; 300662306a36Sopenharmony_ci iov[1].iov_base = path; 300762306a36Sopenharmony_ci 300862306a36Sopenharmony_ci if ((!server->oplocks) || (tcon->no_lease)) 300962306a36Sopenharmony_ci *oplock = SMB2_OPLOCK_LEVEL_NONE; 301062306a36Sopenharmony_ci 301162306a36Sopenharmony_ci if (!(server->capabilities & SMB2_GLOBAL_CAP_LEASING) || 301262306a36Sopenharmony_ci *oplock == SMB2_OPLOCK_LEVEL_NONE) 301362306a36Sopenharmony_ci req->RequestedOplockLevel = *oplock; 301462306a36Sopenharmony_ci else if (!(server->capabilities & SMB2_GLOBAL_CAP_DIRECTORY_LEASING) && 301562306a36Sopenharmony_ci (oparms->create_options & CREATE_NOT_FILE)) 301662306a36Sopenharmony_ci req->RequestedOplockLevel = *oplock; /* no srv lease support */ 301762306a36Sopenharmony_ci else { 301862306a36Sopenharmony_ci rc = add_lease_context(server, req, iov, &n_iov, 301962306a36Sopenharmony_ci oparms->fid->lease_key, oplock); 302062306a36Sopenharmony_ci if (rc) 302162306a36Sopenharmony_ci return rc; 302262306a36Sopenharmony_ci } 302362306a36Sopenharmony_ci 302462306a36Sopenharmony_ci if (*oplock == SMB2_OPLOCK_LEVEL_BATCH) { 302562306a36Sopenharmony_ci rc = add_durable_context(iov, &n_iov, oparms, 302662306a36Sopenharmony_ci tcon->use_persistent); 302762306a36Sopenharmony_ci if (rc) 302862306a36Sopenharmony_ci return rc; 302962306a36Sopenharmony_ci } 303062306a36Sopenharmony_ci 303162306a36Sopenharmony_ci if (tcon->posix_extensions) { 303262306a36Sopenharmony_ci rc = add_posix_context(iov, &n_iov, oparms->mode); 303362306a36Sopenharmony_ci if (rc) 303462306a36Sopenharmony_ci return rc; 303562306a36Sopenharmony_ci } 303662306a36Sopenharmony_ci 303762306a36Sopenharmony_ci if (tcon->snapshot_time) { 303862306a36Sopenharmony_ci cifs_dbg(FYI, "adding snapshot context\n"); 303962306a36Sopenharmony_ci rc = add_twarp_context(iov, &n_iov, tcon->snapshot_time); 304062306a36Sopenharmony_ci if (rc) 304162306a36Sopenharmony_ci return rc; 304262306a36Sopenharmony_ci } 304362306a36Sopenharmony_ci 304462306a36Sopenharmony_ci if ((oparms->disposition != FILE_OPEN) && (oparms->cifs_sb)) { 304562306a36Sopenharmony_ci bool set_mode; 304662306a36Sopenharmony_ci bool set_owner; 304762306a36Sopenharmony_ci 304862306a36Sopenharmony_ci if ((oparms->cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID) && 304962306a36Sopenharmony_ci (oparms->mode != ACL_NO_MODE)) 305062306a36Sopenharmony_ci set_mode = true; 305162306a36Sopenharmony_ci else { 305262306a36Sopenharmony_ci set_mode = false; 305362306a36Sopenharmony_ci oparms->mode = ACL_NO_MODE; 305462306a36Sopenharmony_ci } 305562306a36Sopenharmony_ci 305662306a36Sopenharmony_ci if (oparms->cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL) 305762306a36Sopenharmony_ci set_owner = true; 305862306a36Sopenharmony_ci else 305962306a36Sopenharmony_ci set_owner = false; 306062306a36Sopenharmony_ci 306162306a36Sopenharmony_ci if (set_owner | set_mode) { 306262306a36Sopenharmony_ci cifs_dbg(FYI, "add sd with mode 0x%x\n", oparms->mode); 306362306a36Sopenharmony_ci rc = add_sd_context(iov, &n_iov, oparms->mode, set_owner); 306462306a36Sopenharmony_ci if (rc) 306562306a36Sopenharmony_ci return rc; 306662306a36Sopenharmony_ci } 306762306a36Sopenharmony_ci } 306862306a36Sopenharmony_ci 306962306a36Sopenharmony_ci add_query_id_context(iov, &n_iov); 307062306a36Sopenharmony_ci 307162306a36Sopenharmony_ci if (n_iov > 2) { 307262306a36Sopenharmony_ci /* 307362306a36Sopenharmony_ci * We have create contexts behind iov[1] (the file 307462306a36Sopenharmony_ci * name), point at them from the main create request 307562306a36Sopenharmony_ci */ 307662306a36Sopenharmony_ci req->CreateContextsOffset = cpu_to_le32( 307762306a36Sopenharmony_ci sizeof(struct smb2_create_req) + 307862306a36Sopenharmony_ci iov[1].iov_len); 307962306a36Sopenharmony_ci req->CreateContextsLength = 0; 308062306a36Sopenharmony_ci 308162306a36Sopenharmony_ci for (unsigned int i = 2; i < (n_iov-1); i++) { 308262306a36Sopenharmony_ci struct kvec *v = &iov[i]; 308362306a36Sopenharmony_ci size_t len = v->iov_len; 308462306a36Sopenharmony_ci struct create_context *cctx = 308562306a36Sopenharmony_ci (struct create_context *)v->iov_base; 308662306a36Sopenharmony_ci 308762306a36Sopenharmony_ci cctx->Next = cpu_to_le32(len); 308862306a36Sopenharmony_ci le32_add_cpu(&req->CreateContextsLength, len); 308962306a36Sopenharmony_ci } 309062306a36Sopenharmony_ci le32_add_cpu(&req->CreateContextsLength, 309162306a36Sopenharmony_ci iov[n_iov-1].iov_len); 309262306a36Sopenharmony_ci } 309362306a36Sopenharmony_ci 309462306a36Sopenharmony_ci rqst->rq_nvec = n_iov; 309562306a36Sopenharmony_ci return 0; 309662306a36Sopenharmony_ci} 309762306a36Sopenharmony_ci 309862306a36Sopenharmony_ci/* rq_iov[0] is the request and is released by cifs_small_buf_release(). 309962306a36Sopenharmony_ci * All other vectors are freed by kfree(). 310062306a36Sopenharmony_ci */ 310162306a36Sopenharmony_civoid 310262306a36Sopenharmony_ciSMB2_open_free(struct smb_rqst *rqst) 310362306a36Sopenharmony_ci{ 310462306a36Sopenharmony_ci int i; 310562306a36Sopenharmony_ci 310662306a36Sopenharmony_ci if (rqst && rqst->rq_iov) { 310762306a36Sopenharmony_ci cifs_small_buf_release(rqst->rq_iov[0].iov_base); 310862306a36Sopenharmony_ci for (i = 1; i < rqst->rq_nvec; i++) 310962306a36Sopenharmony_ci if (rqst->rq_iov[i].iov_base != smb2_padding) 311062306a36Sopenharmony_ci kfree(rqst->rq_iov[i].iov_base); 311162306a36Sopenharmony_ci } 311262306a36Sopenharmony_ci} 311362306a36Sopenharmony_ci 311462306a36Sopenharmony_ciint 311562306a36Sopenharmony_ciSMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, 311662306a36Sopenharmony_ci __u8 *oplock, struct smb2_file_all_info *buf, 311762306a36Sopenharmony_ci struct create_posix_rsp *posix, 311862306a36Sopenharmony_ci struct kvec *err_iov, int *buftype) 311962306a36Sopenharmony_ci{ 312062306a36Sopenharmony_ci struct smb_rqst rqst; 312162306a36Sopenharmony_ci struct smb2_create_rsp *rsp = NULL; 312262306a36Sopenharmony_ci struct cifs_tcon *tcon = oparms->tcon; 312362306a36Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 312462306a36Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(ses); 312562306a36Sopenharmony_ci struct kvec iov[SMB2_CREATE_IOV_SIZE]; 312662306a36Sopenharmony_ci struct kvec rsp_iov = {NULL, 0}; 312762306a36Sopenharmony_ci int resp_buftype = CIFS_NO_BUFFER; 312862306a36Sopenharmony_ci int rc = 0; 312962306a36Sopenharmony_ci int flags = 0; 313062306a36Sopenharmony_ci 313162306a36Sopenharmony_ci cifs_dbg(FYI, "create/open\n"); 313262306a36Sopenharmony_ci if (!ses || !server) 313362306a36Sopenharmony_ci return -EIO; 313462306a36Sopenharmony_ci 313562306a36Sopenharmony_ci if (smb3_encryption_required(tcon)) 313662306a36Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 313762306a36Sopenharmony_ci 313862306a36Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 313962306a36Sopenharmony_ci memset(&iov, 0, sizeof(iov)); 314062306a36Sopenharmony_ci rqst.rq_iov = iov; 314162306a36Sopenharmony_ci rqst.rq_nvec = SMB2_CREATE_IOV_SIZE; 314262306a36Sopenharmony_ci 314362306a36Sopenharmony_ci rc = SMB2_open_init(tcon, server, 314462306a36Sopenharmony_ci &rqst, oplock, oparms, path); 314562306a36Sopenharmony_ci if (rc) 314662306a36Sopenharmony_ci goto creat_exit; 314762306a36Sopenharmony_ci 314862306a36Sopenharmony_ci trace_smb3_open_enter(xid, tcon->tid, tcon->ses->Suid, oparms->path, 314962306a36Sopenharmony_ci oparms->create_options, oparms->desired_access); 315062306a36Sopenharmony_ci 315162306a36Sopenharmony_ci rc = cifs_send_recv(xid, ses, server, 315262306a36Sopenharmony_ci &rqst, &resp_buftype, flags, 315362306a36Sopenharmony_ci &rsp_iov); 315462306a36Sopenharmony_ci rsp = (struct smb2_create_rsp *)rsp_iov.iov_base; 315562306a36Sopenharmony_ci 315662306a36Sopenharmony_ci if (rc != 0) { 315762306a36Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_CREATE_HE); 315862306a36Sopenharmony_ci if (err_iov && rsp) { 315962306a36Sopenharmony_ci *err_iov = rsp_iov; 316062306a36Sopenharmony_ci *buftype = resp_buftype; 316162306a36Sopenharmony_ci resp_buftype = CIFS_NO_BUFFER; 316262306a36Sopenharmony_ci rsp = NULL; 316362306a36Sopenharmony_ci } 316462306a36Sopenharmony_ci trace_smb3_open_err(xid, tcon->tid, ses->Suid, 316562306a36Sopenharmony_ci oparms->create_options, oparms->desired_access, rc); 316662306a36Sopenharmony_ci if (rc == -EREMCHG) { 316762306a36Sopenharmony_ci pr_warn_once("server share %s deleted\n", 316862306a36Sopenharmony_ci tcon->tree_name); 316962306a36Sopenharmony_ci tcon->need_reconnect = true; 317062306a36Sopenharmony_ci } 317162306a36Sopenharmony_ci goto creat_exit; 317262306a36Sopenharmony_ci } else if (rsp == NULL) /* unlikely to happen, but safer to check */ 317362306a36Sopenharmony_ci goto creat_exit; 317462306a36Sopenharmony_ci else 317562306a36Sopenharmony_ci trace_smb3_open_done(xid, rsp->PersistentFileId, tcon->tid, ses->Suid, 317662306a36Sopenharmony_ci oparms->create_options, oparms->desired_access); 317762306a36Sopenharmony_ci 317862306a36Sopenharmony_ci atomic_inc(&tcon->num_remote_opens); 317962306a36Sopenharmony_ci oparms->fid->persistent_fid = rsp->PersistentFileId; 318062306a36Sopenharmony_ci oparms->fid->volatile_fid = rsp->VolatileFileId; 318162306a36Sopenharmony_ci oparms->fid->access = oparms->desired_access; 318262306a36Sopenharmony_ci#ifdef CONFIG_CIFS_DEBUG2 318362306a36Sopenharmony_ci oparms->fid->mid = le64_to_cpu(rsp->hdr.MessageId); 318462306a36Sopenharmony_ci#endif /* CIFS_DEBUG2 */ 318562306a36Sopenharmony_ci 318662306a36Sopenharmony_ci if (buf) { 318762306a36Sopenharmony_ci buf->CreationTime = rsp->CreationTime; 318862306a36Sopenharmony_ci buf->LastAccessTime = rsp->LastAccessTime; 318962306a36Sopenharmony_ci buf->LastWriteTime = rsp->LastWriteTime; 319062306a36Sopenharmony_ci buf->ChangeTime = rsp->ChangeTime; 319162306a36Sopenharmony_ci buf->AllocationSize = rsp->AllocationSize; 319262306a36Sopenharmony_ci buf->EndOfFile = rsp->EndofFile; 319362306a36Sopenharmony_ci buf->Attributes = rsp->FileAttributes; 319462306a36Sopenharmony_ci buf->NumberOfLinks = cpu_to_le32(1); 319562306a36Sopenharmony_ci buf->DeletePending = 0; 319662306a36Sopenharmony_ci } 319762306a36Sopenharmony_ci 319862306a36Sopenharmony_ci 319962306a36Sopenharmony_ci rc = smb2_parse_contexts(server, &rsp_iov, &oparms->fid->epoch, 320062306a36Sopenharmony_ci oparms->fid->lease_key, oplock, buf, posix); 320162306a36Sopenharmony_cicreat_exit: 320262306a36Sopenharmony_ci SMB2_open_free(&rqst); 320362306a36Sopenharmony_ci free_rsp_buf(resp_buftype, rsp); 320462306a36Sopenharmony_ci return rc; 320562306a36Sopenharmony_ci} 320662306a36Sopenharmony_ci 320762306a36Sopenharmony_ciint 320862306a36Sopenharmony_ciSMB2_ioctl_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, 320962306a36Sopenharmony_ci struct smb_rqst *rqst, 321062306a36Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, u32 opcode, 321162306a36Sopenharmony_ci char *in_data, u32 indatalen, 321262306a36Sopenharmony_ci __u32 max_response_size) 321362306a36Sopenharmony_ci{ 321462306a36Sopenharmony_ci struct smb2_ioctl_req *req; 321562306a36Sopenharmony_ci struct kvec *iov = rqst->rq_iov; 321662306a36Sopenharmony_ci unsigned int total_len; 321762306a36Sopenharmony_ci int rc; 321862306a36Sopenharmony_ci char *in_data_buf; 321962306a36Sopenharmony_ci 322062306a36Sopenharmony_ci rc = smb2_ioctl_req_init(opcode, tcon, server, 322162306a36Sopenharmony_ci (void **) &req, &total_len); 322262306a36Sopenharmony_ci if (rc) 322362306a36Sopenharmony_ci return rc; 322462306a36Sopenharmony_ci 322562306a36Sopenharmony_ci if (indatalen) { 322662306a36Sopenharmony_ci /* 322762306a36Sopenharmony_ci * indatalen is usually small at a couple of bytes max, so 322862306a36Sopenharmony_ci * just allocate through generic pool 322962306a36Sopenharmony_ci */ 323062306a36Sopenharmony_ci in_data_buf = kmemdup(in_data, indatalen, GFP_NOFS); 323162306a36Sopenharmony_ci if (!in_data_buf) { 323262306a36Sopenharmony_ci cifs_small_buf_release(req); 323362306a36Sopenharmony_ci return -ENOMEM; 323462306a36Sopenharmony_ci } 323562306a36Sopenharmony_ci } 323662306a36Sopenharmony_ci 323762306a36Sopenharmony_ci req->CtlCode = cpu_to_le32(opcode); 323862306a36Sopenharmony_ci req->PersistentFileId = persistent_fid; 323962306a36Sopenharmony_ci req->VolatileFileId = volatile_fid; 324062306a36Sopenharmony_ci 324162306a36Sopenharmony_ci iov[0].iov_base = (char *)req; 324262306a36Sopenharmony_ci /* 324362306a36Sopenharmony_ci * If no input data, the size of ioctl struct in 324462306a36Sopenharmony_ci * protocol spec still includes a 1 byte data buffer, 324562306a36Sopenharmony_ci * but if input data passed to ioctl, we do not 324662306a36Sopenharmony_ci * want to double count this, so we do not send 324762306a36Sopenharmony_ci * the dummy one byte of data in iovec[0] if sending 324862306a36Sopenharmony_ci * input data (in iovec[1]). 324962306a36Sopenharmony_ci */ 325062306a36Sopenharmony_ci if (indatalen) { 325162306a36Sopenharmony_ci req->InputCount = cpu_to_le32(indatalen); 325262306a36Sopenharmony_ci /* do not set InputOffset if no input data */ 325362306a36Sopenharmony_ci req->InputOffset = 325462306a36Sopenharmony_ci cpu_to_le32(offsetof(struct smb2_ioctl_req, Buffer)); 325562306a36Sopenharmony_ci rqst->rq_nvec = 2; 325662306a36Sopenharmony_ci iov[0].iov_len = total_len - 1; 325762306a36Sopenharmony_ci iov[1].iov_base = in_data_buf; 325862306a36Sopenharmony_ci iov[1].iov_len = indatalen; 325962306a36Sopenharmony_ci } else { 326062306a36Sopenharmony_ci rqst->rq_nvec = 1; 326162306a36Sopenharmony_ci iov[0].iov_len = total_len; 326262306a36Sopenharmony_ci } 326362306a36Sopenharmony_ci 326462306a36Sopenharmony_ci req->OutputOffset = 0; 326562306a36Sopenharmony_ci req->OutputCount = 0; /* MBZ */ 326662306a36Sopenharmony_ci 326762306a36Sopenharmony_ci /* 326862306a36Sopenharmony_ci * In most cases max_response_size is set to 16K (CIFSMaxBufSize) 326962306a36Sopenharmony_ci * We Could increase default MaxOutputResponse, but that could require 327062306a36Sopenharmony_ci * more credits. Windows typically sets this smaller, but for some 327162306a36Sopenharmony_ci * ioctls it may be useful to allow server to send more. No point 327262306a36Sopenharmony_ci * limiting what the server can send as long as fits in one credit 327362306a36Sopenharmony_ci * We can not handle more than CIFS_MAX_BUF_SIZE yet but may want 327462306a36Sopenharmony_ci * to increase this limit up in the future. 327562306a36Sopenharmony_ci * Note that for snapshot queries that servers like Azure expect that 327662306a36Sopenharmony_ci * the first query be minimal size (and just used to get the number/size 327762306a36Sopenharmony_ci * of previous versions) so response size must be specified as EXACTLY 327862306a36Sopenharmony_ci * sizeof(struct snapshot_array) which is 16 when rounded up to multiple 327962306a36Sopenharmony_ci * of eight bytes. Currently that is the only case where we set max 328062306a36Sopenharmony_ci * response size smaller. 328162306a36Sopenharmony_ci */ 328262306a36Sopenharmony_ci req->MaxOutputResponse = cpu_to_le32(max_response_size); 328362306a36Sopenharmony_ci req->hdr.CreditCharge = 328462306a36Sopenharmony_ci cpu_to_le16(DIV_ROUND_UP(max(indatalen, max_response_size), 328562306a36Sopenharmony_ci SMB2_MAX_BUFFER_SIZE)); 328662306a36Sopenharmony_ci /* always an FSCTL (for now) */ 328762306a36Sopenharmony_ci req->Flags = cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL); 328862306a36Sopenharmony_ci 328962306a36Sopenharmony_ci /* validate negotiate request must be signed - see MS-SMB2 3.2.5.5 */ 329062306a36Sopenharmony_ci if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO) 329162306a36Sopenharmony_ci req->hdr.Flags |= SMB2_FLAGS_SIGNED; 329262306a36Sopenharmony_ci 329362306a36Sopenharmony_ci return 0; 329462306a36Sopenharmony_ci} 329562306a36Sopenharmony_ci 329662306a36Sopenharmony_civoid 329762306a36Sopenharmony_ciSMB2_ioctl_free(struct smb_rqst *rqst) 329862306a36Sopenharmony_ci{ 329962306a36Sopenharmony_ci int i; 330062306a36Sopenharmony_ci 330162306a36Sopenharmony_ci if (rqst && rqst->rq_iov) { 330262306a36Sopenharmony_ci cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ 330362306a36Sopenharmony_ci for (i = 1; i < rqst->rq_nvec; i++) 330462306a36Sopenharmony_ci if (rqst->rq_iov[i].iov_base != smb2_padding) 330562306a36Sopenharmony_ci kfree(rqst->rq_iov[i].iov_base); 330662306a36Sopenharmony_ci } 330762306a36Sopenharmony_ci} 330862306a36Sopenharmony_ci 330962306a36Sopenharmony_ci 331062306a36Sopenharmony_ci/* 331162306a36Sopenharmony_ci * SMB2 IOCTL is used for both IOCTLs and FSCTLs 331262306a36Sopenharmony_ci */ 331362306a36Sopenharmony_ciint 331462306a36Sopenharmony_ciSMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, 331562306a36Sopenharmony_ci u64 volatile_fid, u32 opcode, char *in_data, u32 indatalen, 331662306a36Sopenharmony_ci u32 max_out_data_len, char **out_data, 331762306a36Sopenharmony_ci u32 *plen /* returned data len */) 331862306a36Sopenharmony_ci{ 331962306a36Sopenharmony_ci struct smb_rqst rqst; 332062306a36Sopenharmony_ci struct smb2_ioctl_rsp *rsp = NULL; 332162306a36Sopenharmony_ci struct cifs_ses *ses; 332262306a36Sopenharmony_ci struct TCP_Server_Info *server; 332362306a36Sopenharmony_ci struct kvec iov[SMB2_IOCTL_IOV_SIZE]; 332462306a36Sopenharmony_ci struct kvec rsp_iov = {NULL, 0}; 332562306a36Sopenharmony_ci int resp_buftype = CIFS_NO_BUFFER; 332662306a36Sopenharmony_ci int rc = 0; 332762306a36Sopenharmony_ci int flags = 0; 332862306a36Sopenharmony_ci 332962306a36Sopenharmony_ci cifs_dbg(FYI, "SMB2 IOCTL\n"); 333062306a36Sopenharmony_ci 333162306a36Sopenharmony_ci if (out_data != NULL) 333262306a36Sopenharmony_ci *out_data = NULL; 333362306a36Sopenharmony_ci 333462306a36Sopenharmony_ci /* zero out returned data len, in case of error */ 333562306a36Sopenharmony_ci if (plen) 333662306a36Sopenharmony_ci *plen = 0; 333762306a36Sopenharmony_ci 333862306a36Sopenharmony_ci if (!tcon) 333962306a36Sopenharmony_ci return -EIO; 334062306a36Sopenharmony_ci 334162306a36Sopenharmony_ci ses = tcon->ses; 334262306a36Sopenharmony_ci if (!ses) 334362306a36Sopenharmony_ci return -EIO; 334462306a36Sopenharmony_ci 334562306a36Sopenharmony_ci server = cifs_pick_channel(ses); 334662306a36Sopenharmony_ci if (!server) 334762306a36Sopenharmony_ci return -EIO; 334862306a36Sopenharmony_ci 334962306a36Sopenharmony_ci if (smb3_encryption_required(tcon)) 335062306a36Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 335162306a36Sopenharmony_ci 335262306a36Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 335362306a36Sopenharmony_ci memset(&iov, 0, sizeof(iov)); 335462306a36Sopenharmony_ci rqst.rq_iov = iov; 335562306a36Sopenharmony_ci rqst.rq_nvec = SMB2_IOCTL_IOV_SIZE; 335662306a36Sopenharmony_ci 335762306a36Sopenharmony_ci rc = SMB2_ioctl_init(tcon, server, 335862306a36Sopenharmony_ci &rqst, persistent_fid, volatile_fid, opcode, 335962306a36Sopenharmony_ci in_data, indatalen, max_out_data_len); 336062306a36Sopenharmony_ci if (rc) 336162306a36Sopenharmony_ci goto ioctl_exit; 336262306a36Sopenharmony_ci 336362306a36Sopenharmony_ci rc = cifs_send_recv(xid, ses, server, 336462306a36Sopenharmony_ci &rqst, &resp_buftype, flags, 336562306a36Sopenharmony_ci &rsp_iov); 336662306a36Sopenharmony_ci rsp = (struct smb2_ioctl_rsp *)rsp_iov.iov_base; 336762306a36Sopenharmony_ci 336862306a36Sopenharmony_ci if (rc != 0) 336962306a36Sopenharmony_ci trace_smb3_fsctl_err(xid, persistent_fid, tcon->tid, 337062306a36Sopenharmony_ci ses->Suid, 0, opcode, rc); 337162306a36Sopenharmony_ci 337262306a36Sopenharmony_ci if ((rc != 0) && (rc != -EINVAL) && (rc != -E2BIG)) { 337362306a36Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); 337462306a36Sopenharmony_ci goto ioctl_exit; 337562306a36Sopenharmony_ci } else if (rc == -EINVAL) { 337662306a36Sopenharmony_ci if ((opcode != FSCTL_SRV_COPYCHUNK_WRITE) && 337762306a36Sopenharmony_ci (opcode != FSCTL_SRV_COPYCHUNK)) { 337862306a36Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); 337962306a36Sopenharmony_ci goto ioctl_exit; 338062306a36Sopenharmony_ci } 338162306a36Sopenharmony_ci } else if (rc == -E2BIG) { 338262306a36Sopenharmony_ci if (opcode != FSCTL_QUERY_ALLOCATED_RANGES) { 338362306a36Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); 338462306a36Sopenharmony_ci goto ioctl_exit; 338562306a36Sopenharmony_ci } 338662306a36Sopenharmony_ci } 338762306a36Sopenharmony_ci 338862306a36Sopenharmony_ci /* check if caller wants to look at return data or just return rc */ 338962306a36Sopenharmony_ci if ((plen == NULL) || (out_data == NULL)) 339062306a36Sopenharmony_ci goto ioctl_exit; 339162306a36Sopenharmony_ci 339262306a36Sopenharmony_ci /* 339362306a36Sopenharmony_ci * Although unlikely to be possible for rsp to be null and rc not set, 339462306a36Sopenharmony_ci * adding check below is slightly safer long term (and quiets Coverity 339562306a36Sopenharmony_ci * warning) 339662306a36Sopenharmony_ci */ 339762306a36Sopenharmony_ci if (rsp == NULL) { 339862306a36Sopenharmony_ci rc = -EIO; 339962306a36Sopenharmony_ci goto ioctl_exit; 340062306a36Sopenharmony_ci } 340162306a36Sopenharmony_ci 340262306a36Sopenharmony_ci *plen = le32_to_cpu(rsp->OutputCount); 340362306a36Sopenharmony_ci 340462306a36Sopenharmony_ci /* We check for obvious errors in the output buffer length and offset */ 340562306a36Sopenharmony_ci if (*plen == 0) 340662306a36Sopenharmony_ci goto ioctl_exit; /* server returned no data */ 340762306a36Sopenharmony_ci else if (*plen > rsp_iov.iov_len || *plen > 0xFF00) { 340862306a36Sopenharmony_ci cifs_tcon_dbg(VFS, "srv returned invalid ioctl length: %d\n", *plen); 340962306a36Sopenharmony_ci *plen = 0; 341062306a36Sopenharmony_ci rc = -EIO; 341162306a36Sopenharmony_ci goto ioctl_exit; 341262306a36Sopenharmony_ci } 341362306a36Sopenharmony_ci 341462306a36Sopenharmony_ci if (rsp_iov.iov_len - *plen < le32_to_cpu(rsp->OutputOffset)) { 341562306a36Sopenharmony_ci cifs_tcon_dbg(VFS, "Malformed ioctl resp: len %d offset %d\n", *plen, 341662306a36Sopenharmony_ci le32_to_cpu(rsp->OutputOffset)); 341762306a36Sopenharmony_ci *plen = 0; 341862306a36Sopenharmony_ci rc = -EIO; 341962306a36Sopenharmony_ci goto ioctl_exit; 342062306a36Sopenharmony_ci } 342162306a36Sopenharmony_ci 342262306a36Sopenharmony_ci *out_data = kmemdup((char *)rsp + le32_to_cpu(rsp->OutputOffset), 342362306a36Sopenharmony_ci *plen, GFP_KERNEL); 342462306a36Sopenharmony_ci if (*out_data == NULL) { 342562306a36Sopenharmony_ci rc = -ENOMEM; 342662306a36Sopenharmony_ci goto ioctl_exit; 342762306a36Sopenharmony_ci } 342862306a36Sopenharmony_ci 342962306a36Sopenharmony_ciioctl_exit: 343062306a36Sopenharmony_ci SMB2_ioctl_free(&rqst); 343162306a36Sopenharmony_ci free_rsp_buf(resp_buftype, rsp); 343262306a36Sopenharmony_ci return rc; 343362306a36Sopenharmony_ci} 343462306a36Sopenharmony_ci 343562306a36Sopenharmony_ci/* 343662306a36Sopenharmony_ci * Individual callers to ioctl worker function follow 343762306a36Sopenharmony_ci */ 343862306a36Sopenharmony_ci 343962306a36Sopenharmony_ciint 344062306a36Sopenharmony_ciSMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, 344162306a36Sopenharmony_ci u64 persistent_fid, u64 volatile_fid) 344262306a36Sopenharmony_ci{ 344362306a36Sopenharmony_ci int rc; 344462306a36Sopenharmony_ci struct compress_ioctl fsctl_input; 344562306a36Sopenharmony_ci char *ret_data = NULL; 344662306a36Sopenharmony_ci 344762306a36Sopenharmony_ci fsctl_input.CompressionState = 344862306a36Sopenharmony_ci cpu_to_le16(COMPRESSION_FORMAT_DEFAULT); 344962306a36Sopenharmony_ci 345062306a36Sopenharmony_ci rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid, 345162306a36Sopenharmony_ci FSCTL_SET_COMPRESSION, 345262306a36Sopenharmony_ci (char *)&fsctl_input /* data input */, 345362306a36Sopenharmony_ci 2 /* in data len */, CIFSMaxBufSize /* max out data */, 345462306a36Sopenharmony_ci &ret_data /* out data */, NULL); 345562306a36Sopenharmony_ci 345662306a36Sopenharmony_ci cifs_dbg(FYI, "set compression rc %d\n", rc); 345762306a36Sopenharmony_ci 345862306a36Sopenharmony_ci return rc; 345962306a36Sopenharmony_ci} 346062306a36Sopenharmony_ci 346162306a36Sopenharmony_ciint 346262306a36Sopenharmony_ciSMB2_close_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, 346362306a36Sopenharmony_ci struct smb_rqst *rqst, 346462306a36Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, bool query_attrs) 346562306a36Sopenharmony_ci{ 346662306a36Sopenharmony_ci struct smb2_close_req *req; 346762306a36Sopenharmony_ci struct kvec *iov = rqst->rq_iov; 346862306a36Sopenharmony_ci unsigned int total_len; 346962306a36Sopenharmony_ci int rc; 347062306a36Sopenharmony_ci 347162306a36Sopenharmony_ci rc = smb2_plain_req_init(SMB2_CLOSE, tcon, server, 347262306a36Sopenharmony_ci (void **) &req, &total_len); 347362306a36Sopenharmony_ci if (rc) 347462306a36Sopenharmony_ci return rc; 347562306a36Sopenharmony_ci 347662306a36Sopenharmony_ci req->PersistentFileId = persistent_fid; 347762306a36Sopenharmony_ci req->VolatileFileId = volatile_fid; 347862306a36Sopenharmony_ci if (query_attrs) 347962306a36Sopenharmony_ci req->Flags = SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB; 348062306a36Sopenharmony_ci else 348162306a36Sopenharmony_ci req->Flags = 0; 348262306a36Sopenharmony_ci iov[0].iov_base = (char *)req; 348362306a36Sopenharmony_ci iov[0].iov_len = total_len; 348462306a36Sopenharmony_ci 348562306a36Sopenharmony_ci return 0; 348662306a36Sopenharmony_ci} 348762306a36Sopenharmony_ci 348862306a36Sopenharmony_civoid 348962306a36Sopenharmony_ciSMB2_close_free(struct smb_rqst *rqst) 349062306a36Sopenharmony_ci{ 349162306a36Sopenharmony_ci if (rqst && rqst->rq_iov) 349262306a36Sopenharmony_ci cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ 349362306a36Sopenharmony_ci} 349462306a36Sopenharmony_ci 349562306a36Sopenharmony_ciint 349662306a36Sopenharmony_ci__SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, 349762306a36Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, 349862306a36Sopenharmony_ci struct smb2_file_network_open_info *pbuf) 349962306a36Sopenharmony_ci{ 350062306a36Sopenharmony_ci struct smb_rqst rqst; 350162306a36Sopenharmony_ci struct smb2_close_rsp *rsp = NULL; 350262306a36Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 350362306a36Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(ses); 350462306a36Sopenharmony_ci struct kvec iov[1]; 350562306a36Sopenharmony_ci struct kvec rsp_iov; 350662306a36Sopenharmony_ci int resp_buftype = CIFS_NO_BUFFER; 350762306a36Sopenharmony_ci int rc = 0; 350862306a36Sopenharmony_ci int flags = 0; 350962306a36Sopenharmony_ci bool query_attrs = false; 351062306a36Sopenharmony_ci 351162306a36Sopenharmony_ci cifs_dbg(FYI, "Close\n"); 351262306a36Sopenharmony_ci 351362306a36Sopenharmony_ci if (!ses || !server) 351462306a36Sopenharmony_ci return -EIO; 351562306a36Sopenharmony_ci 351662306a36Sopenharmony_ci if (smb3_encryption_required(tcon)) 351762306a36Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 351862306a36Sopenharmony_ci 351962306a36Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 352062306a36Sopenharmony_ci memset(&iov, 0, sizeof(iov)); 352162306a36Sopenharmony_ci rqst.rq_iov = iov; 352262306a36Sopenharmony_ci rqst.rq_nvec = 1; 352362306a36Sopenharmony_ci 352462306a36Sopenharmony_ci /* check if need to ask server to return timestamps in close response */ 352562306a36Sopenharmony_ci if (pbuf) 352662306a36Sopenharmony_ci query_attrs = true; 352762306a36Sopenharmony_ci 352862306a36Sopenharmony_ci trace_smb3_close_enter(xid, persistent_fid, tcon->tid, ses->Suid); 352962306a36Sopenharmony_ci rc = SMB2_close_init(tcon, server, 353062306a36Sopenharmony_ci &rqst, persistent_fid, volatile_fid, 353162306a36Sopenharmony_ci query_attrs); 353262306a36Sopenharmony_ci if (rc) 353362306a36Sopenharmony_ci goto close_exit; 353462306a36Sopenharmony_ci 353562306a36Sopenharmony_ci rc = cifs_send_recv(xid, ses, server, 353662306a36Sopenharmony_ci &rqst, &resp_buftype, flags, &rsp_iov); 353762306a36Sopenharmony_ci rsp = (struct smb2_close_rsp *)rsp_iov.iov_base; 353862306a36Sopenharmony_ci 353962306a36Sopenharmony_ci if (rc != 0) { 354062306a36Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_CLOSE_HE); 354162306a36Sopenharmony_ci trace_smb3_close_err(xid, persistent_fid, tcon->tid, ses->Suid, 354262306a36Sopenharmony_ci rc); 354362306a36Sopenharmony_ci goto close_exit; 354462306a36Sopenharmony_ci } else { 354562306a36Sopenharmony_ci trace_smb3_close_done(xid, persistent_fid, tcon->tid, 354662306a36Sopenharmony_ci ses->Suid); 354762306a36Sopenharmony_ci if (pbuf) 354862306a36Sopenharmony_ci memcpy(&pbuf->network_open_info, 354962306a36Sopenharmony_ci &rsp->network_open_info, 355062306a36Sopenharmony_ci sizeof(pbuf->network_open_info)); 355162306a36Sopenharmony_ci } 355262306a36Sopenharmony_ci 355362306a36Sopenharmony_ci atomic_dec(&tcon->num_remote_opens); 355462306a36Sopenharmony_ciclose_exit: 355562306a36Sopenharmony_ci SMB2_close_free(&rqst); 355662306a36Sopenharmony_ci free_rsp_buf(resp_buftype, rsp); 355762306a36Sopenharmony_ci 355862306a36Sopenharmony_ci /* retry close in a worker thread if this one is interrupted */ 355962306a36Sopenharmony_ci if (is_interrupt_error(rc)) { 356062306a36Sopenharmony_ci int tmp_rc; 356162306a36Sopenharmony_ci 356262306a36Sopenharmony_ci tmp_rc = smb2_handle_cancelled_close(tcon, persistent_fid, 356362306a36Sopenharmony_ci volatile_fid); 356462306a36Sopenharmony_ci if (tmp_rc) 356562306a36Sopenharmony_ci cifs_dbg(VFS, "handle cancelled close fid 0x%llx returned error %d\n", 356662306a36Sopenharmony_ci persistent_fid, tmp_rc); 356762306a36Sopenharmony_ci } 356862306a36Sopenharmony_ci return rc; 356962306a36Sopenharmony_ci} 357062306a36Sopenharmony_ci 357162306a36Sopenharmony_ciint 357262306a36Sopenharmony_ciSMB2_close(const unsigned int xid, struct cifs_tcon *tcon, 357362306a36Sopenharmony_ci u64 persistent_fid, u64 volatile_fid) 357462306a36Sopenharmony_ci{ 357562306a36Sopenharmony_ci return __SMB2_close(xid, tcon, persistent_fid, volatile_fid, NULL); 357662306a36Sopenharmony_ci} 357762306a36Sopenharmony_ci 357862306a36Sopenharmony_ciint 357962306a36Sopenharmony_cismb2_validate_iov(unsigned int offset, unsigned int buffer_length, 358062306a36Sopenharmony_ci struct kvec *iov, unsigned int min_buf_size) 358162306a36Sopenharmony_ci{ 358262306a36Sopenharmony_ci unsigned int smb_len = iov->iov_len; 358362306a36Sopenharmony_ci char *end_of_smb = smb_len + (char *)iov->iov_base; 358462306a36Sopenharmony_ci char *begin_of_buf = offset + (char *)iov->iov_base; 358562306a36Sopenharmony_ci char *end_of_buf = begin_of_buf + buffer_length; 358662306a36Sopenharmony_ci 358762306a36Sopenharmony_ci 358862306a36Sopenharmony_ci if (buffer_length < min_buf_size) { 358962306a36Sopenharmony_ci cifs_dbg(VFS, "buffer length %d smaller than minimum size %d\n", 359062306a36Sopenharmony_ci buffer_length, min_buf_size); 359162306a36Sopenharmony_ci return -EINVAL; 359262306a36Sopenharmony_ci } 359362306a36Sopenharmony_ci 359462306a36Sopenharmony_ci /* check if beyond RFC1001 maximum length */ 359562306a36Sopenharmony_ci if ((smb_len > 0x7FFFFF) || (buffer_length > 0x7FFFFF)) { 359662306a36Sopenharmony_ci cifs_dbg(VFS, "buffer length %d or smb length %d too large\n", 359762306a36Sopenharmony_ci buffer_length, smb_len); 359862306a36Sopenharmony_ci return -EINVAL; 359962306a36Sopenharmony_ci } 360062306a36Sopenharmony_ci 360162306a36Sopenharmony_ci if ((begin_of_buf > end_of_smb) || (end_of_buf > end_of_smb)) { 360262306a36Sopenharmony_ci cifs_dbg(VFS, "Invalid server response, bad offset to data\n"); 360362306a36Sopenharmony_ci return -EINVAL; 360462306a36Sopenharmony_ci } 360562306a36Sopenharmony_ci 360662306a36Sopenharmony_ci return 0; 360762306a36Sopenharmony_ci} 360862306a36Sopenharmony_ci 360962306a36Sopenharmony_ci/* 361062306a36Sopenharmony_ci * If SMB buffer fields are valid, copy into temporary buffer to hold result. 361162306a36Sopenharmony_ci * Caller must free buffer. 361262306a36Sopenharmony_ci */ 361362306a36Sopenharmony_ciint 361462306a36Sopenharmony_cismb2_validate_and_copy_iov(unsigned int offset, unsigned int buffer_length, 361562306a36Sopenharmony_ci struct kvec *iov, unsigned int minbufsize, 361662306a36Sopenharmony_ci char *data) 361762306a36Sopenharmony_ci{ 361862306a36Sopenharmony_ci char *begin_of_buf = offset + (char *)iov->iov_base; 361962306a36Sopenharmony_ci int rc; 362062306a36Sopenharmony_ci 362162306a36Sopenharmony_ci if (!data) 362262306a36Sopenharmony_ci return -EINVAL; 362362306a36Sopenharmony_ci 362462306a36Sopenharmony_ci rc = smb2_validate_iov(offset, buffer_length, iov, minbufsize); 362562306a36Sopenharmony_ci if (rc) 362662306a36Sopenharmony_ci return rc; 362762306a36Sopenharmony_ci 362862306a36Sopenharmony_ci memcpy(data, begin_of_buf, minbufsize); 362962306a36Sopenharmony_ci 363062306a36Sopenharmony_ci return 0; 363162306a36Sopenharmony_ci} 363262306a36Sopenharmony_ci 363362306a36Sopenharmony_ciint 363462306a36Sopenharmony_ciSMB2_query_info_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, 363562306a36Sopenharmony_ci struct smb_rqst *rqst, 363662306a36Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, 363762306a36Sopenharmony_ci u8 info_class, u8 info_type, u32 additional_info, 363862306a36Sopenharmony_ci size_t output_len, size_t input_len, void *input) 363962306a36Sopenharmony_ci{ 364062306a36Sopenharmony_ci struct smb2_query_info_req *req; 364162306a36Sopenharmony_ci struct kvec *iov = rqst->rq_iov; 364262306a36Sopenharmony_ci unsigned int total_len; 364362306a36Sopenharmony_ci size_t len; 364462306a36Sopenharmony_ci int rc; 364562306a36Sopenharmony_ci 364662306a36Sopenharmony_ci if (unlikely(check_add_overflow(input_len, sizeof(*req), &len) || 364762306a36Sopenharmony_ci len > CIFSMaxBufSize)) 364862306a36Sopenharmony_ci return -EINVAL; 364962306a36Sopenharmony_ci 365062306a36Sopenharmony_ci rc = smb2_plain_req_init(SMB2_QUERY_INFO, tcon, server, 365162306a36Sopenharmony_ci (void **) &req, &total_len); 365262306a36Sopenharmony_ci if (rc) 365362306a36Sopenharmony_ci return rc; 365462306a36Sopenharmony_ci 365562306a36Sopenharmony_ci req->InfoType = info_type; 365662306a36Sopenharmony_ci req->FileInfoClass = info_class; 365762306a36Sopenharmony_ci req->PersistentFileId = persistent_fid; 365862306a36Sopenharmony_ci req->VolatileFileId = volatile_fid; 365962306a36Sopenharmony_ci req->AdditionalInformation = cpu_to_le32(additional_info); 366062306a36Sopenharmony_ci 366162306a36Sopenharmony_ci req->OutputBufferLength = cpu_to_le32(output_len); 366262306a36Sopenharmony_ci if (input_len) { 366362306a36Sopenharmony_ci req->InputBufferLength = cpu_to_le32(input_len); 366462306a36Sopenharmony_ci /* total_len for smb query request never close to le16 max */ 366562306a36Sopenharmony_ci req->InputBufferOffset = cpu_to_le16(total_len - 1); 366662306a36Sopenharmony_ci memcpy(req->Buffer, input, input_len); 366762306a36Sopenharmony_ci } 366862306a36Sopenharmony_ci 366962306a36Sopenharmony_ci iov[0].iov_base = (char *)req; 367062306a36Sopenharmony_ci /* 1 for Buffer */ 367162306a36Sopenharmony_ci iov[0].iov_len = len; 367262306a36Sopenharmony_ci return 0; 367362306a36Sopenharmony_ci} 367462306a36Sopenharmony_ci 367562306a36Sopenharmony_civoid 367662306a36Sopenharmony_ciSMB2_query_info_free(struct smb_rqst *rqst) 367762306a36Sopenharmony_ci{ 367862306a36Sopenharmony_ci if (rqst && rqst->rq_iov) 367962306a36Sopenharmony_ci cifs_buf_release(rqst->rq_iov[0].iov_base); /* request */ 368062306a36Sopenharmony_ci} 368162306a36Sopenharmony_ci 368262306a36Sopenharmony_cistatic int 368362306a36Sopenharmony_ciquery_info(const unsigned int xid, struct cifs_tcon *tcon, 368462306a36Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, u8 info_class, u8 info_type, 368562306a36Sopenharmony_ci u32 additional_info, size_t output_len, size_t min_len, void **data, 368662306a36Sopenharmony_ci u32 *dlen) 368762306a36Sopenharmony_ci{ 368862306a36Sopenharmony_ci struct smb_rqst rqst; 368962306a36Sopenharmony_ci struct smb2_query_info_rsp *rsp = NULL; 369062306a36Sopenharmony_ci struct kvec iov[1]; 369162306a36Sopenharmony_ci struct kvec rsp_iov; 369262306a36Sopenharmony_ci int rc = 0; 369362306a36Sopenharmony_ci int resp_buftype = CIFS_NO_BUFFER; 369462306a36Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 369562306a36Sopenharmony_ci struct TCP_Server_Info *server; 369662306a36Sopenharmony_ci int flags = 0; 369762306a36Sopenharmony_ci bool allocated = false; 369862306a36Sopenharmony_ci 369962306a36Sopenharmony_ci cifs_dbg(FYI, "Query Info\n"); 370062306a36Sopenharmony_ci 370162306a36Sopenharmony_ci if (!ses) 370262306a36Sopenharmony_ci return -EIO; 370362306a36Sopenharmony_ci server = cifs_pick_channel(ses); 370462306a36Sopenharmony_ci if (!server) 370562306a36Sopenharmony_ci return -EIO; 370662306a36Sopenharmony_ci 370762306a36Sopenharmony_ci if (smb3_encryption_required(tcon)) 370862306a36Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 370962306a36Sopenharmony_ci 371062306a36Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 371162306a36Sopenharmony_ci memset(&iov, 0, sizeof(iov)); 371262306a36Sopenharmony_ci rqst.rq_iov = iov; 371362306a36Sopenharmony_ci rqst.rq_nvec = 1; 371462306a36Sopenharmony_ci 371562306a36Sopenharmony_ci rc = SMB2_query_info_init(tcon, server, 371662306a36Sopenharmony_ci &rqst, persistent_fid, volatile_fid, 371762306a36Sopenharmony_ci info_class, info_type, additional_info, 371862306a36Sopenharmony_ci output_len, 0, NULL); 371962306a36Sopenharmony_ci if (rc) 372062306a36Sopenharmony_ci goto qinf_exit; 372162306a36Sopenharmony_ci 372262306a36Sopenharmony_ci trace_smb3_query_info_enter(xid, persistent_fid, tcon->tid, 372362306a36Sopenharmony_ci ses->Suid, info_class, (__u32)info_type); 372462306a36Sopenharmony_ci 372562306a36Sopenharmony_ci rc = cifs_send_recv(xid, ses, server, 372662306a36Sopenharmony_ci &rqst, &resp_buftype, flags, &rsp_iov); 372762306a36Sopenharmony_ci rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; 372862306a36Sopenharmony_ci 372962306a36Sopenharmony_ci if (rc) { 373062306a36Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); 373162306a36Sopenharmony_ci trace_smb3_query_info_err(xid, persistent_fid, tcon->tid, 373262306a36Sopenharmony_ci ses->Suid, info_class, (__u32)info_type, rc); 373362306a36Sopenharmony_ci goto qinf_exit; 373462306a36Sopenharmony_ci } 373562306a36Sopenharmony_ci 373662306a36Sopenharmony_ci trace_smb3_query_info_done(xid, persistent_fid, tcon->tid, 373762306a36Sopenharmony_ci ses->Suid, info_class, (__u32)info_type); 373862306a36Sopenharmony_ci 373962306a36Sopenharmony_ci if (dlen) { 374062306a36Sopenharmony_ci *dlen = le32_to_cpu(rsp->OutputBufferLength); 374162306a36Sopenharmony_ci if (!*data) { 374262306a36Sopenharmony_ci *data = kmalloc(*dlen, GFP_KERNEL); 374362306a36Sopenharmony_ci if (!*data) { 374462306a36Sopenharmony_ci cifs_tcon_dbg(VFS, 374562306a36Sopenharmony_ci "Error %d allocating memory for acl\n", 374662306a36Sopenharmony_ci rc); 374762306a36Sopenharmony_ci *dlen = 0; 374862306a36Sopenharmony_ci rc = -ENOMEM; 374962306a36Sopenharmony_ci goto qinf_exit; 375062306a36Sopenharmony_ci } 375162306a36Sopenharmony_ci allocated = true; 375262306a36Sopenharmony_ci } 375362306a36Sopenharmony_ci } 375462306a36Sopenharmony_ci 375562306a36Sopenharmony_ci rc = smb2_validate_and_copy_iov(le16_to_cpu(rsp->OutputBufferOffset), 375662306a36Sopenharmony_ci le32_to_cpu(rsp->OutputBufferLength), 375762306a36Sopenharmony_ci &rsp_iov, dlen ? *dlen : min_len, *data); 375862306a36Sopenharmony_ci if (rc && allocated) { 375962306a36Sopenharmony_ci kfree(*data); 376062306a36Sopenharmony_ci *data = NULL; 376162306a36Sopenharmony_ci *dlen = 0; 376262306a36Sopenharmony_ci } 376362306a36Sopenharmony_ci 376462306a36Sopenharmony_ciqinf_exit: 376562306a36Sopenharmony_ci SMB2_query_info_free(&rqst); 376662306a36Sopenharmony_ci free_rsp_buf(resp_buftype, rsp); 376762306a36Sopenharmony_ci return rc; 376862306a36Sopenharmony_ci} 376962306a36Sopenharmony_ci 377062306a36Sopenharmony_ciint SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, 377162306a36Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, struct smb2_file_all_info *data) 377262306a36Sopenharmony_ci{ 377362306a36Sopenharmony_ci return query_info(xid, tcon, persistent_fid, volatile_fid, 377462306a36Sopenharmony_ci FILE_ALL_INFORMATION, SMB2_O_INFO_FILE, 0, 377562306a36Sopenharmony_ci sizeof(struct smb2_file_all_info) + PATH_MAX * 2, 377662306a36Sopenharmony_ci sizeof(struct smb2_file_all_info), (void **)&data, 377762306a36Sopenharmony_ci NULL); 377862306a36Sopenharmony_ci} 377962306a36Sopenharmony_ci 378062306a36Sopenharmony_ci#if 0 378162306a36Sopenharmony_ci/* currently unused, as now we are doing compounding instead (see smb311_posix_query_path_info) */ 378262306a36Sopenharmony_ciint 378362306a36Sopenharmony_ciSMB311_posix_query_info(const unsigned int xid, struct cifs_tcon *tcon, 378462306a36Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, struct smb311_posix_qinfo *data, u32 *plen) 378562306a36Sopenharmony_ci{ 378662306a36Sopenharmony_ci size_t output_len = sizeof(struct smb311_posix_qinfo *) + 378762306a36Sopenharmony_ci (sizeof(struct cifs_sid) * 2) + (PATH_MAX * 2); 378862306a36Sopenharmony_ci *plen = 0; 378962306a36Sopenharmony_ci 379062306a36Sopenharmony_ci return query_info(xid, tcon, persistent_fid, volatile_fid, 379162306a36Sopenharmony_ci SMB_FIND_FILE_POSIX_INFO, SMB2_O_INFO_FILE, 0, 379262306a36Sopenharmony_ci output_len, sizeof(struct smb311_posix_qinfo), (void **)&data, plen); 379362306a36Sopenharmony_ci /* Note caller must free "data" (passed in above). It may be allocated in query_info call */ 379462306a36Sopenharmony_ci} 379562306a36Sopenharmony_ci#endif 379662306a36Sopenharmony_ci 379762306a36Sopenharmony_ciint 379862306a36Sopenharmony_ciSMB2_query_acl(const unsigned int xid, struct cifs_tcon *tcon, 379962306a36Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, 380062306a36Sopenharmony_ci void **data, u32 *plen, u32 extra_info) 380162306a36Sopenharmony_ci{ 380262306a36Sopenharmony_ci __u32 additional_info = OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO | 380362306a36Sopenharmony_ci extra_info; 380462306a36Sopenharmony_ci *plen = 0; 380562306a36Sopenharmony_ci 380662306a36Sopenharmony_ci return query_info(xid, tcon, persistent_fid, volatile_fid, 380762306a36Sopenharmony_ci 0, SMB2_O_INFO_SECURITY, additional_info, 380862306a36Sopenharmony_ci SMB2_MAX_BUFFER_SIZE, MIN_SEC_DESC_LEN, data, plen); 380962306a36Sopenharmony_ci} 381062306a36Sopenharmony_ci 381162306a36Sopenharmony_ciint 381262306a36Sopenharmony_ciSMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon, 381362306a36Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, __le64 *uniqueid) 381462306a36Sopenharmony_ci{ 381562306a36Sopenharmony_ci return query_info(xid, tcon, persistent_fid, volatile_fid, 381662306a36Sopenharmony_ci FILE_INTERNAL_INFORMATION, SMB2_O_INFO_FILE, 0, 381762306a36Sopenharmony_ci sizeof(struct smb2_file_internal_info), 381862306a36Sopenharmony_ci sizeof(struct smb2_file_internal_info), 381962306a36Sopenharmony_ci (void **)&uniqueid, NULL); 382062306a36Sopenharmony_ci} 382162306a36Sopenharmony_ci 382262306a36Sopenharmony_ci/* 382362306a36Sopenharmony_ci * CHANGE_NOTIFY Request is sent to get notifications on changes to a directory 382462306a36Sopenharmony_ci * See MS-SMB2 2.2.35 and 2.2.36 382562306a36Sopenharmony_ci */ 382662306a36Sopenharmony_ci 382762306a36Sopenharmony_cistatic int 382862306a36Sopenharmony_ciSMB2_notify_init(const unsigned int xid, struct smb_rqst *rqst, 382962306a36Sopenharmony_ci struct cifs_tcon *tcon, struct TCP_Server_Info *server, 383062306a36Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, 383162306a36Sopenharmony_ci u32 completion_filter, bool watch_tree) 383262306a36Sopenharmony_ci{ 383362306a36Sopenharmony_ci struct smb2_change_notify_req *req; 383462306a36Sopenharmony_ci struct kvec *iov = rqst->rq_iov; 383562306a36Sopenharmony_ci unsigned int total_len; 383662306a36Sopenharmony_ci int rc; 383762306a36Sopenharmony_ci 383862306a36Sopenharmony_ci rc = smb2_plain_req_init(SMB2_CHANGE_NOTIFY, tcon, server, 383962306a36Sopenharmony_ci (void **) &req, &total_len); 384062306a36Sopenharmony_ci if (rc) 384162306a36Sopenharmony_ci return rc; 384262306a36Sopenharmony_ci 384362306a36Sopenharmony_ci req->PersistentFileId = persistent_fid; 384462306a36Sopenharmony_ci req->VolatileFileId = volatile_fid; 384562306a36Sopenharmony_ci /* See note 354 of MS-SMB2, 64K max */ 384662306a36Sopenharmony_ci req->OutputBufferLength = 384762306a36Sopenharmony_ci cpu_to_le32(SMB2_MAX_BUFFER_SIZE - MAX_SMB2_HDR_SIZE); 384862306a36Sopenharmony_ci req->CompletionFilter = cpu_to_le32(completion_filter); 384962306a36Sopenharmony_ci if (watch_tree) 385062306a36Sopenharmony_ci req->Flags = cpu_to_le16(SMB2_WATCH_TREE); 385162306a36Sopenharmony_ci else 385262306a36Sopenharmony_ci req->Flags = 0; 385362306a36Sopenharmony_ci 385462306a36Sopenharmony_ci iov[0].iov_base = (char *)req; 385562306a36Sopenharmony_ci iov[0].iov_len = total_len; 385662306a36Sopenharmony_ci 385762306a36Sopenharmony_ci return 0; 385862306a36Sopenharmony_ci} 385962306a36Sopenharmony_ci 386062306a36Sopenharmony_ciint 386162306a36Sopenharmony_ciSMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon, 386262306a36Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, bool watch_tree, 386362306a36Sopenharmony_ci u32 completion_filter, u32 max_out_data_len, char **out_data, 386462306a36Sopenharmony_ci u32 *plen /* returned data len */) 386562306a36Sopenharmony_ci{ 386662306a36Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 386762306a36Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(ses); 386862306a36Sopenharmony_ci struct smb_rqst rqst; 386962306a36Sopenharmony_ci struct smb2_change_notify_rsp *smb_rsp; 387062306a36Sopenharmony_ci struct kvec iov[1]; 387162306a36Sopenharmony_ci struct kvec rsp_iov = {NULL, 0}; 387262306a36Sopenharmony_ci int resp_buftype = CIFS_NO_BUFFER; 387362306a36Sopenharmony_ci int flags = 0; 387462306a36Sopenharmony_ci int rc = 0; 387562306a36Sopenharmony_ci 387662306a36Sopenharmony_ci cifs_dbg(FYI, "change notify\n"); 387762306a36Sopenharmony_ci if (!ses || !server) 387862306a36Sopenharmony_ci return -EIO; 387962306a36Sopenharmony_ci 388062306a36Sopenharmony_ci if (smb3_encryption_required(tcon)) 388162306a36Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 388262306a36Sopenharmony_ci 388362306a36Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 388462306a36Sopenharmony_ci memset(&iov, 0, sizeof(iov)); 388562306a36Sopenharmony_ci if (plen) 388662306a36Sopenharmony_ci *plen = 0; 388762306a36Sopenharmony_ci 388862306a36Sopenharmony_ci rqst.rq_iov = iov; 388962306a36Sopenharmony_ci rqst.rq_nvec = 1; 389062306a36Sopenharmony_ci 389162306a36Sopenharmony_ci rc = SMB2_notify_init(xid, &rqst, tcon, server, 389262306a36Sopenharmony_ci persistent_fid, volatile_fid, 389362306a36Sopenharmony_ci completion_filter, watch_tree); 389462306a36Sopenharmony_ci if (rc) 389562306a36Sopenharmony_ci goto cnotify_exit; 389662306a36Sopenharmony_ci 389762306a36Sopenharmony_ci trace_smb3_notify_enter(xid, persistent_fid, tcon->tid, ses->Suid, 389862306a36Sopenharmony_ci (u8)watch_tree, completion_filter); 389962306a36Sopenharmony_ci rc = cifs_send_recv(xid, ses, server, 390062306a36Sopenharmony_ci &rqst, &resp_buftype, flags, &rsp_iov); 390162306a36Sopenharmony_ci 390262306a36Sopenharmony_ci if (rc != 0) { 390362306a36Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_CHANGE_NOTIFY_HE); 390462306a36Sopenharmony_ci trace_smb3_notify_err(xid, persistent_fid, tcon->tid, ses->Suid, 390562306a36Sopenharmony_ci (u8)watch_tree, completion_filter, rc); 390662306a36Sopenharmony_ci } else { 390762306a36Sopenharmony_ci trace_smb3_notify_done(xid, persistent_fid, tcon->tid, 390862306a36Sopenharmony_ci ses->Suid, (u8)watch_tree, completion_filter); 390962306a36Sopenharmony_ci /* validate that notify information is plausible */ 391062306a36Sopenharmony_ci if ((rsp_iov.iov_base == NULL) || 391162306a36Sopenharmony_ci (rsp_iov.iov_len < sizeof(struct smb2_change_notify_rsp) + 1)) 391262306a36Sopenharmony_ci goto cnotify_exit; 391362306a36Sopenharmony_ci 391462306a36Sopenharmony_ci smb_rsp = (struct smb2_change_notify_rsp *)rsp_iov.iov_base; 391562306a36Sopenharmony_ci 391662306a36Sopenharmony_ci smb2_validate_iov(le16_to_cpu(smb_rsp->OutputBufferOffset), 391762306a36Sopenharmony_ci le32_to_cpu(smb_rsp->OutputBufferLength), &rsp_iov, 391862306a36Sopenharmony_ci sizeof(struct file_notify_information)); 391962306a36Sopenharmony_ci 392062306a36Sopenharmony_ci *out_data = kmemdup((char *)smb_rsp + le16_to_cpu(smb_rsp->OutputBufferOffset), 392162306a36Sopenharmony_ci le32_to_cpu(smb_rsp->OutputBufferLength), GFP_KERNEL); 392262306a36Sopenharmony_ci if (*out_data == NULL) { 392362306a36Sopenharmony_ci rc = -ENOMEM; 392462306a36Sopenharmony_ci goto cnotify_exit; 392562306a36Sopenharmony_ci } else if (plen) 392662306a36Sopenharmony_ci *plen = le32_to_cpu(smb_rsp->OutputBufferLength); 392762306a36Sopenharmony_ci } 392862306a36Sopenharmony_ci 392962306a36Sopenharmony_ci cnotify_exit: 393062306a36Sopenharmony_ci if (rqst.rq_iov) 393162306a36Sopenharmony_ci cifs_small_buf_release(rqst.rq_iov[0].iov_base); /* request */ 393262306a36Sopenharmony_ci free_rsp_buf(resp_buftype, rsp_iov.iov_base); 393362306a36Sopenharmony_ci return rc; 393462306a36Sopenharmony_ci} 393562306a36Sopenharmony_ci 393662306a36Sopenharmony_ci 393762306a36Sopenharmony_ci 393862306a36Sopenharmony_ci/* 393962306a36Sopenharmony_ci * This is a no-op for now. We're not really interested in the reply, but 394062306a36Sopenharmony_ci * rather in the fact that the server sent one and that server->lstrp 394162306a36Sopenharmony_ci * gets updated. 394262306a36Sopenharmony_ci * 394362306a36Sopenharmony_ci * FIXME: maybe we should consider checking that the reply matches request? 394462306a36Sopenharmony_ci */ 394562306a36Sopenharmony_cistatic void 394662306a36Sopenharmony_cismb2_echo_callback(struct mid_q_entry *mid) 394762306a36Sopenharmony_ci{ 394862306a36Sopenharmony_ci struct TCP_Server_Info *server = mid->callback_data; 394962306a36Sopenharmony_ci struct smb2_echo_rsp *rsp = (struct smb2_echo_rsp *)mid->resp_buf; 395062306a36Sopenharmony_ci struct cifs_credits credits = { .value = 0, .instance = 0 }; 395162306a36Sopenharmony_ci 395262306a36Sopenharmony_ci if (mid->mid_state == MID_RESPONSE_RECEIVED 395362306a36Sopenharmony_ci || mid->mid_state == MID_RESPONSE_MALFORMED) { 395462306a36Sopenharmony_ci credits.value = le16_to_cpu(rsp->hdr.CreditRequest); 395562306a36Sopenharmony_ci credits.instance = server->reconnect_instance; 395662306a36Sopenharmony_ci } 395762306a36Sopenharmony_ci 395862306a36Sopenharmony_ci release_mid(mid); 395962306a36Sopenharmony_ci add_credits(server, &credits, CIFS_ECHO_OP); 396062306a36Sopenharmony_ci} 396162306a36Sopenharmony_ci 396262306a36Sopenharmony_civoid smb2_reconnect_server(struct work_struct *work) 396362306a36Sopenharmony_ci{ 396462306a36Sopenharmony_ci struct TCP_Server_Info *server = container_of(work, 396562306a36Sopenharmony_ci struct TCP_Server_Info, reconnect.work); 396662306a36Sopenharmony_ci struct TCP_Server_Info *pserver; 396762306a36Sopenharmony_ci struct cifs_ses *ses, *ses2; 396862306a36Sopenharmony_ci struct cifs_tcon *tcon, *tcon2; 396962306a36Sopenharmony_ci struct list_head tmp_list, tmp_ses_list; 397062306a36Sopenharmony_ci bool tcon_exist = false, ses_exist = false; 397162306a36Sopenharmony_ci bool tcon_selected = false; 397262306a36Sopenharmony_ci int rc; 397362306a36Sopenharmony_ci bool resched = false; 397462306a36Sopenharmony_ci 397562306a36Sopenharmony_ci /* first check if ref count has reached 0, if not inc ref count */ 397662306a36Sopenharmony_ci spin_lock(&cifs_tcp_ses_lock); 397762306a36Sopenharmony_ci if (!server->srv_count) { 397862306a36Sopenharmony_ci spin_unlock(&cifs_tcp_ses_lock); 397962306a36Sopenharmony_ci return; 398062306a36Sopenharmony_ci } 398162306a36Sopenharmony_ci server->srv_count++; 398262306a36Sopenharmony_ci spin_unlock(&cifs_tcp_ses_lock); 398362306a36Sopenharmony_ci 398462306a36Sopenharmony_ci /* If server is a channel, select the primary channel */ 398562306a36Sopenharmony_ci pserver = SERVER_IS_CHAN(server) ? server->primary_server : server; 398662306a36Sopenharmony_ci 398762306a36Sopenharmony_ci /* Prevent simultaneous reconnects that can corrupt tcon->rlist list */ 398862306a36Sopenharmony_ci mutex_lock(&pserver->reconnect_mutex); 398962306a36Sopenharmony_ci 399062306a36Sopenharmony_ci /* if the server is marked for termination, drop the ref count here */ 399162306a36Sopenharmony_ci if (server->terminate) { 399262306a36Sopenharmony_ci cifs_put_tcp_session(server, true); 399362306a36Sopenharmony_ci mutex_unlock(&pserver->reconnect_mutex); 399462306a36Sopenharmony_ci return; 399562306a36Sopenharmony_ci } 399662306a36Sopenharmony_ci 399762306a36Sopenharmony_ci INIT_LIST_HEAD(&tmp_list); 399862306a36Sopenharmony_ci INIT_LIST_HEAD(&tmp_ses_list); 399962306a36Sopenharmony_ci cifs_dbg(FYI, "Reconnecting tcons and channels\n"); 400062306a36Sopenharmony_ci 400162306a36Sopenharmony_ci spin_lock(&cifs_tcp_ses_lock); 400262306a36Sopenharmony_ci list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { 400362306a36Sopenharmony_ci spin_lock(&ses->ses_lock); 400462306a36Sopenharmony_ci if (ses->ses_status == SES_EXITING) { 400562306a36Sopenharmony_ci spin_unlock(&ses->ses_lock); 400662306a36Sopenharmony_ci continue; 400762306a36Sopenharmony_ci } 400862306a36Sopenharmony_ci spin_unlock(&ses->ses_lock); 400962306a36Sopenharmony_ci 401062306a36Sopenharmony_ci tcon_selected = false; 401162306a36Sopenharmony_ci 401262306a36Sopenharmony_ci list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { 401362306a36Sopenharmony_ci if (tcon->need_reconnect || tcon->need_reopen_files) { 401462306a36Sopenharmony_ci tcon->tc_count++; 401562306a36Sopenharmony_ci list_add_tail(&tcon->rlist, &tmp_list); 401662306a36Sopenharmony_ci tcon_selected = tcon_exist = true; 401762306a36Sopenharmony_ci } 401862306a36Sopenharmony_ci } 401962306a36Sopenharmony_ci /* 402062306a36Sopenharmony_ci * IPC has the same lifetime as its session and uses its 402162306a36Sopenharmony_ci * refcount. 402262306a36Sopenharmony_ci */ 402362306a36Sopenharmony_ci if (ses->tcon_ipc && ses->tcon_ipc->need_reconnect) { 402462306a36Sopenharmony_ci list_add_tail(&ses->tcon_ipc->rlist, &tmp_list); 402562306a36Sopenharmony_ci tcon_selected = tcon_exist = true; 402662306a36Sopenharmony_ci cifs_smb_ses_inc_refcount(ses); 402762306a36Sopenharmony_ci } 402862306a36Sopenharmony_ci /* 402962306a36Sopenharmony_ci * handle the case where channel needs to reconnect 403062306a36Sopenharmony_ci * binding session, but tcon is healthy (some other channel 403162306a36Sopenharmony_ci * is active) 403262306a36Sopenharmony_ci */ 403362306a36Sopenharmony_ci spin_lock(&ses->chan_lock); 403462306a36Sopenharmony_ci if (!tcon_selected && cifs_chan_needs_reconnect(ses, server)) { 403562306a36Sopenharmony_ci list_add_tail(&ses->rlist, &tmp_ses_list); 403662306a36Sopenharmony_ci ses_exist = true; 403762306a36Sopenharmony_ci cifs_smb_ses_inc_refcount(ses); 403862306a36Sopenharmony_ci } 403962306a36Sopenharmony_ci spin_unlock(&ses->chan_lock); 404062306a36Sopenharmony_ci } 404162306a36Sopenharmony_ci spin_unlock(&cifs_tcp_ses_lock); 404262306a36Sopenharmony_ci 404362306a36Sopenharmony_ci list_for_each_entry_safe(tcon, tcon2, &tmp_list, rlist) { 404462306a36Sopenharmony_ci rc = smb2_reconnect(SMB2_INTERNAL_CMD, tcon, server, true); 404562306a36Sopenharmony_ci if (!rc) 404662306a36Sopenharmony_ci cifs_reopen_persistent_handles(tcon); 404762306a36Sopenharmony_ci else 404862306a36Sopenharmony_ci resched = true; 404962306a36Sopenharmony_ci list_del_init(&tcon->rlist); 405062306a36Sopenharmony_ci if (tcon->ipc) 405162306a36Sopenharmony_ci cifs_put_smb_ses(tcon->ses); 405262306a36Sopenharmony_ci else 405362306a36Sopenharmony_ci cifs_put_tcon(tcon); 405462306a36Sopenharmony_ci } 405562306a36Sopenharmony_ci 405662306a36Sopenharmony_ci if (!ses_exist) 405762306a36Sopenharmony_ci goto done; 405862306a36Sopenharmony_ci 405962306a36Sopenharmony_ci /* allocate a dummy tcon struct used for reconnect */ 406062306a36Sopenharmony_ci tcon = tcon_info_alloc(false); 406162306a36Sopenharmony_ci if (!tcon) { 406262306a36Sopenharmony_ci resched = true; 406362306a36Sopenharmony_ci list_for_each_entry_safe(ses, ses2, &tmp_ses_list, rlist) { 406462306a36Sopenharmony_ci list_del_init(&ses->rlist); 406562306a36Sopenharmony_ci cifs_put_smb_ses(ses); 406662306a36Sopenharmony_ci } 406762306a36Sopenharmony_ci goto done; 406862306a36Sopenharmony_ci } 406962306a36Sopenharmony_ci 407062306a36Sopenharmony_ci tcon->status = TID_GOOD; 407162306a36Sopenharmony_ci tcon->retry = false; 407262306a36Sopenharmony_ci tcon->need_reconnect = false; 407362306a36Sopenharmony_ci 407462306a36Sopenharmony_ci /* now reconnect sessions for necessary channels */ 407562306a36Sopenharmony_ci list_for_each_entry_safe(ses, ses2, &tmp_ses_list, rlist) { 407662306a36Sopenharmony_ci tcon->ses = ses; 407762306a36Sopenharmony_ci rc = smb2_reconnect(SMB2_INTERNAL_CMD, tcon, server, true); 407862306a36Sopenharmony_ci if (rc) 407962306a36Sopenharmony_ci resched = true; 408062306a36Sopenharmony_ci list_del_init(&ses->rlist); 408162306a36Sopenharmony_ci cifs_put_smb_ses(ses); 408262306a36Sopenharmony_ci } 408362306a36Sopenharmony_ci tconInfoFree(tcon); 408462306a36Sopenharmony_ci 408562306a36Sopenharmony_cidone: 408662306a36Sopenharmony_ci cifs_dbg(FYI, "Reconnecting tcons and channels finished\n"); 408762306a36Sopenharmony_ci if (resched) 408862306a36Sopenharmony_ci queue_delayed_work(cifsiod_wq, &server->reconnect, 2 * HZ); 408962306a36Sopenharmony_ci mutex_unlock(&pserver->reconnect_mutex); 409062306a36Sopenharmony_ci 409162306a36Sopenharmony_ci /* now we can safely release srv struct */ 409262306a36Sopenharmony_ci cifs_put_tcp_session(server, true); 409362306a36Sopenharmony_ci} 409462306a36Sopenharmony_ci 409562306a36Sopenharmony_ciint 409662306a36Sopenharmony_ciSMB2_echo(struct TCP_Server_Info *server) 409762306a36Sopenharmony_ci{ 409862306a36Sopenharmony_ci struct smb2_echo_req *req; 409962306a36Sopenharmony_ci int rc = 0; 410062306a36Sopenharmony_ci struct kvec iov[1]; 410162306a36Sopenharmony_ci struct smb_rqst rqst = { .rq_iov = iov, 410262306a36Sopenharmony_ci .rq_nvec = 1 }; 410362306a36Sopenharmony_ci unsigned int total_len; 410462306a36Sopenharmony_ci 410562306a36Sopenharmony_ci cifs_dbg(FYI, "In echo request for conn_id %lld\n", server->conn_id); 410662306a36Sopenharmony_ci 410762306a36Sopenharmony_ci spin_lock(&server->srv_lock); 410862306a36Sopenharmony_ci if (server->ops->need_neg && 410962306a36Sopenharmony_ci server->ops->need_neg(server)) { 411062306a36Sopenharmony_ci spin_unlock(&server->srv_lock); 411162306a36Sopenharmony_ci /* No need to send echo on newly established connections */ 411262306a36Sopenharmony_ci mod_delayed_work(cifsiod_wq, &server->reconnect, 0); 411362306a36Sopenharmony_ci return rc; 411462306a36Sopenharmony_ci } 411562306a36Sopenharmony_ci spin_unlock(&server->srv_lock); 411662306a36Sopenharmony_ci 411762306a36Sopenharmony_ci rc = smb2_plain_req_init(SMB2_ECHO, NULL, server, 411862306a36Sopenharmony_ci (void **)&req, &total_len); 411962306a36Sopenharmony_ci if (rc) 412062306a36Sopenharmony_ci return rc; 412162306a36Sopenharmony_ci 412262306a36Sopenharmony_ci req->hdr.CreditRequest = cpu_to_le16(1); 412362306a36Sopenharmony_ci 412462306a36Sopenharmony_ci iov[0].iov_len = total_len; 412562306a36Sopenharmony_ci iov[0].iov_base = (char *)req; 412662306a36Sopenharmony_ci 412762306a36Sopenharmony_ci rc = cifs_call_async(server, &rqst, NULL, smb2_echo_callback, NULL, 412862306a36Sopenharmony_ci server, CIFS_ECHO_OP, NULL); 412962306a36Sopenharmony_ci if (rc) 413062306a36Sopenharmony_ci cifs_dbg(FYI, "Echo request failed: %d\n", rc); 413162306a36Sopenharmony_ci 413262306a36Sopenharmony_ci cifs_small_buf_release(req); 413362306a36Sopenharmony_ci return rc; 413462306a36Sopenharmony_ci} 413562306a36Sopenharmony_ci 413662306a36Sopenharmony_civoid 413762306a36Sopenharmony_ciSMB2_flush_free(struct smb_rqst *rqst) 413862306a36Sopenharmony_ci{ 413962306a36Sopenharmony_ci if (rqst && rqst->rq_iov) 414062306a36Sopenharmony_ci cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ 414162306a36Sopenharmony_ci} 414262306a36Sopenharmony_ci 414362306a36Sopenharmony_ciint 414462306a36Sopenharmony_ciSMB2_flush_init(const unsigned int xid, struct smb_rqst *rqst, 414562306a36Sopenharmony_ci struct cifs_tcon *tcon, struct TCP_Server_Info *server, 414662306a36Sopenharmony_ci u64 persistent_fid, u64 volatile_fid) 414762306a36Sopenharmony_ci{ 414862306a36Sopenharmony_ci struct smb2_flush_req *req; 414962306a36Sopenharmony_ci struct kvec *iov = rqst->rq_iov; 415062306a36Sopenharmony_ci unsigned int total_len; 415162306a36Sopenharmony_ci int rc; 415262306a36Sopenharmony_ci 415362306a36Sopenharmony_ci rc = smb2_plain_req_init(SMB2_FLUSH, tcon, server, 415462306a36Sopenharmony_ci (void **) &req, &total_len); 415562306a36Sopenharmony_ci if (rc) 415662306a36Sopenharmony_ci return rc; 415762306a36Sopenharmony_ci 415862306a36Sopenharmony_ci req->PersistentFileId = persistent_fid; 415962306a36Sopenharmony_ci req->VolatileFileId = volatile_fid; 416062306a36Sopenharmony_ci 416162306a36Sopenharmony_ci iov[0].iov_base = (char *)req; 416262306a36Sopenharmony_ci iov[0].iov_len = total_len; 416362306a36Sopenharmony_ci 416462306a36Sopenharmony_ci return 0; 416562306a36Sopenharmony_ci} 416662306a36Sopenharmony_ci 416762306a36Sopenharmony_ciint 416862306a36Sopenharmony_ciSMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, 416962306a36Sopenharmony_ci u64 volatile_fid) 417062306a36Sopenharmony_ci{ 417162306a36Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 417262306a36Sopenharmony_ci struct smb_rqst rqst; 417362306a36Sopenharmony_ci struct kvec iov[1]; 417462306a36Sopenharmony_ci struct kvec rsp_iov = {NULL, 0}; 417562306a36Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(ses); 417662306a36Sopenharmony_ci int resp_buftype = CIFS_NO_BUFFER; 417762306a36Sopenharmony_ci int flags = 0; 417862306a36Sopenharmony_ci int rc = 0; 417962306a36Sopenharmony_ci 418062306a36Sopenharmony_ci cifs_dbg(FYI, "flush\n"); 418162306a36Sopenharmony_ci if (!ses || !(ses->server)) 418262306a36Sopenharmony_ci return -EIO; 418362306a36Sopenharmony_ci 418462306a36Sopenharmony_ci if (smb3_encryption_required(tcon)) 418562306a36Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 418662306a36Sopenharmony_ci 418762306a36Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 418862306a36Sopenharmony_ci memset(&iov, 0, sizeof(iov)); 418962306a36Sopenharmony_ci rqst.rq_iov = iov; 419062306a36Sopenharmony_ci rqst.rq_nvec = 1; 419162306a36Sopenharmony_ci 419262306a36Sopenharmony_ci rc = SMB2_flush_init(xid, &rqst, tcon, server, 419362306a36Sopenharmony_ci persistent_fid, volatile_fid); 419462306a36Sopenharmony_ci if (rc) 419562306a36Sopenharmony_ci goto flush_exit; 419662306a36Sopenharmony_ci 419762306a36Sopenharmony_ci trace_smb3_flush_enter(xid, persistent_fid, tcon->tid, ses->Suid); 419862306a36Sopenharmony_ci rc = cifs_send_recv(xid, ses, server, 419962306a36Sopenharmony_ci &rqst, &resp_buftype, flags, &rsp_iov); 420062306a36Sopenharmony_ci 420162306a36Sopenharmony_ci if (rc != 0) { 420262306a36Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_FLUSH_HE); 420362306a36Sopenharmony_ci trace_smb3_flush_err(xid, persistent_fid, tcon->tid, ses->Suid, 420462306a36Sopenharmony_ci rc); 420562306a36Sopenharmony_ci } else 420662306a36Sopenharmony_ci trace_smb3_flush_done(xid, persistent_fid, tcon->tid, 420762306a36Sopenharmony_ci ses->Suid); 420862306a36Sopenharmony_ci 420962306a36Sopenharmony_ci flush_exit: 421062306a36Sopenharmony_ci SMB2_flush_free(&rqst); 421162306a36Sopenharmony_ci free_rsp_buf(resp_buftype, rsp_iov.iov_base); 421262306a36Sopenharmony_ci return rc; 421362306a36Sopenharmony_ci} 421462306a36Sopenharmony_ci 421562306a36Sopenharmony_ci#ifdef CONFIG_CIFS_SMB_DIRECT 421662306a36Sopenharmony_cistatic inline bool smb3_use_rdma_offload(struct cifs_io_parms *io_parms) 421762306a36Sopenharmony_ci{ 421862306a36Sopenharmony_ci struct TCP_Server_Info *server = io_parms->server; 421962306a36Sopenharmony_ci struct cifs_tcon *tcon = io_parms->tcon; 422062306a36Sopenharmony_ci 422162306a36Sopenharmony_ci /* we can only offload if we're connected */ 422262306a36Sopenharmony_ci if (!server || !tcon) 422362306a36Sopenharmony_ci return false; 422462306a36Sopenharmony_ci 422562306a36Sopenharmony_ci /* we can only offload on an rdma connection */ 422662306a36Sopenharmony_ci if (!server->rdma || !server->smbd_conn) 422762306a36Sopenharmony_ci return false; 422862306a36Sopenharmony_ci 422962306a36Sopenharmony_ci /* we don't support signed offload yet */ 423062306a36Sopenharmony_ci if (server->sign) 423162306a36Sopenharmony_ci return false; 423262306a36Sopenharmony_ci 423362306a36Sopenharmony_ci /* we don't support encrypted offload yet */ 423462306a36Sopenharmony_ci if (smb3_encryption_required(tcon)) 423562306a36Sopenharmony_ci return false; 423662306a36Sopenharmony_ci 423762306a36Sopenharmony_ci /* offload also has its overhead, so only do it if desired */ 423862306a36Sopenharmony_ci if (io_parms->length < server->smbd_conn->rdma_readwrite_threshold) 423962306a36Sopenharmony_ci return false; 424062306a36Sopenharmony_ci 424162306a36Sopenharmony_ci return true; 424262306a36Sopenharmony_ci} 424362306a36Sopenharmony_ci#endif /* CONFIG_CIFS_SMB_DIRECT */ 424462306a36Sopenharmony_ci 424562306a36Sopenharmony_ci/* 424662306a36Sopenharmony_ci * To form a chain of read requests, any read requests after the first should 424762306a36Sopenharmony_ci * have the end_of_chain boolean set to true. 424862306a36Sopenharmony_ci */ 424962306a36Sopenharmony_cistatic int 425062306a36Sopenharmony_cismb2_new_read_req(void **buf, unsigned int *total_len, 425162306a36Sopenharmony_ci struct cifs_io_parms *io_parms, struct cifs_readdata *rdata, 425262306a36Sopenharmony_ci unsigned int remaining_bytes, int request_type) 425362306a36Sopenharmony_ci{ 425462306a36Sopenharmony_ci int rc = -EACCES; 425562306a36Sopenharmony_ci struct smb2_read_req *req = NULL; 425662306a36Sopenharmony_ci struct smb2_hdr *shdr; 425762306a36Sopenharmony_ci struct TCP_Server_Info *server = io_parms->server; 425862306a36Sopenharmony_ci 425962306a36Sopenharmony_ci rc = smb2_plain_req_init(SMB2_READ, io_parms->tcon, server, 426062306a36Sopenharmony_ci (void **) &req, total_len); 426162306a36Sopenharmony_ci if (rc) 426262306a36Sopenharmony_ci return rc; 426362306a36Sopenharmony_ci 426462306a36Sopenharmony_ci if (server == NULL) 426562306a36Sopenharmony_ci return -ECONNABORTED; 426662306a36Sopenharmony_ci 426762306a36Sopenharmony_ci shdr = &req->hdr; 426862306a36Sopenharmony_ci shdr->Id.SyncId.ProcessId = cpu_to_le32(io_parms->pid); 426962306a36Sopenharmony_ci 427062306a36Sopenharmony_ci req->PersistentFileId = io_parms->persistent_fid; 427162306a36Sopenharmony_ci req->VolatileFileId = io_parms->volatile_fid; 427262306a36Sopenharmony_ci req->ReadChannelInfoOffset = 0; /* reserved */ 427362306a36Sopenharmony_ci req->ReadChannelInfoLength = 0; /* reserved */ 427462306a36Sopenharmony_ci req->Channel = 0; /* reserved */ 427562306a36Sopenharmony_ci req->MinimumCount = 0; 427662306a36Sopenharmony_ci req->Length = cpu_to_le32(io_parms->length); 427762306a36Sopenharmony_ci req->Offset = cpu_to_le64(io_parms->offset); 427862306a36Sopenharmony_ci 427962306a36Sopenharmony_ci trace_smb3_read_enter(0 /* xid */, 428062306a36Sopenharmony_ci io_parms->persistent_fid, 428162306a36Sopenharmony_ci io_parms->tcon->tid, io_parms->tcon->ses->Suid, 428262306a36Sopenharmony_ci io_parms->offset, io_parms->length); 428362306a36Sopenharmony_ci#ifdef CONFIG_CIFS_SMB_DIRECT 428462306a36Sopenharmony_ci /* 428562306a36Sopenharmony_ci * If we want to do a RDMA write, fill in and append 428662306a36Sopenharmony_ci * smbd_buffer_descriptor_v1 to the end of read request 428762306a36Sopenharmony_ci */ 428862306a36Sopenharmony_ci if (smb3_use_rdma_offload(io_parms)) { 428962306a36Sopenharmony_ci struct smbd_buffer_descriptor_v1 *v1; 429062306a36Sopenharmony_ci bool need_invalidate = server->dialect == SMB30_PROT_ID; 429162306a36Sopenharmony_ci 429262306a36Sopenharmony_ci rdata->mr = smbd_register_mr(server->smbd_conn, &rdata->iter, 429362306a36Sopenharmony_ci true, need_invalidate); 429462306a36Sopenharmony_ci if (!rdata->mr) 429562306a36Sopenharmony_ci return -EAGAIN; 429662306a36Sopenharmony_ci 429762306a36Sopenharmony_ci req->Channel = SMB2_CHANNEL_RDMA_V1_INVALIDATE; 429862306a36Sopenharmony_ci if (need_invalidate) 429962306a36Sopenharmony_ci req->Channel = SMB2_CHANNEL_RDMA_V1; 430062306a36Sopenharmony_ci req->ReadChannelInfoOffset = 430162306a36Sopenharmony_ci cpu_to_le16(offsetof(struct smb2_read_req, Buffer)); 430262306a36Sopenharmony_ci req->ReadChannelInfoLength = 430362306a36Sopenharmony_ci cpu_to_le16(sizeof(struct smbd_buffer_descriptor_v1)); 430462306a36Sopenharmony_ci v1 = (struct smbd_buffer_descriptor_v1 *) &req->Buffer[0]; 430562306a36Sopenharmony_ci v1->offset = cpu_to_le64(rdata->mr->mr->iova); 430662306a36Sopenharmony_ci v1->token = cpu_to_le32(rdata->mr->mr->rkey); 430762306a36Sopenharmony_ci v1->length = cpu_to_le32(rdata->mr->mr->length); 430862306a36Sopenharmony_ci 430962306a36Sopenharmony_ci *total_len += sizeof(*v1) - 1; 431062306a36Sopenharmony_ci } 431162306a36Sopenharmony_ci#endif 431262306a36Sopenharmony_ci if (request_type & CHAINED_REQUEST) { 431362306a36Sopenharmony_ci if (!(request_type & END_OF_CHAIN)) { 431462306a36Sopenharmony_ci /* next 8-byte aligned request */ 431562306a36Sopenharmony_ci *total_len = ALIGN(*total_len, 8); 431662306a36Sopenharmony_ci shdr->NextCommand = cpu_to_le32(*total_len); 431762306a36Sopenharmony_ci } else /* END_OF_CHAIN */ 431862306a36Sopenharmony_ci shdr->NextCommand = 0; 431962306a36Sopenharmony_ci if (request_type & RELATED_REQUEST) { 432062306a36Sopenharmony_ci shdr->Flags |= SMB2_FLAGS_RELATED_OPERATIONS; 432162306a36Sopenharmony_ci /* 432262306a36Sopenharmony_ci * Related requests use info from previous read request 432362306a36Sopenharmony_ci * in chain. 432462306a36Sopenharmony_ci */ 432562306a36Sopenharmony_ci shdr->SessionId = cpu_to_le64(0xFFFFFFFFFFFFFFFF); 432662306a36Sopenharmony_ci shdr->Id.SyncId.TreeId = cpu_to_le32(0xFFFFFFFF); 432762306a36Sopenharmony_ci req->PersistentFileId = (u64)-1; 432862306a36Sopenharmony_ci req->VolatileFileId = (u64)-1; 432962306a36Sopenharmony_ci } 433062306a36Sopenharmony_ci } 433162306a36Sopenharmony_ci if (remaining_bytes > io_parms->length) 433262306a36Sopenharmony_ci req->RemainingBytes = cpu_to_le32(remaining_bytes); 433362306a36Sopenharmony_ci else 433462306a36Sopenharmony_ci req->RemainingBytes = 0; 433562306a36Sopenharmony_ci 433662306a36Sopenharmony_ci *buf = req; 433762306a36Sopenharmony_ci return rc; 433862306a36Sopenharmony_ci} 433962306a36Sopenharmony_ci 434062306a36Sopenharmony_cistatic void 434162306a36Sopenharmony_cismb2_readv_callback(struct mid_q_entry *mid) 434262306a36Sopenharmony_ci{ 434362306a36Sopenharmony_ci struct cifs_readdata *rdata = mid->callback_data; 434462306a36Sopenharmony_ci struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink); 434562306a36Sopenharmony_ci struct TCP_Server_Info *server = rdata->server; 434662306a36Sopenharmony_ci struct smb2_hdr *shdr = 434762306a36Sopenharmony_ci (struct smb2_hdr *)rdata->iov[0].iov_base; 434862306a36Sopenharmony_ci struct cifs_credits credits = { .value = 0, .instance = 0 }; 434962306a36Sopenharmony_ci struct smb_rqst rqst = { .rq_iov = &rdata->iov[1], .rq_nvec = 1 }; 435062306a36Sopenharmony_ci 435162306a36Sopenharmony_ci if (rdata->got_bytes) { 435262306a36Sopenharmony_ci rqst.rq_iter = rdata->iter; 435362306a36Sopenharmony_ci rqst.rq_iter_size = iov_iter_count(&rdata->iter); 435462306a36Sopenharmony_ci } 435562306a36Sopenharmony_ci 435662306a36Sopenharmony_ci WARN_ONCE(rdata->server != mid->server, 435762306a36Sopenharmony_ci "rdata server %p != mid server %p", 435862306a36Sopenharmony_ci rdata->server, mid->server); 435962306a36Sopenharmony_ci 436062306a36Sopenharmony_ci cifs_dbg(FYI, "%s: mid=%llu state=%d result=%d bytes=%u\n", 436162306a36Sopenharmony_ci __func__, mid->mid, mid->mid_state, rdata->result, 436262306a36Sopenharmony_ci rdata->bytes); 436362306a36Sopenharmony_ci 436462306a36Sopenharmony_ci switch (mid->mid_state) { 436562306a36Sopenharmony_ci case MID_RESPONSE_RECEIVED: 436662306a36Sopenharmony_ci credits.value = le16_to_cpu(shdr->CreditRequest); 436762306a36Sopenharmony_ci credits.instance = server->reconnect_instance; 436862306a36Sopenharmony_ci /* result already set, check signature */ 436962306a36Sopenharmony_ci if (server->sign && !mid->decrypted) { 437062306a36Sopenharmony_ci int rc; 437162306a36Sopenharmony_ci 437262306a36Sopenharmony_ci iov_iter_revert(&rqst.rq_iter, rdata->got_bytes); 437362306a36Sopenharmony_ci iov_iter_truncate(&rqst.rq_iter, rdata->got_bytes); 437462306a36Sopenharmony_ci rc = smb2_verify_signature(&rqst, server); 437562306a36Sopenharmony_ci if (rc) 437662306a36Sopenharmony_ci cifs_tcon_dbg(VFS, "SMB signature verification returned error = %d\n", 437762306a36Sopenharmony_ci rc); 437862306a36Sopenharmony_ci } 437962306a36Sopenharmony_ci /* FIXME: should this be counted toward the initiating task? */ 438062306a36Sopenharmony_ci task_io_account_read(rdata->got_bytes); 438162306a36Sopenharmony_ci cifs_stats_bytes_read(tcon, rdata->got_bytes); 438262306a36Sopenharmony_ci break; 438362306a36Sopenharmony_ci case MID_REQUEST_SUBMITTED: 438462306a36Sopenharmony_ci case MID_RETRY_NEEDED: 438562306a36Sopenharmony_ci rdata->result = -EAGAIN; 438662306a36Sopenharmony_ci if (server->sign && rdata->got_bytes) 438762306a36Sopenharmony_ci /* reset bytes number since we can not check a sign */ 438862306a36Sopenharmony_ci rdata->got_bytes = 0; 438962306a36Sopenharmony_ci /* FIXME: should this be counted toward the initiating task? */ 439062306a36Sopenharmony_ci task_io_account_read(rdata->got_bytes); 439162306a36Sopenharmony_ci cifs_stats_bytes_read(tcon, rdata->got_bytes); 439262306a36Sopenharmony_ci break; 439362306a36Sopenharmony_ci case MID_RESPONSE_MALFORMED: 439462306a36Sopenharmony_ci credits.value = le16_to_cpu(shdr->CreditRequest); 439562306a36Sopenharmony_ci credits.instance = server->reconnect_instance; 439662306a36Sopenharmony_ci fallthrough; 439762306a36Sopenharmony_ci default: 439862306a36Sopenharmony_ci rdata->result = -EIO; 439962306a36Sopenharmony_ci } 440062306a36Sopenharmony_ci#ifdef CONFIG_CIFS_SMB_DIRECT 440162306a36Sopenharmony_ci /* 440262306a36Sopenharmony_ci * If this rdata has a memmory registered, the MR can be freed 440362306a36Sopenharmony_ci * MR needs to be freed as soon as I/O finishes to prevent deadlock 440462306a36Sopenharmony_ci * because they have limited number and are used for future I/Os 440562306a36Sopenharmony_ci */ 440662306a36Sopenharmony_ci if (rdata->mr) { 440762306a36Sopenharmony_ci smbd_deregister_mr(rdata->mr); 440862306a36Sopenharmony_ci rdata->mr = NULL; 440962306a36Sopenharmony_ci } 441062306a36Sopenharmony_ci#endif 441162306a36Sopenharmony_ci if (rdata->result && rdata->result != -ENODATA) { 441262306a36Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_READ_HE); 441362306a36Sopenharmony_ci trace_smb3_read_err(0 /* xid */, 441462306a36Sopenharmony_ci rdata->cfile->fid.persistent_fid, 441562306a36Sopenharmony_ci tcon->tid, tcon->ses->Suid, rdata->offset, 441662306a36Sopenharmony_ci rdata->bytes, rdata->result); 441762306a36Sopenharmony_ci } else 441862306a36Sopenharmony_ci trace_smb3_read_done(0 /* xid */, 441962306a36Sopenharmony_ci rdata->cfile->fid.persistent_fid, 442062306a36Sopenharmony_ci tcon->tid, tcon->ses->Suid, 442162306a36Sopenharmony_ci rdata->offset, rdata->got_bytes); 442262306a36Sopenharmony_ci 442362306a36Sopenharmony_ci queue_work(cifsiod_wq, &rdata->work); 442462306a36Sopenharmony_ci release_mid(mid); 442562306a36Sopenharmony_ci add_credits(server, &credits, 0); 442662306a36Sopenharmony_ci} 442762306a36Sopenharmony_ci 442862306a36Sopenharmony_ci/* smb2_async_readv - send an async read, and set up mid to handle result */ 442962306a36Sopenharmony_ciint 443062306a36Sopenharmony_cismb2_async_readv(struct cifs_readdata *rdata) 443162306a36Sopenharmony_ci{ 443262306a36Sopenharmony_ci int rc, flags = 0; 443362306a36Sopenharmony_ci char *buf; 443462306a36Sopenharmony_ci struct smb2_hdr *shdr; 443562306a36Sopenharmony_ci struct cifs_io_parms io_parms; 443662306a36Sopenharmony_ci struct smb_rqst rqst = { .rq_iov = rdata->iov, 443762306a36Sopenharmony_ci .rq_nvec = 1 }; 443862306a36Sopenharmony_ci struct TCP_Server_Info *server; 443962306a36Sopenharmony_ci struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink); 444062306a36Sopenharmony_ci unsigned int total_len; 444162306a36Sopenharmony_ci int credit_request; 444262306a36Sopenharmony_ci 444362306a36Sopenharmony_ci cifs_dbg(FYI, "%s: offset=%llu bytes=%u\n", 444462306a36Sopenharmony_ci __func__, rdata->offset, rdata->bytes); 444562306a36Sopenharmony_ci 444662306a36Sopenharmony_ci if (!rdata->server) 444762306a36Sopenharmony_ci rdata->server = cifs_pick_channel(tcon->ses); 444862306a36Sopenharmony_ci 444962306a36Sopenharmony_ci io_parms.tcon = tlink_tcon(rdata->cfile->tlink); 445062306a36Sopenharmony_ci io_parms.server = server = rdata->server; 445162306a36Sopenharmony_ci io_parms.offset = rdata->offset; 445262306a36Sopenharmony_ci io_parms.length = rdata->bytes; 445362306a36Sopenharmony_ci io_parms.persistent_fid = rdata->cfile->fid.persistent_fid; 445462306a36Sopenharmony_ci io_parms.volatile_fid = rdata->cfile->fid.volatile_fid; 445562306a36Sopenharmony_ci io_parms.pid = rdata->pid; 445662306a36Sopenharmony_ci 445762306a36Sopenharmony_ci rc = smb2_new_read_req( 445862306a36Sopenharmony_ci (void **) &buf, &total_len, &io_parms, rdata, 0, 0); 445962306a36Sopenharmony_ci if (rc) 446062306a36Sopenharmony_ci return rc; 446162306a36Sopenharmony_ci 446262306a36Sopenharmony_ci if (smb3_encryption_required(io_parms.tcon)) 446362306a36Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 446462306a36Sopenharmony_ci 446562306a36Sopenharmony_ci rdata->iov[0].iov_base = buf; 446662306a36Sopenharmony_ci rdata->iov[0].iov_len = total_len; 446762306a36Sopenharmony_ci 446862306a36Sopenharmony_ci shdr = (struct smb2_hdr *)buf; 446962306a36Sopenharmony_ci 447062306a36Sopenharmony_ci if (rdata->credits.value > 0) { 447162306a36Sopenharmony_ci shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->bytes, 447262306a36Sopenharmony_ci SMB2_MAX_BUFFER_SIZE)); 447362306a36Sopenharmony_ci credit_request = le16_to_cpu(shdr->CreditCharge) + 8; 447462306a36Sopenharmony_ci if (server->credits >= server->max_credits) 447562306a36Sopenharmony_ci shdr->CreditRequest = cpu_to_le16(0); 447662306a36Sopenharmony_ci else 447762306a36Sopenharmony_ci shdr->CreditRequest = cpu_to_le16( 447862306a36Sopenharmony_ci min_t(int, server->max_credits - 447962306a36Sopenharmony_ci server->credits, credit_request)); 448062306a36Sopenharmony_ci 448162306a36Sopenharmony_ci rc = adjust_credits(server, &rdata->credits, rdata->bytes); 448262306a36Sopenharmony_ci if (rc) 448362306a36Sopenharmony_ci goto async_readv_out; 448462306a36Sopenharmony_ci 448562306a36Sopenharmony_ci flags |= CIFS_HAS_CREDITS; 448662306a36Sopenharmony_ci } 448762306a36Sopenharmony_ci 448862306a36Sopenharmony_ci kref_get(&rdata->refcount); 448962306a36Sopenharmony_ci rc = cifs_call_async(server, &rqst, 449062306a36Sopenharmony_ci cifs_readv_receive, smb2_readv_callback, 449162306a36Sopenharmony_ci smb3_handle_read_data, rdata, flags, 449262306a36Sopenharmony_ci &rdata->credits); 449362306a36Sopenharmony_ci if (rc) { 449462306a36Sopenharmony_ci kref_put(&rdata->refcount, cifs_readdata_release); 449562306a36Sopenharmony_ci cifs_stats_fail_inc(io_parms.tcon, SMB2_READ_HE); 449662306a36Sopenharmony_ci trace_smb3_read_err(0 /* xid */, io_parms.persistent_fid, 449762306a36Sopenharmony_ci io_parms.tcon->tid, 449862306a36Sopenharmony_ci io_parms.tcon->ses->Suid, 449962306a36Sopenharmony_ci io_parms.offset, io_parms.length, rc); 450062306a36Sopenharmony_ci } 450162306a36Sopenharmony_ci 450262306a36Sopenharmony_ciasync_readv_out: 450362306a36Sopenharmony_ci cifs_small_buf_release(buf); 450462306a36Sopenharmony_ci return rc; 450562306a36Sopenharmony_ci} 450662306a36Sopenharmony_ci 450762306a36Sopenharmony_ciint 450862306a36Sopenharmony_ciSMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms, 450962306a36Sopenharmony_ci unsigned int *nbytes, char **buf, int *buf_type) 451062306a36Sopenharmony_ci{ 451162306a36Sopenharmony_ci struct smb_rqst rqst; 451262306a36Sopenharmony_ci int resp_buftype, rc; 451362306a36Sopenharmony_ci struct smb2_read_req *req = NULL; 451462306a36Sopenharmony_ci struct smb2_read_rsp *rsp = NULL; 451562306a36Sopenharmony_ci struct kvec iov[1]; 451662306a36Sopenharmony_ci struct kvec rsp_iov; 451762306a36Sopenharmony_ci unsigned int total_len; 451862306a36Sopenharmony_ci int flags = CIFS_LOG_ERROR; 451962306a36Sopenharmony_ci struct cifs_ses *ses = io_parms->tcon->ses; 452062306a36Sopenharmony_ci 452162306a36Sopenharmony_ci if (!io_parms->server) 452262306a36Sopenharmony_ci io_parms->server = cifs_pick_channel(io_parms->tcon->ses); 452362306a36Sopenharmony_ci 452462306a36Sopenharmony_ci *nbytes = 0; 452562306a36Sopenharmony_ci rc = smb2_new_read_req((void **)&req, &total_len, io_parms, NULL, 0, 0); 452662306a36Sopenharmony_ci if (rc) 452762306a36Sopenharmony_ci return rc; 452862306a36Sopenharmony_ci 452962306a36Sopenharmony_ci if (smb3_encryption_required(io_parms->tcon)) 453062306a36Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 453162306a36Sopenharmony_ci 453262306a36Sopenharmony_ci iov[0].iov_base = (char *)req; 453362306a36Sopenharmony_ci iov[0].iov_len = total_len; 453462306a36Sopenharmony_ci 453562306a36Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 453662306a36Sopenharmony_ci rqst.rq_iov = iov; 453762306a36Sopenharmony_ci rqst.rq_nvec = 1; 453862306a36Sopenharmony_ci 453962306a36Sopenharmony_ci rc = cifs_send_recv(xid, ses, io_parms->server, 454062306a36Sopenharmony_ci &rqst, &resp_buftype, flags, &rsp_iov); 454162306a36Sopenharmony_ci rsp = (struct smb2_read_rsp *)rsp_iov.iov_base; 454262306a36Sopenharmony_ci 454362306a36Sopenharmony_ci if (rc) { 454462306a36Sopenharmony_ci if (rc != -ENODATA) { 454562306a36Sopenharmony_ci cifs_stats_fail_inc(io_parms->tcon, SMB2_READ_HE); 454662306a36Sopenharmony_ci cifs_dbg(VFS, "Send error in read = %d\n", rc); 454762306a36Sopenharmony_ci trace_smb3_read_err(xid, 454862306a36Sopenharmony_ci req->PersistentFileId, 454962306a36Sopenharmony_ci io_parms->tcon->tid, ses->Suid, 455062306a36Sopenharmony_ci io_parms->offset, io_parms->length, 455162306a36Sopenharmony_ci rc); 455262306a36Sopenharmony_ci } else 455362306a36Sopenharmony_ci trace_smb3_read_done(xid, req->PersistentFileId, io_parms->tcon->tid, 455462306a36Sopenharmony_ci ses->Suid, io_parms->offset, 0); 455562306a36Sopenharmony_ci free_rsp_buf(resp_buftype, rsp_iov.iov_base); 455662306a36Sopenharmony_ci cifs_small_buf_release(req); 455762306a36Sopenharmony_ci return rc == -ENODATA ? 0 : rc; 455862306a36Sopenharmony_ci } else 455962306a36Sopenharmony_ci trace_smb3_read_done(xid, 456062306a36Sopenharmony_ci req->PersistentFileId, 456162306a36Sopenharmony_ci io_parms->tcon->tid, ses->Suid, 456262306a36Sopenharmony_ci io_parms->offset, io_parms->length); 456362306a36Sopenharmony_ci 456462306a36Sopenharmony_ci cifs_small_buf_release(req); 456562306a36Sopenharmony_ci 456662306a36Sopenharmony_ci *nbytes = le32_to_cpu(rsp->DataLength); 456762306a36Sopenharmony_ci if ((*nbytes > CIFS_MAX_MSGSIZE) || 456862306a36Sopenharmony_ci (*nbytes > io_parms->length)) { 456962306a36Sopenharmony_ci cifs_dbg(FYI, "bad length %d for count %d\n", 457062306a36Sopenharmony_ci *nbytes, io_parms->length); 457162306a36Sopenharmony_ci rc = -EIO; 457262306a36Sopenharmony_ci *nbytes = 0; 457362306a36Sopenharmony_ci } 457462306a36Sopenharmony_ci 457562306a36Sopenharmony_ci if (*buf) { 457662306a36Sopenharmony_ci memcpy(*buf, (char *)rsp + rsp->DataOffset, *nbytes); 457762306a36Sopenharmony_ci free_rsp_buf(resp_buftype, rsp_iov.iov_base); 457862306a36Sopenharmony_ci } else if (resp_buftype != CIFS_NO_BUFFER) { 457962306a36Sopenharmony_ci *buf = rsp_iov.iov_base; 458062306a36Sopenharmony_ci if (resp_buftype == CIFS_SMALL_BUFFER) 458162306a36Sopenharmony_ci *buf_type = CIFS_SMALL_BUFFER; 458262306a36Sopenharmony_ci else if (resp_buftype == CIFS_LARGE_BUFFER) 458362306a36Sopenharmony_ci *buf_type = CIFS_LARGE_BUFFER; 458462306a36Sopenharmony_ci } 458562306a36Sopenharmony_ci return rc; 458662306a36Sopenharmony_ci} 458762306a36Sopenharmony_ci 458862306a36Sopenharmony_ci/* 458962306a36Sopenharmony_ci * Check the mid_state and signature on received buffer (if any), and queue the 459062306a36Sopenharmony_ci * workqueue completion task. 459162306a36Sopenharmony_ci */ 459262306a36Sopenharmony_cistatic void 459362306a36Sopenharmony_cismb2_writev_callback(struct mid_q_entry *mid) 459462306a36Sopenharmony_ci{ 459562306a36Sopenharmony_ci struct cifs_writedata *wdata = mid->callback_data; 459662306a36Sopenharmony_ci struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); 459762306a36Sopenharmony_ci struct TCP_Server_Info *server = wdata->server; 459862306a36Sopenharmony_ci unsigned int written; 459962306a36Sopenharmony_ci struct smb2_write_rsp *rsp = (struct smb2_write_rsp *)mid->resp_buf; 460062306a36Sopenharmony_ci struct cifs_credits credits = { .value = 0, .instance = 0 }; 460162306a36Sopenharmony_ci 460262306a36Sopenharmony_ci WARN_ONCE(wdata->server != mid->server, 460362306a36Sopenharmony_ci "wdata server %p != mid server %p", 460462306a36Sopenharmony_ci wdata->server, mid->server); 460562306a36Sopenharmony_ci 460662306a36Sopenharmony_ci switch (mid->mid_state) { 460762306a36Sopenharmony_ci case MID_RESPONSE_RECEIVED: 460862306a36Sopenharmony_ci credits.value = le16_to_cpu(rsp->hdr.CreditRequest); 460962306a36Sopenharmony_ci credits.instance = server->reconnect_instance; 461062306a36Sopenharmony_ci wdata->result = smb2_check_receive(mid, server, 0); 461162306a36Sopenharmony_ci if (wdata->result != 0) 461262306a36Sopenharmony_ci break; 461362306a36Sopenharmony_ci 461462306a36Sopenharmony_ci written = le32_to_cpu(rsp->DataLength); 461562306a36Sopenharmony_ci /* 461662306a36Sopenharmony_ci * Mask off high 16 bits when bytes written as returned 461762306a36Sopenharmony_ci * by the server is greater than bytes requested by the 461862306a36Sopenharmony_ci * client. OS/2 servers are known to set incorrect 461962306a36Sopenharmony_ci * CountHigh values. 462062306a36Sopenharmony_ci */ 462162306a36Sopenharmony_ci if (written > wdata->bytes) 462262306a36Sopenharmony_ci written &= 0xFFFF; 462362306a36Sopenharmony_ci 462462306a36Sopenharmony_ci if (written < wdata->bytes) 462562306a36Sopenharmony_ci wdata->result = -ENOSPC; 462662306a36Sopenharmony_ci else 462762306a36Sopenharmony_ci wdata->bytes = written; 462862306a36Sopenharmony_ci break; 462962306a36Sopenharmony_ci case MID_REQUEST_SUBMITTED: 463062306a36Sopenharmony_ci case MID_RETRY_NEEDED: 463162306a36Sopenharmony_ci wdata->result = -EAGAIN; 463262306a36Sopenharmony_ci break; 463362306a36Sopenharmony_ci case MID_RESPONSE_MALFORMED: 463462306a36Sopenharmony_ci credits.value = le16_to_cpu(rsp->hdr.CreditRequest); 463562306a36Sopenharmony_ci credits.instance = server->reconnect_instance; 463662306a36Sopenharmony_ci fallthrough; 463762306a36Sopenharmony_ci default: 463862306a36Sopenharmony_ci wdata->result = -EIO; 463962306a36Sopenharmony_ci break; 464062306a36Sopenharmony_ci } 464162306a36Sopenharmony_ci#ifdef CONFIG_CIFS_SMB_DIRECT 464262306a36Sopenharmony_ci /* 464362306a36Sopenharmony_ci * If this wdata has a memory registered, the MR can be freed 464462306a36Sopenharmony_ci * The number of MRs available is limited, it's important to recover 464562306a36Sopenharmony_ci * used MR as soon as I/O is finished. Hold MR longer in the later 464662306a36Sopenharmony_ci * I/O process can possibly result in I/O deadlock due to lack of MR 464762306a36Sopenharmony_ci * to send request on I/O retry 464862306a36Sopenharmony_ci */ 464962306a36Sopenharmony_ci if (wdata->mr) { 465062306a36Sopenharmony_ci smbd_deregister_mr(wdata->mr); 465162306a36Sopenharmony_ci wdata->mr = NULL; 465262306a36Sopenharmony_ci } 465362306a36Sopenharmony_ci#endif 465462306a36Sopenharmony_ci if (wdata->result) { 465562306a36Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_WRITE_HE); 465662306a36Sopenharmony_ci trace_smb3_write_err(0 /* no xid */, 465762306a36Sopenharmony_ci wdata->cfile->fid.persistent_fid, 465862306a36Sopenharmony_ci tcon->tid, tcon->ses->Suid, wdata->offset, 465962306a36Sopenharmony_ci wdata->bytes, wdata->result); 466062306a36Sopenharmony_ci if (wdata->result == -ENOSPC) 466162306a36Sopenharmony_ci pr_warn_once("Out of space writing to %s\n", 466262306a36Sopenharmony_ci tcon->tree_name); 466362306a36Sopenharmony_ci } else 466462306a36Sopenharmony_ci trace_smb3_write_done(0 /* no xid */, 466562306a36Sopenharmony_ci wdata->cfile->fid.persistent_fid, 466662306a36Sopenharmony_ci tcon->tid, tcon->ses->Suid, 466762306a36Sopenharmony_ci wdata->offset, wdata->bytes); 466862306a36Sopenharmony_ci 466962306a36Sopenharmony_ci queue_work(cifsiod_wq, &wdata->work); 467062306a36Sopenharmony_ci release_mid(mid); 467162306a36Sopenharmony_ci add_credits(server, &credits, 0); 467262306a36Sopenharmony_ci} 467362306a36Sopenharmony_ci 467462306a36Sopenharmony_ci/* smb2_async_writev - send an async write, and set up mid to handle result */ 467562306a36Sopenharmony_ciint 467662306a36Sopenharmony_cismb2_async_writev(struct cifs_writedata *wdata, 467762306a36Sopenharmony_ci void (*release)(struct kref *kref)) 467862306a36Sopenharmony_ci{ 467962306a36Sopenharmony_ci int rc = -EACCES, flags = 0; 468062306a36Sopenharmony_ci struct smb2_write_req *req = NULL; 468162306a36Sopenharmony_ci struct smb2_hdr *shdr; 468262306a36Sopenharmony_ci struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); 468362306a36Sopenharmony_ci struct TCP_Server_Info *server = wdata->server; 468462306a36Sopenharmony_ci struct kvec iov[1]; 468562306a36Sopenharmony_ci struct smb_rqst rqst = { }; 468662306a36Sopenharmony_ci unsigned int total_len; 468762306a36Sopenharmony_ci struct cifs_io_parms _io_parms; 468862306a36Sopenharmony_ci struct cifs_io_parms *io_parms = NULL; 468962306a36Sopenharmony_ci int credit_request; 469062306a36Sopenharmony_ci 469162306a36Sopenharmony_ci if (!wdata->server) 469262306a36Sopenharmony_ci server = wdata->server = cifs_pick_channel(tcon->ses); 469362306a36Sopenharmony_ci 469462306a36Sopenharmony_ci /* 469562306a36Sopenharmony_ci * in future we may get cifs_io_parms passed in from the caller, 469662306a36Sopenharmony_ci * but for now we construct it here... 469762306a36Sopenharmony_ci */ 469862306a36Sopenharmony_ci _io_parms = (struct cifs_io_parms) { 469962306a36Sopenharmony_ci .tcon = tcon, 470062306a36Sopenharmony_ci .server = server, 470162306a36Sopenharmony_ci .offset = wdata->offset, 470262306a36Sopenharmony_ci .length = wdata->bytes, 470362306a36Sopenharmony_ci .persistent_fid = wdata->cfile->fid.persistent_fid, 470462306a36Sopenharmony_ci .volatile_fid = wdata->cfile->fid.volatile_fid, 470562306a36Sopenharmony_ci .pid = wdata->pid, 470662306a36Sopenharmony_ci }; 470762306a36Sopenharmony_ci io_parms = &_io_parms; 470862306a36Sopenharmony_ci 470962306a36Sopenharmony_ci rc = smb2_plain_req_init(SMB2_WRITE, tcon, server, 471062306a36Sopenharmony_ci (void **) &req, &total_len); 471162306a36Sopenharmony_ci if (rc) 471262306a36Sopenharmony_ci return rc; 471362306a36Sopenharmony_ci 471462306a36Sopenharmony_ci if (smb3_encryption_required(tcon)) 471562306a36Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 471662306a36Sopenharmony_ci 471762306a36Sopenharmony_ci shdr = (struct smb2_hdr *)req; 471862306a36Sopenharmony_ci shdr->Id.SyncId.ProcessId = cpu_to_le32(io_parms->pid); 471962306a36Sopenharmony_ci 472062306a36Sopenharmony_ci req->PersistentFileId = io_parms->persistent_fid; 472162306a36Sopenharmony_ci req->VolatileFileId = io_parms->volatile_fid; 472262306a36Sopenharmony_ci req->WriteChannelInfoOffset = 0; 472362306a36Sopenharmony_ci req->WriteChannelInfoLength = 0; 472462306a36Sopenharmony_ci req->Channel = SMB2_CHANNEL_NONE; 472562306a36Sopenharmony_ci req->Offset = cpu_to_le64(io_parms->offset); 472662306a36Sopenharmony_ci req->DataOffset = cpu_to_le16( 472762306a36Sopenharmony_ci offsetof(struct smb2_write_req, Buffer)); 472862306a36Sopenharmony_ci req->RemainingBytes = 0; 472962306a36Sopenharmony_ci 473062306a36Sopenharmony_ci trace_smb3_write_enter(0 /* xid */, 473162306a36Sopenharmony_ci io_parms->persistent_fid, 473262306a36Sopenharmony_ci io_parms->tcon->tid, 473362306a36Sopenharmony_ci io_parms->tcon->ses->Suid, 473462306a36Sopenharmony_ci io_parms->offset, 473562306a36Sopenharmony_ci io_parms->length); 473662306a36Sopenharmony_ci 473762306a36Sopenharmony_ci#ifdef CONFIG_CIFS_SMB_DIRECT 473862306a36Sopenharmony_ci /* 473962306a36Sopenharmony_ci * If we want to do a server RDMA read, fill in and append 474062306a36Sopenharmony_ci * smbd_buffer_descriptor_v1 to the end of write request 474162306a36Sopenharmony_ci */ 474262306a36Sopenharmony_ci if (smb3_use_rdma_offload(io_parms)) { 474362306a36Sopenharmony_ci struct smbd_buffer_descriptor_v1 *v1; 474462306a36Sopenharmony_ci size_t data_size = iov_iter_count(&wdata->iter); 474562306a36Sopenharmony_ci bool need_invalidate = server->dialect == SMB30_PROT_ID; 474662306a36Sopenharmony_ci 474762306a36Sopenharmony_ci wdata->mr = smbd_register_mr(server->smbd_conn, &wdata->iter, 474862306a36Sopenharmony_ci false, need_invalidate); 474962306a36Sopenharmony_ci if (!wdata->mr) { 475062306a36Sopenharmony_ci rc = -EAGAIN; 475162306a36Sopenharmony_ci goto async_writev_out; 475262306a36Sopenharmony_ci } 475362306a36Sopenharmony_ci req->Length = 0; 475462306a36Sopenharmony_ci req->DataOffset = 0; 475562306a36Sopenharmony_ci req->RemainingBytes = cpu_to_le32(data_size); 475662306a36Sopenharmony_ci req->Channel = SMB2_CHANNEL_RDMA_V1_INVALIDATE; 475762306a36Sopenharmony_ci if (need_invalidate) 475862306a36Sopenharmony_ci req->Channel = SMB2_CHANNEL_RDMA_V1; 475962306a36Sopenharmony_ci req->WriteChannelInfoOffset = 476062306a36Sopenharmony_ci cpu_to_le16(offsetof(struct smb2_write_req, Buffer)); 476162306a36Sopenharmony_ci req->WriteChannelInfoLength = 476262306a36Sopenharmony_ci cpu_to_le16(sizeof(struct smbd_buffer_descriptor_v1)); 476362306a36Sopenharmony_ci v1 = (struct smbd_buffer_descriptor_v1 *) &req->Buffer[0]; 476462306a36Sopenharmony_ci v1->offset = cpu_to_le64(wdata->mr->mr->iova); 476562306a36Sopenharmony_ci v1->token = cpu_to_le32(wdata->mr->mr->rkey); 476662306a36Sopenharmony_ci v1->length = cpu_to_le32(wdata->mr->mr->length); 476762306a36Sopenharmony_ci } 476862306a36Sopenharmony_ci#endif 476962306a36Sopenharmony_ci iov[0].iov_len = total_len - 1; 477062306a36Sopenharmony_ci iov[0].iov_base = (char *)req; 477162306a36Sopenharmony_ci 477262306a36Sopenharmony_ci rqst.rq_iov = iov; 477362306a36Sopenharmony_ci rqst.rq_nvec = 1; 477462306a36Sopenharmony_ci rqst.rq_iter = wdata->iter; 477562306a36Sopenharmony_ci rqst.rq_iter_size = iov_iter_count(&rqst.rq_iter); 477662306a36Sopenharmony_ci#ifdef CONFIG_CIFS_SMB_DIRECT 477762306a36Sopenharmony_ci if (wdata->mr) 477862306a36Sopenharmony_ci iov[0].iov_len += sizeof(struct smbd_buffer_descriptor_v1); 477962306a36Sopenharmony_ci#endif 478062306a36Sopenharmony_ci cifs_dbg(FYI, "async write at %llu %u bytes iter=%zx\n", 478162306a36Sopenharmony_ci io_parms->offset, io_parms->length, iov_iter_count(&rqst.rq_iter)); 478262306a36Sopenharmony_ci 478362306a36Sopenharmony_ci#ifdef CONFIG_CIFS_SMB_DIRECT 478462306a36Sopenharmony_ci /* For RDMA read, I/O size is in RemainingBytes not in Length */ 478562306a36Sopenharmony_ci if (!wdata->mr) 478662306a36Sopenharmony_ci req->Length = cpu_to_le32(io_parms->length); 478762306a36Sopenharmony_ci#else 478862306a36Sopenharmony_ci req->Length = cpu_to_le32(io_parms->length); 478962306a36Sopenharmony_ci#endif 479062306a36Sopenharmony_ci 479162306a36Sopenharmony_ci if (wdata->credits.value > 0) { 479262306a36Sopenharmony_ci shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(wdata->bytes, 479362306a36Sopenharmony_ci SMB2_MAX_BUFFER_SIZE)); 479462306a36Sopenharmony_ci credit_request = le16_to_cpu(shdr->CreditCharge) + 8; 479562306a36Sopenharmony_ci if (server->credits >= server->max_credits) 479662306a36Sopenharmony_ci shdr->CreditRequest = cpu_to_le16(0); 479762306a36Sopenharmony_ci else 479862306a36Sopenharmony_ci shdr->CreditRequest = cpu_to_le16( 479962306a36Sopenharmony_ci min_t(int, server->max_credits - 480062306a36Sopenharmony_ci server->credits, credit_request)); 480162306a36Sopenharmony_ci 480262306a36Sopenharmony_ci rc = adjust_credits(server, &wdata->credits, io_parms->length); 480362306a36Sopenharmony_ci if (rc) 480462306a36Sopenharmony_ci goto async_writev_out; 480562306a36Sopenharmony_ci 480662306a36Sopenharmony_ci flags |= CIFS_HAS_CREDITS; 480762306a36Sopenharmony_ci } 480862306a36Sopenharmony_ci 480962306a36Sopenharmony_ci kref_get(&wdata->refcount); 481062306a36Sopenharmony_ci rc = cifs_call_async(server, &rqst, NULL, smb2_writev_callback, NULL, 481162306a36Sopenharmony_ci wdata, flags, &wdata->credits); 481262306a36Sopenharmony_ci 481362306a36Sopenharmony_ci if (rc) { 481462306a36Sopenharmony_ci trace_smb3_write_err(0 /* no xid */, 481562306a36Sopenharmony_ci io_parms->persistent_fid, 481662306a36Sopenharmony_ci io_parms->tcon->tid, 481762306a36Sopenharmony_ci io_parms->tcon->ses->Suid, 481862306a36Sopenharmony_ci io_parms->offset, 481962306a36Sopenharmony_ci io_parms->length, 482062306a36Sopenharmony_ci rc); 482162306a36Sopenharmony_ci kref_put(&wdata->refcount, release); 482262306a36Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_WRITE_HE); 482362306a36Sopenharmony_ci } 482462306a36Sopenharmony_ci 482562306a36Sopenharmony_ciasync_writev_out: 482662306a36Sopenharmony_ci cifs_small_buf_release(req); 482762306a36Sopenharmony_ci return rc; 482862306a36Sopenharmony_ci} 482962306a36Sopenharmony_ci 483062306a36Sopenharmony_ci/* 483162306a36Sopenharmony_ci * SMB2_write function gets iov pointer to kvec array with n_vec as a length. 483262306a36Sopenharmony_ci * The length field from io_parms must be at least 1 and indicates a number of 483362306a36Sopenharmony_ci * elements with data to write that begins with position 1 in iov array. All 483462306a36Sopenharmony_ci * data length is specified by count. 483562306a36Sopenharmony_ci */ 483662306a36Sopenharmony_ciint 483762306a36Sopenharmony_ciSMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms, 483862306a36Sopenharmony_ci unsigned int *nbytes, struct kvec *iov, int n_vec) 483962306a36Sopenharmony_ci{ 484062306a36Sopenharmony_ci struct smb_rqst rqst; 484162306a36Sopenharmony_ci int rc = 0; 484262306a36Sopenharmony_ci struct smb2_write_req *req = NULL; 484362306a36Sopenharmony_ci struct smb2_write_rsp *rsp = NULL; 484462306a36Sopenharmony_ci int resp_buftype; 484562306a36Sopenharmony_ci struct kvec rsp_iov; 484662306a36Sopenharmony_ci int flags = 0; 484762306a36Sopenharmony_ci unsigned int total_len; 484862306a36Sopenharmony_ci struct TCP_Server_Info *server; 484962306a36Sopenharmony_ci 485062306a36Sopenharmony_ci *nbytes = 0; 485162306a36Sopenharmony_ci 485262306a36Sopenharmony_ci if (n_vec < 1) 485362306a36Sopenharmony_ci return rc; 485462306a36Sopenharmony_ci 485562306a36Sopenharmony_ci if (!io_parms->server) 485662306a36Sopenharmony_ci io_parms->server = cifs_pick_channel(io_parms->tcon->ses); 485762306a36Sopenharmony_ci server = io_parms->server; 485862306a36Sopenharmony_ci if (server == NULL) 485962306a36Sopenharmony_ci return -ECONNABORTED; 486062306a36Sopenharmony_ci 486162306a36Sopenharmony_ci rc = smb2_plain_req_init(SMB2_WRITE, io_parms->tcon, server, 486262306a36Sopenharmony_ci (void **) &req, &total_len); 486362306a36Sopenharmony_ci if (rc) 486462306a36Sopenharmony_ci return rc; 486562306a36Sopenharmony_ci 486662306a36Sopenharmony_ci if (smb3_encryption_required(io_parms->tcon)) 486762306a36Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 486862306a36Sopenharmony_ci 486962306a36Sopenharmony_ci req->hdr.Id.SyncId.ProcessId = cpu_to_le32(io_parms->pid); 487062306a36Sopenharmony_ci 487162306a36Sopenharmony_ci req->PersistentFileId = io_parms->persistent_fid; 487262306a36Sopenharmony_ci req->VolatileFileId = io_parms->volatile_fid; 487362306a36Sopenharmony_ci req->WriteChannelInfoOffset = 0; 487462306a36Sopenharmony_ci req->WriteChannelInfoLength = 0; 487562306a36Sopenharmony_ci req->Channel = 0; 487662306a36Sopenharmony_ci req->Length = cpu_to_le32(io_parms->length); 487762306a36Sopenharmony_ci req->Offset = cpu_to_le64(io_parms->offset); 487862306a36Sopenharmony_ci req->DataOffset = cpu_to_le16( 487962306a36Sopenharmony_ci offsetof(struct smb2_write_req, Buffer)); 488062306a36Sopenharmony_ci req->RemainingBytes = 0; 488162306a36Sopenharmony_ci 488262306a36Sopenharmony_ci trace_smb3_write_enter(xid, io_parms->persistent_fid, 488362306a36Sopenharmony_ci io_parms->tcon->tid, io_parms->tcon->ses->Suid, 488462306a36Sopenharmony_ci io_parms->offset, io_parms->length); 488562306a36Sopenharmony_ci 488662306a36Sopenharmony_ci iov[0].iov_base = (char *)req; 488762306a36Sopenharmony_ci /* 1 for Buffer */ 488862306a36Sopenharmony_ci iov[0].iov_len = total_len - 1; 488962306a36Sopenharmony_ci 489062306a36Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 489162306a36Sopenharmony_ci rqst.rq_iov = iov; 489262306a36Sopenharmony_ci rqst.rq_nvec = n_vec + 1; 489362306a36Sopenharmony_ci 489462306a36Sopenharmony_ci rc = cifs_send_recv(xid, io_parms->tcon->ses, server, 489562306a36Sopenharmony_ci &rqst, 489662306a36Sopenharmony_ci &resp_buftype, flags, &rsp_iov); 489762306a36Sopenharmony_ci rsp = (struct smb2_write_rsp *)rsp_iov.iov_base; 489862306a36Sopenharmony_ci 489962306a36Sopenharmony_ci if (rc) { 490062306a36Sopenharmony_ci trace_smb3_write_err(xid, 490162306a36Sopenharmony_ci req->PersistentFileId, 490262306a36Sopenharmony_ci io_parms->tcon->tid, 490362306a36Sopenharmony_ci io_parms->tcon->ses->Suid, 490462306a36Sopenharmony_ci io_parms->offset, io_parms->length, rc); 490562306a36Sopenharmony_ci cifs_stats_fail_inc(io_parms->tcon, SMB2_WRITE_HE); 490662306a36Sopenharmony_ci cifs_dbg(VFS, "Send error in write = %d\n", rc); 490762306a36Sopenharmony_ci } else { 490862306a36Sopenharmony_ci *nbytes = le32_to_cpu(rsp->DataLength); 490962306a36Sopenharmony_ci trace_smb3_write_done(xid, 491062306a36Sopenharmony_ci req->PersistentFileId, 491162306a36Sopenharmony_ci io_parms->tcon->tid, 491262306a36Sopenharmony_ci io_parms->tcon->ses->Suid, 491362306a36Sopenharmony_ci io_parms->offset, *nbytes); 491462306a36Sopenharmony_ci } 491562306a36Sopenharmony_ci 491662306a36Sopenharmony_ci cifs_small_buf_release(req); 491762306a36Sopenharmony_ci free_rsp_buf(resp_buftype, rsp); 491862306a36Sopenharmony_ci return rc; 491962306a36Sopenharmony_ci} 492062306a36Sopenharmony_ci 492162306a36Sopenharmony_ciint posix_info_sid_size(const void *beg, const void *end) 492262306a36Sopenharmony_ci{ 492362306a36Sopenharmony_ci size_t subauth; 492462306a36Sopenharmony_ci int total; 492562306a36Sopenharmony_ci 492662306a36Sopenharmony_ci if (beg + 1 > end) 492762306a36Sopenharmony_ci return -1; 492862306a36Sopenharmony_ci 492962306a36Sopenharmony_ci subauth = *(u8 *)(beg+1); 493062306a36Sopenharmony_ci if (subauth < 1 || subauth > 15) 493162306a36Sopenharmony_ci return -1; 493262306a36Sopenharmony_ci 493362306a36Sopenharmony_ci total = 1 + 1 + 6 + 4*subauth; 493462306a36Sopenharmony_ci if (beg + total > end) 493562306a36Sopenharmony_ci return -1; 493662306a36Sopenharmony_ci 493762306a36Sopenharmony_ci return total; 493862306a36Sopenharmony_ci} 493962306a36Sopenharmony_ci 494062306a36Sopenharmony_ciint posix_info_parse(const void *beg, const void *end, 494162306a36Sopenharmony_ci struct smb2_posix_info_parsed *out) 494262306a36Sopenharmony_ci 494362306a36Sopenharmony_ci{ 494462306a36Sopenharmony_ci int total_len = 0; 494562306a36Sopenharmony_ci int owner_len, group_len; 494662306a36Sopenharmony_ci int name_len; 494762306a36Sopenharmony_ci const void *owner_sid; 494862306a36Sopenharmony_ci const void *group_sid; 494962306a36Sopenharmony_ci const void *name; 495062306a36Sopenharmony_ci 495162306a36Sopenharmony_ci /* if no end bound given, assume payload to be correct */ 495262306a36Sopenharmony_ci if (!end) { 495362306a36Sopenharmony_ci const struct smb2_posix_info *p = beg; 495462306a36Sopenharmony_ci 495562306a36Sopenharmony_ci end = beg + le32_to_cpu(p->NextEntryOffset); 495662306a36Sopenharmony_ci /* last element will have a 0 offset, pick a sensible bound */ 495762306a36Sopenharmony_ci if (end == beg) 495862306a36Sopenharmony_ci end += 0xFFFF; 495962306a36Sopenharmony_ci } 496062306a36Sopenharmony_ci 496162306a36Sopenharmony_ci /* check base buf */ 496262306a36Sopenharmony_ci if (beg + sizeof(struct smb2_posix_info) > end) 496362306a36Sopenharmony_ci return -1; 496462306a36Sopenharmony_ci total_len = sizeof(struct smb2_posix_info); 496562306a36Sopenharmony_ci 496662306a36Sopenharmony_ci /* check owner sid */ 496762306a36Sopenharmony_ci owner_sid = beg + total_len; 496862306a36Sopenharmony_ci owner_len = posix_info_sid_size(owner_sid, end); 496962306a36Sopenharmony_ci if (owner_len < 0) 497062306a36Sopenharmony_ci return -1; 497162306a36Sopenharmony_ci total_len += owner_len; 497262306a36Sopenharmony_ci 497362306a36Sopenharmony_ci /* check group sid */ 497462306a36Sopenharmony_ci group_sid = beg + total_len; 497562306a36Sopenharmony_ci group_len = posix_info_sid_size(group_sid, end); 497662306a36Sopenharmony_ci if (group_len < 0) 497762306a36Sopenharmony_ci return -1; 497862306a36Sopenharmony_ci total_len += group_len; 497962306a36Sopenharmony_ci 498062306a36Sopenharmony_ci /* check name len */ 498162306a36Sopenharmony_ci if (beg + total_len + 4 > end) 498262306a36Sopenharmony_ci return -1; 498362306a36Sopenharmony_ci name_len = le32_to_cpu(*(__le32 *)(beg + total_len)); 498462306a36Sopenharmony_ci if (name_len < 1 || name_len > 0xFFFF) 498562306a36Sopenharmony_ci return -1; 498662306a36Sopenharmony_ci total_len += 4; 498762306a36Sopenharmony_ci 498862306a36Sopenharmony_ci /* check name */ 498962306a36Sopenharmony_ci name = beg + total_len; 499062306a36Sopenharmony_ci if (name + name_len > end) 499162306a36Sopenharmony_ci return -1; 499262306a36Sopenharmony_ci total_len += name_len; 499362306a36Sopenharmony_ci 499462306a36Sopenharmony_ci if (out) { 499562306a36Sopenharmony_ci out->base = beg; 499662306a36Sopenharmony_ci out->size = total_len; 499762306a36Sopenharmony_ci out->name_len = name_len; 499862306a36Sopenharmony_ci out->name = name; 499962306a36Sopenharmony_ci memcpy(&out->owner, owner_sid, owner_len); 500062306a36Sopenharmony_ci memcpy(&out->group, group_sid, group_len); 500162306a36Sopenharmony_ci } 500262306a36Sopenharmony_ci return total_len; 500362306a36Sopenharmony_ci} 500462306a36Sopenharmony_ci 500562306a36Sopenharmony_cistatic int posix_info_extra_size(const void *beg, const void *end) 500662306a36Sopenharmony_ci{ 500762306a36Sopenharmony_ci int len = posix_info_parse(beg, end, NULL); 500862306a36Sopenharmony_ci 500962306a36Sopenharmony_ci if (len < 0) 501062306a36Sopenharmony_ci return -1; 501162306a36Sopenharmony_ci return len - sizeof(struct smb2_posix_info); 501262306a36Sopenharmony_ci} 501362306a36Sopenharmony_ci 501462306a36Sopenharmony_cistatic unsigned int 501562306a36Sopenharmony_cinum_entries(int infotype, char *bufstart, char *end_of_buf, char **lastentry, 501662306a36Sopenharmony_ci size_t size) 501762306a36Sopenharmony_ci{ 501862306a36Sopenharmony_ci int len; 501962306a36Sopenharmony_ci unsigned int entrycount = 0; 502062306a36Sopenharmony_ci unsigned int next_offset = 0; 502162306a36Sopenharmony_ci char *entryptr; 502262306a36Sopenharmony_ci FILE_DIRECTORY_INFO *dir_info; 502362306a36Sopenharmony_ci 502462306a36Sopenharmony_ci if (bufstart == NULL) 502562306a36Sopenharmony_ci return 0; 502662306a36Sopenharmony_ci 502762306a36Sopenharmony_ci entryptr = bufstart; 502862306a36Sopenharmony_ci 502962306a36Sopenharmony_ci while (1) { 503062306a36Sopenharmony_ci if (entryptr + next_offset < entryptr || 503162306a36Sopenharmony_ci entryptr + next_offset > end_of_buf || 503262306a36Sopenharmony_ci entryptr + next_offset + size > end_of_buf) { 503362306a36Sopenharmony_ci cifs_dbg(VFS, "malformed search entry would overflow\n"); 503462306a36Sopenharmony_ci break; 503562306a36Sopenharmony_ci } 503662306a36Sopenharmony_ci 503762306a36Sopenharmony_ci entryptr = entryptr + next_offset; 503862306a36Sopenharmony_ci dir_info = (FILE_DIRECTORY_INFO *)entryptr; 503962306a36Sopenharmony_ci 504062306a36Sopenharmony_ci if (infotype == SMB_FIND_FILE_POSIX_INFO) 504162306a36Sopenharmony_ci len = posix_info_extra_size(entryptr, end_of_buf); 504262306a36Sopenharmony_ci else 504362306a36Sopenharmony_ci len = le32_to_cpu(dir_info->FileNameLength); 504462306a36Sopenharmony_ci 504562306a36Sopenharmony_ci if (len < 0 || 504662306a36Sopenharmony_ci entryptr + len < entryptr || 504762306a36Sopenharmony_ci entryptr + len > end_of_buf || 504862306a36Sopenharmony_ci entryptr + len + size > end_of_buf) { 504962306a36Sopenharmony_ci cifs_dbg(VFS, "directory entry name would overflow frame end of buf %p\n", 505062306a36Sopenharmony_ci end_of_buf); 505162306a36Sopenharmony_ci break; 505262306a36Sopenharmony_ci } 505362306a36Sopenharmony_ci 505462306a36Sopenharmony_ci *lastentry = entryptr; 505562306a36Sopenharmony_ci entrycount++; 505662306a36Sopenharmony_ci 505762306a36Sopenharmony_ci next_offset = le32_to_cpu(dir_info->NextEntryOffset); 505862306a36Sopenharmony_ci if (!next_offset) 505962306a36Sopenharmony_ci break; 506062306a36Sopenharmony_ci } 506162306a36Sopenharmony_ci 506262306a36Sopenharmony_ci return entrycount; 506362306a36Sopenharmony_ci} 506462306a36Sopenharmony_ci 506562306a36Sopenharmony_ci/* 506662306a36Sopenharmony_ci * Readdir/FindFirst 506762306a36Sopenharmony_ci */ 506862306a36Sopenharmony_ciint SMB2_query_directory_init(const unsigned int xid, 506962306a36Sopenharmony_ci struct cifs_tcon *tcon, 507062306a36Sopenharmony_ci struct TCP_Server_Info *server, 507162306a36Sopenharmony_ci struct smb_rqst *rqst, 507262306a36Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, 507362306a36Sopenharmony_ci int index, int info_level) 507462306a36Sopenharmony_ci{ 507562306a36Sopenharmony_ci struct smb2_query_directory_req *req; 507662306a36Sopenharmony_ci unsigned char *bufptr; 507762306a36Sopenharmony_ci __le16 asteriks = cpu_to_le16('*'); 507862306a36Sopenharmony_ci unsigned int output_size = CIFSMaxBufSize - 507962306a36Sopenharmony_ci MAX_SMB2_CREATE_RESPONSE_SIZE - 508062306a36Sopenharmony_ci MAX_SMB2_CLOSE_RESPONSE_SIZE; 508162306a36Sopenharmony_ci unsigned int total_len; 508262306a36Sopenharmony_ci struct kvec *iov = rqst->rq_iov; 508362306a36Sopenharmony_ci int len, rc; 508462306a36Sopenharmony_ci 508562306a36Sopenharmony_ci rc = smb2_plain_req_init(SMB2_QUERY_DIRECTORY, tcon, server, 508662306a36Sopenharmony_ci (void **) &req, &total_len); 508762306a36Sopenharmony_ci if (rc) 508862306a36Sopenharmony_ci return rc; 508962306a36Sopenharmony_ci 509062306a36Sopenharmony_ci switch (info_level) { 509162306a36Sopenharmony_ci case SMB_FIND_FILE_DIRECTORY_INFO: 509262306a36Sopenharmony_ci req->FileInformationClass = FILE_DIRECTORY_INFORMATION; 509362306a36Sopenharmony_ci break; 509462306a36Sopenharmony_ci case SMB_FIND_FILE_ID_FULL_DIR_INFO: 509562306a36Sopenharmony_ci req->FileInformationClass = FILEID_FULL_DIRECTORY_INFORMATION; 509662306a36Sopenharmony_ci break; 509762306a36Sopenharmony_ci case SMB_FIND_FILE_POSIX_INFO: 509862306a36Sopenharmony_ci req->FileInformationClass = SMB_FIND_FILE_POSIX_INFO; 509962306a36Sopenharmony_ci break; 510062306a36Sopenharmony_ci case SMB_FIND_FILE_FULL_DIRECTORY_INFO: 510162306a36Sopenharmony_ci req->FileInformationClass = FILE_FULL_DIRECTORY_INFORMATION; 510262306a36Sopenharmony_ci break; 510362306a36Sopenharmony_ci default: 510462306a36Sopenharmony_ci cifs_tcon_dbg(VFS, "info level %u isn't supported\n", 510562306a36Sopenharmony_ci info_level); 510662306a36Sopenharmony_ci return -EINVAL; 510762306a36Sopenharmony_ci } 510862306a36Sopenharmony_ci 510962306a36Sopenharmony_ci req->FileIndex = cpu_to_le32(index); 511062306a36Sopenharmony_ci req->PersistentFileId = persistent_fid; 511162306a36Sopenharmony_ci req->VolatileFileId = volatile_fid; 511262306a36Sopenharmony_ci 511362306a36Sopenharmony_ci len = 0x2; 511462306a36Sopenharmony_ci bufptr = req->Buffer; 511562306a36Sopenharmony_ci memcpy(bufptr, &asteriks, len); 511662306a36Sopenharmony_ci 511762306a36Sopenharmony_ci req->FileNameOffset = 511862306a36Sopenharmony_ci cpu_to_le16(sizeof(struct smb2_query_directory_req)); 511962306a36Sopenharmony_ci req->FileNameLength = cpu_to_le16(len); 512062306a36Sopenharmony_ci /* 512162306a36Sopenharmony_ci * BB could be 30 bytes or so longer if we used SMB2 specific 512262306a36Sopenharmony_ci * buffer lengths, but this is safe and close enough. 512362306a36Sopenharmony_ci */ 512462306a36Sopenharmony_ci output_size = min_t(unsigned int, output_size, server->maxBuf); 512562306a36Sopenharmony_ci output_size = min_t(unsigned int, output_size, 2 << 15); 512662306a36Sopenharmony_ci req->OutputBufferLength = cpu_to_le32(output_size); 512762306a36Sopenharmony_ci 512862306a36Sopenharmony_ci iov[0].iov_base = (char *)req; 512962306a36Sopenharmony_ci /* 1 for Buffer */ 513062306a36Sopenharmony_ci iov[0].iov_len = total_len - 1; 513162306a36Sopenharmony_ci 513262306a36Sopenharmony_ci iov[1].iov_base = (char *)(req->Buffer); 513362306a36Sopenharmony_ci iov[1].iov_len = len; 513462306a36Sopenharmony_ci 513562306a36Sopenharmony_ci trace_smb3_query_dir_enter(xid, persistent_fid, tcon->tid, 513662306a36Sopenharmony_ci tcon->ses->Suid, index, output_size); 513762306a36Sopenharmony_ci 513862306a36Sopenharmony_ci return 0; 513962306a36Sopenharmony_ci} 514062306a36Sopenharmony_ci 514162306a36Sopenharmony_civoid SMB2_query_directory_free(struct smb_rqst *rqst) 514262306a36Sopenharmony_ci{ 514362306a36Sopenharmony_ci if (rqst && rqst->rq_iov) { 514462306a36Sopenharmony_ci cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ 514562306a36Sopenharmony_ci } 514662306a36Sopenharmony_ci} 514762306a36Sopenharmony_ci 514862306a36Sopenharmony_ciint 514962306a36Sopenharmony_cismb2_parse_query_directory(struct cifs_tcon *tcon, 515062306a36Sopenharmony_ci struct kvec *rsp_iov, 515162306a36Sopenharmony_ci int resp_buftype, 515262306a36Sopenharmony_ci struct cifs_search_info *srch_inf) 515362306a36Sopenharmony_ci{ 515462306a36Sopenharmony_ci struct smb2_query_directory_rsp *rsp; 515562306a36Sopenharmony_ci size_t info_buf_size; 515662306a36Sopenharmony_ci char *end_of_smb; 515762306a36Sopenharmony_ci int rc; 515862306a36Sopenharmony_ci 515962306a36Sopenharmony_ci rsp = (struct smb2_query_directory_rsp *)rsp_iov->iov_base; 516062306a36Sopenharmony_ci 516162306a36Sopenharmony_ci switch (srch_inf->info_level) { 516262306a36Sopenharmony_ci case SMB_FIND_FILE_DIRECTORY_INFO: 516362306a36Sopenharmony_ci info_buf_size = sizeof(FILE_DIRECTORY_INFO); 516462306a36Sopenharmony_ci break; 516562306a36Sopenharmony_ci case SMB_FIND_FILE_ID_FULL_DIR_INFO: 516662306a36Sopenharmony_ci info_buf_size = sizeof(SEARCH_ID_FULL_DIR_INFO); 516762306a36Sopenharmony_ci break; 516862306a36Sopenharmony_ci case SMB_FIND_FILE_POSIX_INFO: 516962306a36Sopenharmony_ci /* note that posix payload are variable size */ 517062306a36Sopenharmony_ci info_buf_size = sizeof(struct smb2_posix_info); 517162306a36Sopenharmony_ci break; 517262306a36Sopenharmony_ci case SMB_FIND_FILE_FULL_DIRECTORY_INFO: 517362306a36Sopenharmony_ci info_buf_size = sizeof(FILE_FULL_DIRECTORY_INFO); 517462306a36Sopenharmony_ci break; 517562306a36Sopenharmony_ci default: 517662306a36Sopenharmony_ci cifs_tcon_dbg(VFS, "info level %u isn't supported\n", 517762306a36Sopenharmony_ci srch_inf->info_level); 517862306a36Sopenharmony_ci return -EINVAL; 517962306a36Sopenharmony_ci } 518062306a36Sopenharmony_ci 518162306a36Sopenharmony_ci rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), 518262306a36Sopenharmony_ci le32_to_cpu(rsp->OutputBufferLength), rsp_iov, 518362306a36Sopenharmony_ci info_buf_size); 518462306a36Sopenharmony_ci if (rc) { 518562306a36Sopenharmony_ci cifs_tcon_dbg(VFS, "bad info payload"); 518662306a36Sopenharmony_ci return rc; 518762306a36Sopenharmony_ci } 518862306a36Sopenharmony_ci 518962306a36Sopenharmony_ci srch_inf->unicode = true; 519062306a36Sopenharmony_ci 519162306a36Sopenharmony_ci if (srch_inf->ntwrk_buf_start) { 519262306a36Sopenharmony_ci if (srch_inf->smallBuf) 519362306a36Sopenharmony_ci cifs_small_buf_release(srch_inf->ntwrk_buf_start); 519462306a36Sopenharmony_ci else 519562306a36Sopenharmony_ci cifs_buf_release(srch_inf->ntwrk_buf_start); 519662306a36Sopenharmony_ci } 519762306a36Sopenharmony_ci srch_inf->ntwrk_buf_start = (char *)rsp; 519862306a36Sopenharmony_ci srch_inf->srch_entries_start = srch_inf->last_entry = 519962306a36Sopenharmony_ci (char *)rsp + le16_to_cpu(rsp->OutputBufferOffset); 520062306a36Sopenharmony_ci end_of_smb = rsp_iov->iov_len + (char *)rsp; 520162306a36Sopenharmony_ci 520262306a36Sopenharmony_ci srch_inf->entries_in_buffer = num_entries( 520362306a36Sopenharmony_ci srch_inf->info_level, 520462306a36Sopenharmony_ci srch_inf->srch_entries_start, 520562306a36Sopenharmony_ci end_of_smb, 520662306a36Sopenharmony_ci &srch_inf->last_entry, 520762306a36Sopenharmony_ci info_buf_size); 520862306a36Sopenharmony_ci 520962306a36Sopenharmony_ci srch_inf->index_of_last_entry += srch_inf->entries_in_buffer; 521062306a36Sopenharmony_ci cifs_dbg(FYI, "num entries %d last_index %lld srch start %p srch end %p\n", 521162306a36Sopenharmony_ci srch_inf->entries_in_buffer, srch_inf->index_of_last_entry, 521262306a36Sopenharmony_ci srch_inf->srch_entries_start, srch_inf->last_entry); 521362306a36Sopenharmony_ci if (resp_buftype == CIFS_LARGE_BUFFER) 521462306a36Sopenharmony_ci srch_inf->smallBuf = false; 521562306a36Sopenharmony_ci else if (resp_buftype == CIFS_SMALL_BUFFER) 521662306a36Sopenharmony_ci srch_inf->smallBuf = true; 521762306a36Sopenharmony_ci else 521862306a36Sopenharmony_ci cifs_tcon_dbg(VFS, "Invalid search buffer type\n"); 521962306a36Sopenharmony_ci 522062306a36Sopenharmony_ci return 0; 522162306a36Sopenharmony_ci} 522262306a36Sopenharmony_ci 522362306a36Sopenharmony_ciint 522462306a36Sopenharmony_ciSMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, 522562306a36Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, int index, 522662306a36Sopenharmony_ci struct cifs_search_info *srch_inf) 522762306a36Sopenharmony_ci{ 522862306a36Sopenharmony_ci struct smb_rqst rqst; 522962306a36Sopenharmony_ci struct kvec iov[SMB2_QUERY_DIRECTORY_IOV_SIZE]; 523062306a36Sopenharmony_ci struct smb2_query_directory_rsp *rsp = NULL; 523162306a36Sopenharmony_ci int resp_buftype = CIFS_NO_BUFFER; 523262306a36Sopenharmony_ci struct kvec rsp_iov; 523362306a36Sopenharmony_ci int rc = 0; 523462306a36Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 523562306a36Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(ses); 523662306a36Sopenharmony_ci int flags = 0; 523762306a36Sopenharmony_ci 523862306a36Sopenharmony_ci if (!ses || !(ses->server)) 523962306a36Sopenharmony_ci return -EIO; 524062306a36Sopenharmony_ci 524162306a36Sopenharmony_ci if (smb3_encryption_required(tcon)) 524262306a36Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 524362306a36Sopenharmony_ci 524462306a36Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 524562306a36Sopenharmony_ci memset(&iov, 0, sizeof(iov)); 524662306a36Sopenharmony_ci rqst.rq_iov = iov; 524762306a36Sopenharmony_ci rqst.rq_nvec = SMB2_QUERY_DIRECTORY_IOV_SIZE; 524862306a36Sopenharmony_ci 524962306a36Sopenharmony_ci rc = SMB2_query_directory_init(xid, tcon, server, 525062306a36Sopenharmony_ci &rqst, persistent_fid, 525162306a36Sopenharmony_ci volatile_fid, index, 525262306a36Sopenharmony_ci srch_inf->info_level); 525362306a36Sopenharmony_ci if (rc) 525462306a36Sopenharmony_ci goto qdir_exit; 525562306a36Sopenharmony_ci 525662306a36Sopenharmony_ci rc = cifs_send_recv(xid, ses, server, 525762306a36Sopenharmony_ci &rqst, &resp_buftype, flags, &rsp_iov); 525862306a36Sopenharmony_ci rsp = (struct smb2_query_directory_rsp *)rsp_iov.iov_base; 525962306a36Sopenharmony_ci 526062306a36Sopenharmony_ci if (rc) { 526162306a36Sopenharmony_ci if (rc == -ENODATA && 526262306a36Sopenharmony_ci rsp->hdr.Status == STATUS_NO_MORE_FILES) { 526362306a36Sopenharmony_ci trace_smb3_query_dir_done(xid, persistent_fid, 526462306a36Sopenharmony_ci tcon->tid, tcon->ses->Suid, index, 0); 526562306a36Sopenharmony_ci srch_inf->endOfSearch = true; 526662306a36Sopenharmony_ci rc = 0; 526762306a36Sopenharmony_ci } else { 526862306a36Sopenharmony_ci trace_smb3_query_dir_err(xid, persistent_fid, tcon->tid, 526962306a36Sopenharmony_ci tcon->ses->Suid, index, 0, rc); 527062306a36Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_QUERY_DIRECTORY_HE); 527162306a36Sopenharmony_ci } 527262306a36Sopenharmony_ci goto qdir_exit; 527362306a36Sopenharmony_ci } 527462306a36Sopenharmony_ci 527562306a36Sopenharmony_ci rc = smb2_parse_query_directory(tcon, &rsp_iov, resp_buftype, 527662306a36Sopenharmony_ci srch_inf); 527762306a36Sopenharmony_ci if (rc) { 527862306a36Sopenharmony_ci trace_smb3_query_dir_err(xid, persistent_fid, tcon->tid, 527962306a36Sopenharmony_ci tcon->ses->Suid, index, 0, rc); 528062306a36Sopenharmony_ci goto qdir_exit; 528162306a36Sopenharmony_ci } 528262306a36Sopenharmony_ci resp_buftype = CIFS_NO_BUFFER; 528362306a36Sopenharmony_ci 528462306a36Sopenharmony_ci trace_smb3_query_dir_done(xid, persistent_fid, tcon->tid, 528562306a36Sopenharmony_ci tcon->ses->Suid, index, srch_inf->entries_in_buffer); 528662306a36Sopenharmony_ci 528762306a36Sopenharmony_ciqdir_exit: 528862306a36Sopenharmony_ci SMB2_query_directory_free(&rqst); 528962306a36Sopenharmony_ci free_rsp_buf(resp_buftype, rsp); 529062306a36Sopenharmony_ci return rc; 529162306a36Sopenharmony_ci} 529262306a36Sopenharmony_ci 529362306a36Sopenharmony_ciint 529462306a36Sopenharmony_ciSMB2_set_info_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, 529562306a36Sopenharmony_ci struct smb_rqst *rqst, 529662306a36Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, u32 pid, 529762306a36Sopenharmony_ci u8 info_class, u8 info_type, u32 additional_info, 529862306a36Sopenharmony_ci void **data, unsigned int *size) 529962306a36Sopenharmony_ci{ 530062306a36Sopenharmony_ci struct smb2_set_info_req *req; 530162306a36Sopenharmony_ci struct kvec *iov = rqst->rq_iov; 530262306a36Sopenharmony_ci unsigned int i, total_len; 530362306a36Sopenharmony_ci int rc; 530462306a36Sopenharmony_ci 530562306a36Sopenharmony_ci rc = smb2_plain_req_init(SMB2_SET_INFO, tcon, server, 530662306a36Sopenharmony_ci (void **) &req, &total_len); 530762306a36Sopenharmony_ci if (rc) 530862306a36Sopenharmony_ci return rc; 530962306a36Sopenharmony_ci 531062306a36Sopenharmony_ci req->hdr.Id.SyncId.ProcessId = cpu_to_le32(pid); 531162306a36Sopenharmony_ci req->InfoType = info_type; 531262306a36Sopenharmony_ci req->FileInfoClass = info_class; 531362306a36Sopenharmony_ci req->PersistentFileId = persistent_fid; 531462306a36Sopenharmony_ci req->VolatileFileId = volatile_fid; 531562306a36Sopenharmony_ci req->AdditionalInformation = cpu_to_le32(additional_info); 531662306a36Sopenharmony_ci 531762306a36Sopenharmony_ci req->BufferOffset = cpu_to_le16(sizeof(struct smb2_set_info_req)); 531862306a36Sopenharmony_ci req->BufferLength = cpu_to_le32(*size); 531962306a36Sopenharmony_ci 532062306a36Sopenharmony_ci memcpy(req->Buffer, *data, *size); 532162306a36Sopenharmony_ci total_len += *size; 532262306a36Sopenharmony_ci 532362306a36Sopenharmony_ci iov[0].iov_base = (char *)req; 532462306a36Sopenharmony_ci /* 1 for Buffer */ 532562306a36Sopenharmony_ci iov[0].iov_len = total_len - 1; 532662306a36Sopenharmony_ci 532762306a36Sopenharmony_ci for (i = 1; i < rqst->rq_nvec; i++) { 532862306a36Sopenharmony_ci le32_add_cpu(&req->BufferLength, size[i]); 532962306a36Sopenharmony_ci iov[i].iov_base = (char *)data[i]; 533062306a36Sopenharmony_ci iov[i].iov_len = size[i]; 533162306a36Sopenharmony_ci } 533262306a36Sopenharmony_ci 533362306a36Sopenharmony_ci return 0; 533462306a36Sopenharmony_ci} 533562306a36Sopenharmony_ci 533662306a36Sopenharmony_civoid 533762306a36Sopenharmony_ciSMB2_set_info_free(struct smb_rqst *rqst) 533862306a36Sopenharmony_ci{ 533962306a36Sopenharmony_ci if (rqst && rqst->rq_iov) 534062306a36Sopenharmony_ci cifs_buf_release(rqst->rq_iov[0].iov_base); /* request */ 534162306a36Sopenharmony_ci} 534262306a36Sopenharmony_ci 534362306a36Sopenharmony_cistatic int 534462306a36Sopenharmony_cisend_set_info(const unsigned int xid, struct cifs_tcon *tcon, 534562306a36Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, u32 pid, u8 info_class, 534662306a36Sopenharmony_ci u8 info_type, u32 additional_info, unsigned int num, 534762306a36Sopenharmony_ci void **data, unsigned int *size) 534862306a36Sopenharmony_ci{ 534962306a36Sopenharmony_ci struct smb_rqst rqst; 535062306a36Sopenharmony_ci struct smb2_set_info_rsp *rsp = NULL; 535162306a36Sopenharmony_ci struct kvec *iov; 535262306a36Sopenharmony_ci struct kvec rsp_iov; 535362306a36Sopenharmony_ci int rc = 0; 535462306a36Sopenharmony_ci int resp_buftype; 535562306a36Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 535662306a36Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(ses); 535762306a36Sopenharmony_ci int flags = 0; 535862306a36Sopenharmony_ci 535962306a36Sopenharmony_ci if (!ses || !server) 536062306a36Sopenharmony_ci return -EIO; 536162306a36Sopenharmony_ci 536262306a36Sopenharmony_ci if (!num) 536362306a36Sopenharmony_ci return -EINVAL; 536462306a36Sopenharmony_ci 536562306a36Sopenharmony_ci if (smb3_encryption_required(tcon)) 536662306a36Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 536762306a36Sopenharmony_ci 536862306a36Sopenharmony_ci iov = kmalloc_array(num, sizeof(struct kvec), GFP_KERNEL); 536962306a36Sopenharmony_ci if (!iov) 537062306a36Sopenharmony_ci return -ENOMEM; 537162306a36Sopenharmony_ci 537262306a36Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 537362306a36Sopenharmony_ci rqst.rq_iov = iov; 537462306a36Sopenharmony_ci rqst.rq_nvec = num; 537562306a36Sopenharmony_ci 537662306a36Sopenharmony_ci rc = SMB2_set_info_init(tcon, server, 537762306a36Sopenharmony_ci &rqst, persistent_fid, volatile_fid, pid, 537862306a36Sopenharmony_ci info_class, info_type, additional_info, 537962306a36Sopenharmony_ci data, size); 538062306a36Sopenharmony_ci if (rc) { 538162306a36Sopenharmony_ci kfree(iov); 538262306a36Sopenharmony_ci return rc; 538362306a36Sopenharmony_ci } 538462306a36Sopenharmony_ci 538562306a36Sopenharmony_ci 538662306a36Sopenharmony_ci rc = cifs_send_recv(xid, ses, server, 538762306a36Sopenharmony_ci &rqst, &resp_buftype, flags, 538862306a36Sopenharmony_ci &rsp_iov); 538962306a36Sopenharmony_ci SMB2_set_info_free(&rqst); 539062306a36Sopenharmony_ci rsp = (struct smb2_set_info_rsp *)rsp_iov.iov_base; 539162306a36Sopenharmony_ci 539262306a36Sopenharmony_ci if (rc != 0) { 539362306a36Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_SET_INFO_HE); 539462306a36Sopenharmony_ci trace_smb3_set_info_err(xid, persistent_fid, tcon->tid, 539562306a36Sopenharmony_ci ses->Suid, info_class, (__u32)info_type, rc); 539662306a36Sopenharmony_ci } 539762306a36Sopenharmony_ci 539862306a36Sopenharmony_ci free_rsp_buf(resp_buftype, rsp); 539962306a36Sopenharmony_ci kfree(iov); 540062306a36Sopenharmony_ci return rc; 540162306a36Sopenharmony_ci} 540262306a36Sopenharmony_ci 540362306a36Sopenharmony_ciint 540462306a36Sopenharmony_ciSMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, 540562306a36Sopenharmony_ci u64 volatile_fid, u32 pid, __le64 *eof) 540662306a36Sopenharmony_ci{ 540762306a36Sopenharmony_ci struct smb2_file_eof_info info; 540862306a36Sopenharmony_ci void *data; 540962306a36Sopenharmony_ci unsigned int size; 541062306a36Sopenharmony_ci 541162306a36Sopenharmony_ci info.EndOfFile = *eof; 541262306a36Sopenharmony_ci 541362306a36Sopenharmony_ci data = &info; 541462306a36Sopenharmony_ci size = sizeof(struct smb2_file_eof_info); 541562306a36Sopenharmony_ci 541662306a36Sopenharmony_ci trace_smb3_set_eof(xid, persistent_fid, tcon->tid, tcon->ses->Suid, le64_to_cpu(*eof)); 541762306a36Sopenharmony_ci 541862306a36Sopenharmony_ci return send_set_info(xid, tcon, persistent_fid, volatile_fid, 541962306a36Sopenharmony_ci pid, FILE_END_OF_FILE_INFORMATION, SMB2_O_INFO_FILE, 542062306a36Sopenharmony_ci 0, 1, &data, &size); 542162306a36Sopenharmony_ci} 542262306a36Sopenharmony_ci 542362306a36Sopenharmony_ciint 542462306a36Sopenharmony_ciSMB2_set_acl(const unsigned int xid, struct cifs_tcon *tcon, 542562306a36Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, 542662306a36Sopenharmony_ci struct cifs_ntsd *pnntsd, int pacllen, int aclflag) 542762306a36Sopenharmony_ci{ 542862306a36Sopenharmony_ci return send_set_info(xid, tcon, persistent_fid, volatile_fid, 542962306a36Sopenharmony_ci current->tgid, 0, SMB2_O_INFO_SECURITY, aclflag, 543062306a36Sopenharmony_ci 1, (void **)&pnntsd, &pacllen); 543162306a36Sopenharmony_ci} 543262306a36Sopenharmony_ci 543362306a36Sopenharmony_ciint 543462306a36Sopenharmony_ciSMB2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, 543562306a36Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, 543662306a36Sopenharmony_ci struct smb2_file_full_ea_info *buf, int len) 543762306a36Sopenharmony_ci{ 543862306a36Sopenharmony_ci return send_set_info(xid, tcon, persistent_fid, volatile_fid, 543962306a36Sopenharmony_ci current->tgid, FILE_FULL_EA_INFORMATION, SMB2_O_INFO_FILE, 544062306a36Sopenharmony_ci 0, 1, (void **)&buf, &len); 544162306a36Sopenharmony_ci} 544262306a36Sopenharmony_ci 544362306a36Sopenharmony_ciint 544462306a36Sopenharmony_ciSMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon, 544562306a36Sopenharmony_ci const u64 persistent_fid, const u64 volatile_fid, 544662306a36Sopenharmony_ci __u8 oplock_level) 544762306a36Sopenharmony_ci{ 544862306a36Sopenharmony_ci struct smb_rqst rqst; 544962306a36Sopenharmony_ci int rc; 545062306a36Sopenharmony_ci struct smb2_oplock_break *req = NULL; 545162306a36Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 545262306a36Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(ses); 545362306a36Sopenharmony_ci int flags = CIFS_OBREAK_OP; 545462306a36Sopenharmony_ci unsigned int total_len; 545562306a36Sopenharmony_ci struct kvec iov[1]; 545662306a36Sopenharmony_ci struct kvec rsp_iov; 545762306a36Sopenharmony_ci int resp_buf_type; 545862306a36Sopenharmony_ci 545962306a36Sopenharmony_ci cifs_dbg(FYI, "SMB2_oplock_break\n"); 546062306a36Sopenharmony_ci rc = smb2_plain_req_init(SMB2_OPLOCK_BREAK, tcon, server, 546162306a36Sopenharmony_ci (void **) &req, &total_len); 546262306a36Sopenharmony_ci if (rc) 546362306a36Sopenharmony_ci return rc; 546462306a36Sopenharmony_ci 546562306a36Sopenharmony_ci if (smb3_encryption_required(tcon)) 546662306a36Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 546762306a36Sopenharmony_ci 546862306a36Sopenharmony_ci req->VolatileFid = volatile_fid; 546962306a36Sopenharmony_ci req->PersistentFid = persistent_fid; 547062306a36Sopenharmony_ci req->OplockLevel = oplock_level; 547162306a36Sopenharmony_ci req->hdr.CreditRequest = cpu_to_le16(1); 547262306a36Sopenharmony_ci 547362306a36Sopenharmony_ci flags |= CIFS_NO_RSP_BUF; 547462306a36Sopenharmony_ci 547562306a36Sopenharmony_ci iov[0].iov_base = (char *)req; 547662306a36Sopenharmony_ci iov[0].iov_len = total_len; 547762306a36Sopenharmony_ci 547862306a36Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 547962306a36Sopenharmony_ci rqst.rq_iov = iov; 548062306a36Sopenharmony_ci rqst.rq_nvec = 1; 548162306a36Sopenharmony_ci 548262306a36Sopenharmony_ci rc = cifs_send_recv(xid, ses, server, 548362306a36Sopenharmony_ci &rqst, &resp_buf_type, flags, &rsp_iov); 548462306a36Sopenharmony_ci cifs_small_buf_release(req); 548562306a36Sopenharmony_ci 548662306a36Sopenharmony_ci if (rc) { 548762306a36Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE); 548862306a36Sopenharmony_ci cifs_dbg(FYI, "Send error in Oplock Break = %d\n", rc); 548962306a36Sopenharmony_ci } 549062306a36Sopenharmony_ci 549162306a36Sopenharmony_ci return rc; 549262306a36Sopenharmony_ci} 549362306a36Sopenharmony_ci 549462306a36Sopenharmony_civoid 549562306a36Sopenharmony_cismb2_copy_fs_info_to_kstatfs(struct smb2_fs_full_size_info *pfs_inf, 549662306a36Sopenharmony_ci struct kstatfs *kst) 549762306a36Sopenharmony_ci{ 549862306a36Sopenharmony_ci kst->f_bsize = le32_to_cpu(pfs_inf->BytesPerSector) * 549962306a36Sopenharmony_ci le32_to_cpu(pfs_inf->SectorsPerAllocationUnit); 550062306a36Sopenharmony_ci kst->f_blocks = le64_to_cpu(pfs_inf->TotalAllocationUnits); 550162306a36Sopenharmony_ci kst->f_bfree = kst->f_bavail = 550262306a36Sopenharmony_ci le64_to_cpu(pfs_inf->CallerAvailableAllocationUnits); 550362306a36Sopenharmony_ci return; 550462306a36Sopenharmony_ci} 550562306a36Sopenharmony_ci 550662306a36Sopenharmony_cistatic void 550762306a36Sopenharmony_cicopy_posix_fs_info_to_kstatfs(FILE_SYSTEM_POSIX_INFO *response_data, 550862306a36Sopenharmony_ci struct kstatfs *kst) 550962306a36Sopenharmony_ci{ 551062306a36Sopenharmony_ci kst->f_bsize = le32_to_cpu(response_data->BlockSize); 551162306a36Sopenharmony_ci kst->f_blocks = le64_to_cpu(response_data->TotalBlocks); 551262306a36Sopenharmony_ci kst->f_bfree = le64_to_cpu(response_data->BlocksAvail); 551362306a36Sopenharmony_ci if (response_data->UserBlocksAvail == cpu_to_le64(-1)) 551462306a36Sopenharmony_ci kst->f_bavail = kst->f_bfree; 551562306a36Sopenharmony_ci else 551662306a36Sopenharmony_ci kst->f_bavail = le64_to_cpu(response_data->UserBlocksAvail); 551762306a36Sopenharmony_ci if (response_data->TotalFileNodes != cpu_to_le64(-1)) 551862306a36Sopenharmony_ci kst->f_files = le64_to_cpu(response_data->TotalFileNodes); 551962306a36Sopenharmony_ci if (response_data->FreeFileNodes != cpu_to_le64(-1)) 552062306a36Sopenharmony_ci kst->f_ffree = le64_to_cpu(response_data->FreeFileNodes); 552162306a36Sopenharmony_ci 552262306a36Sopenharmony_ci return; 552362306a36Sopenharmony_ci} 552462306a36Sopenharmony_ci 552562306a36Sopenharmony_cistatic int 552662306a36Sopenharmony_cibuild_qfs_info_req(struct kvec *iov, struct cifs_tcon *tcon, 552762306a36Sopenharmony_ci struct TCP_Server_Info *server, 552862306a36Sopenharmony_ci int level, int outbuf_len, u64 persistent_fid, 552962306a36Sopenharmony_ci u64 volatile_fid) 553062306a36Sopenharmony_ci{ 553162306a36Sopenharmony_ci int rc; 553262306a36Sopenharmony_ci struct smb2_query_info_req *req; 553362306a36Sopenharmony_ci unsigned int total_len; 553462306a36Sopenharmony_ci 553562306a36Sopenharmony_ci cifs_dbg(FYI, "Query FSInfo level %d\n", level); 553662306a36Sopenharmony_ci 553762306a36Sopenharmony_ci if ((tcon->ses == NULL) || server == NULL) 553862306a36Sopenharmony_ci return -EIO; 553962306a36Sopenharmony_ci 554062306a36Sopenharmony_ci rc = smb2_plain_req_init(SMB2_QUERY_INFO, tcon, server, 554162306a36Sopenharmony_ci (void **) &req, &total_len); 554262306a36Sopenharmony_ci if (rc) 554362306a36Sopenharmony_ci return rc; 554462306a36Sopenharmony_ci 554562306a36Sopenharmony_ci req->InfoType = SMB2_O_INFO_FILESYSTEM; 554662306a36Sopenharmony_ci req->FileInfoClass = level; 554762306a36Sopenharmony_ci req->PersistentFileId = persistent_fid; 554862306a36Sopenharmony_ci req->VolatileFileId = volatile_fid; 554962306a36Sopenharmony_ci /* 1 for pad */ 555062306a36Sopenharmony_ci req->InputBufferOffset = 555162306a36Sopenharmony_ci cpu_to_le16(sizeof(struct smb2_query_info_req)); 555262306a36Sopenharmony_ci req->OutputBufferLength = cpu_to_le32( 555362306a36Sopenharmony_ci outbuf_len + sizeof(struct smb2_query_info_rsp)); 555462306a36Sopenharmony_ci 555562306a36Sopenharmony_ci iov->iov_base = (char *)req; 555662306a36Sopenharmony_ci iov->iov_len = total_len; 555762306a36Sopenharmony_ci return 0; 555862306a36Sopenharmony_ci} 555962306a36Sopenharmony_ci 556062306a36Sopenharmony_cistatic inline void free_qfs_info_req(struct kvec *iov) 556162306a36Sopenharmony_ci{ 556262306a36Sopenharmony_ci cifs_buf_release(iov->iov_base); 556362306a36Sopenharmony_ci} 556462306a36Sopenharmony_ci 556562306a36Sopenharmony_ciint 556662306a36Sopenharmony_ciSMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon, 556762306a36Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata) 556862306a36Sopenharmony_ci{ 556962306a36Sopenharmony_ci struct smb_rqst rqst; 557062306a36Sopenharmony_ci struct smb2_query_info_rsp *rsp = NULL; 557162306a36Sopenharmony_ci struct kvec iov; 557262306a36Sopenharmony_ci struct kvec rsp_iov; 557362306a36Sopenharmony_ci int rc = 0; 557462306a36Sopenharmony_ci int resp_buftype; 557562306a36Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 557662306a36Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(ses); 557762306a36Sopenharmony_ci FILE_SYSTEM_POSIX_INFO *info = NULL; 557862306a36Sopenharmony_ci int flags = 0; 557962306a36Sopenharmony_ci 558062306a36Sopenharmony_ci rc = build_qfs_info_req(&iov, tcon, server, 558162306a36Sopenharmony_ci FS_POSIX_INFORMATION, 558262306a36Sopenharmony_ci sizeof(FILE_SYSTEM_POSIX_INFO), 558362306a36Sopenharmony_ci persistent_fid, volatile_fid); 558462306a36Sopenharmony_ci if (rc) 558562306a36Sopenharmony_ci return rc; 558662306a36Sopenharmony_ci 558762306a36Sopenharmony_ci if (smb3_encryption_required(tcon)) 558862306a36Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 558962306a36Sopenharmony_ci 559062306a36Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 559162306a36Sopenharmony_ci rqst.rq_iov = &iov; 559262306a36Sopenharmony_ci rqst.rq_nvec = 1; 559362306a36Sopenharmony_ci 559462306a36Sopenharmony_ci rc = cifs_send_recv(xid, ses, server, 559562306a36Sopenharmony_ci &rqst, &resp_buftype, flags, &rsp_iov); 559662306a36Sopenharmony_ci free_qfs_info_req(&iov); 559762306a36Sopenharmony_ci if (rc) { 559862306a36Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); 559962306a36Sopenharmony_ci goto posix_qfsinf_exit; 560062306a36Sopenharmony_ci } 560162306a36Sopenharmony_ci rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; 560262306a36Sopenharmony_ci 560362306a36Sopenharmony_ci info = (FILE_SYSTEM_POSIX_INFO *)( 560462306a36Sopenharmony_ci le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp); 560562306a36Sopenharmony_ci rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), 560662306a36Sopenharmony_ci le32_to_cpu(rsp->OutputBufferLength), &rsp_iov, 560762306a36Sopenharmony_ci sizeof(FILE_SYSTEM_POSIX_INFO)); 560862306a36Sopenharmony_ci if (!rc) 560962306a36Sopenharmony_ci copy_posix_fs_info_to_kstatfs(info, fsdata); 561062306a36Sopenharmony_ci 561162306a36Sopenharmony_ciposix_qfsinf_exit: 561262306a36Sopenharmony_ci free_rsp_buf(resp_buftype, rsp_iov.iov_base); 561362306a36Sopenharmony_ci return rc; 561462306a36Sopenharmony_ci} 561562306a36Sopenharmony_ci 561662306a36Sopenharmony_ciint 561762306a36Sopenharmony_ciSMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, 561862306a36Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata) 561962306a36Sopenharmony_ci{ 562062306a36Sopenharmony_ci struct smb_rqst rqst; 562162306a36Sopenharmony_ci struct smb2_query_info_rsp *rsp = NULL; 562262306a36Sopenharmony_ci struct kvec iov; 562362306a36Sopenharmony_ci struct kvec rsp_iov; 562462306a36Sopenharmony_ci int rc = 0; 562562306a36Sopenharmony_ci int resp_buftype; 562662306a36Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 562762306a36Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(ses); 562862306a36Sopenharmony_ci struct smb2_fs_full_size_info *info = NULL; 562962306a36Sopenharmony_ci int flags = 0; 563062306a36Sopenharmony_ci 563162306a36Sopenharmony_ci rc = build_qfs_info_req(&iov, tcon, server, 563262306a36Sopenharmony_ci FS_FULL_SIZE_INFORMATION, 563362306a36Sopenharmony_ci sizeof(struct smb2_fs_full_size_info), 563462306a36Sopenharmony_ci persistent_fid, volatile_fid); 563562306a36Sopenharmony_ci if (rc) 563662306a36Sopenharmony_ci return rc; 563762306a36Sopenharmony_ci 563862306a36Sopenharmony_ci if (smb3_encryption_required(tcon)) 563962306a36Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 564062306a36Sopenharmony_ci 564162306a36Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 564262306a36Sopenharmony_ci rqst.rq_iov = &iov; 564362306a36Sopenharmony_ci rqst.rq_nvec = 1; 564462306a36Sopenharmony_ci 564562306a36Sopenharmony_ci rc = cifs_send_recv(xid, ses, server, 564662306a36Sopenharmony_ci &rqst, &resp_buftype, flags, &rsp_iov); 564762306a36Sopenharmony_ci free_qfs_info_req(&iov); 564862306a36Sopenharmony_ci if (rc) { 564962306a36Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); 565062306a36Sopenharmony_ci goto qfsinf_exit; 565162306a36Sopenharmony_ci } 565262306a36Sopenharmony_ci rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; 565362306a36Sopenharmony_ci 565462306a36Sopenharmony_ci info = (struct smb2_fs_full_size_info *)( 565562306a36Sopenharmony_ci le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp); 565662306a36Sopenharmony_ci rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), 565762306a36Sopenharmony_ci le32_to_cpu(rsp->OutputBufferLength), &rsp_iov, 565862306a36Sopenharmony_ci sizeof(struct smb2_fs_full_size_info)); 565962306a36Sopenharmony_ci if (!rc) 566062306a36Sopenharmony_ci smb2_copy_fs_info_to_kstatfs(info, fsdata); 566162306a36Sopenharmony_ci 566262306a36Sopenharmony_ciqfsinf_exit: 566362306a36Sopenharmony_ci free_rsp_buf(resp_buftype, rsp_iov.iov_base); 566462306a36Sopenharmony_ci return rc; 566562306a36Sopenharmony_ci} 566662306a36Sopenharmony_ci 566762306a36Sopenharmony_ciint 566862306a36Sopenharmony_ciSMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, 566962306a36Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, int level) 567062306a36Sopenharmony_ci{ 567162306a36Sopenharmony_ci struct smb_rqst rqst; 567262306a36Sopenharmony_ci struct smb2_query_info_rsp *rsp = NULL; 567362306a36Sopenharmony_ci struct kvec iov; 567462306a36Sopenharmony_ci struct kvec rsp_iov; 567562306a36Sopenharmony_ci int rc = 0; 567662306a36Sopenharmony_ci int resp_buftype, max_len, min_len; 567762306a36Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 567862306a36Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(ses); 567962306a36Sopenharmony_ci unsigned int rsp_len, offset; 568062306a36Sopenharmony_ci int flags = 0; 568162306a36Sopenharmony_ci 568262306a36Sopenharmony_ci if (level == FS_DEVICE_INFORMATION) { 568362306a36Sopenharmony_ci max_len = sizeof(FILE_SYSTEM_DEVICE_INFO); 568462306a36Sopenharmony_ci min_len = sizeof(FILE_SYSTEM_DEVICE_INFO); 568562306a36Sopenharmony_ci } else if (level == FS_ATTRIBUTE_INFORMATION) { 568662306a36Sopenharmony_ci max_len = sizeof(FILE_SYSTEM_ATTRIBUTE_INFO); 568762306a36Sopenharmony_ci min_len = MIN_FS_ATTR_INFO_SIZE; 568862306a36Sopenharmony_ci } else if (level == FS_SECTOR_SIZE_INFORMATION) { 568962306a36Sopenharmony_ci max_len = sizeof(struct smb3_fs_ss_info); 569062306a36Sopenharmony_ci min_len = sizeof(struct smb3_fs_ss_info); 569162306a36Sopenharmony_ci } else if (level == FS_VOLUME_INFORMATION) { 569262306a36Sopenharmony_ci max_len = sizeof(struct smb3_fs_vol_info) + MAX_VOL_LABEL_LEN; 569362306a36Sopenharmony_ci min_len = sizeof(struct smb3_fs_vol_info); 569462306a36Sopenharmony_ci } else { 569562306a36Sopenharmony_ci cifs_dbg(FYI, "Invalid qfsinfo level %d\n", level); 569662306a36Sopenharmony_ci return -EINVAL; 569762306a36Sopenharmony_ci } 569862306a36Sopenharmony_ci 569962306a36Sopenharmony_ci rc = build_qfs_info_req(&iov, tcon, server, 570062306a36Sopenharmony_ci level, max_len, 570162306a36Sopenharmony_ci persistent_fid, volatile_fid); 570262306a36Sopenharmony_ci if (rc) 570362306a36Sopenharmony_ci return rc; 570462306a36Sopenharmony_ci 570562306a36Sopenharmony_ci if (smb3_encryption_required(tcon)) 570662306a36Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 570762306a36Sopenharmony_ci 570862306a36Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 570962306a36Sopenharmony_ci rqst.rq_iov = &iov; 571062306a36Sopenharmony_ci rqst.rq_nvec = 1; 571162306a36Sopenharmony_ci 571262306a36Sopenharmony_ci rc = cifs_send_recv(xid, ses, server, 571362306a36Sopenharmony_ci &rqst, &resp_buftype, flags, &rsp_iov); 571462306a36Sopenharmony_ci free_qfs_info_req(&iov); 571562306a36Sopenharmony_ci if (rc) { 571662306a36Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); 571762306a36Sopenharmony_ci goto qfsattr_exit; 571862306a36Sopenharmony_ci } 571962306a36Sopenharmony_ci rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; 572062306a36Sopenharmony_ci 572162306a36Sopenharmony_ci rsp_len = le32_to_cpu(rsp->OutputBufferLength); 572262306a36Sopenharmony_ci offset = le16_to_cpu(rsp->OutputBufferOffset); 572362306a36Sopenharmony_ci rc = smb2_validate_iov(offset, rsp_len, &rsp_iov, min_len); 572462306a36Sopenharmony_ci if (rc) 572562306a36Sopenharmony_ci goto qfsattr_exit; 572662306a36Sopenharmony_ci 572762306a36Sopenharmony_ci if (level == FS_ATTRIBUTE_INFORMATION) 572862306a36Sopenharmony_ci memcpy(&tcon->fsAttrInfo, offset 572962306a36Sopenharmony_ci + (char *)rsp, min_t(unsigned int, 573062306a36Sopenharmony_ci rsp_len, max_len)); 573162306a36Sopenharmony_ci else if (level == FS_DEVICE_INFORMATION) 573262306a36Sopenharmony_ci memcpy(&tcon->fsDevInfo, offset 573362306a36Sopenharmony_ci + (char *)rsp, sizeof(FILE_SYSTEM_DEVICE_INFO)); 573462306a36Sopenharmony_ci else if (level == FS_SECTOR_SIZE_INFORMATION) { 573562306a36Sopenharmony_ci struct smb3_fs_ss_info *ss_info = (struct smb3_fs_ss_info *) 573662306a36Sopenharmony_ci (offset + (char *)rsp); 573762306a36Sopenharmony_ci tcon->ss_flags = le32_to_cpu(ss_info->Flags); 573862306a36Sopenharmony_ci tcon->perf_sector_size = 573962306a36Sopenharmony_ci le32_to_cpu(ss_info->PhysicalBytesPerSectorForPerf); 574062306a36Sopenharmony_ci } else if (level == FS_VOLUME_INFORMATION) { 574162306a36Sopenharmony_ci struct smb3_fs_vol_info *vol_info = (struct smb3_fs_vol_info *) 574262306a36Sopenharmony_ci (offset + (char *)rsp); 574362306a36Sopenharmony_ci tcon->vol_serial_number = vol_info->VolumeSerialNumber; 574462306a36Sopenharmony_ci tcon->vol_create_time = vol_info->VolumeCreationTime; 574562306a36Sopenharmony_ci } 574662306a36Sopenharmony_ci 574762306a36Sopenharmony_ciqfsattr_exit: 574862306a36Sopenharmony_ci free_rsp_buf(resp_buftype, rsp_iov.iov_base); 574962306a36Sopenharmony_ci return rc; 575062306a36Sopenharmony_ci} 575162306a36Sopenharmony_ci 575262306a36Sopenharmony_ciint 575362306a36Sopenharmony_cismb2_lockv(const unsigned int xid, struct cifs_tcon *tcon, 575462306a36Sopenharmony_ci const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid, 575562306a36Sopenharmony_ci const __u32 num_lock, struct smb2_lock_element *buf) 575662306a36Sopenharmony_ci{ 575762306a36Sopenharmony_ci struct smb_rqst rqst; 575862306a36Sopenharmony_ci int rc = 0; 575962306a36Sopenharmony_ci struct smb2_lock_req *req = NULL; 576062306a36Sopenharmony_ci struct kvec iov[2]; 576162306a36Sopenharmony_ci struct kvec rsp_iov; 576262306a36Sopenharmony_ci int resp_buf_type; 576362306a36Sopenharmony_ci unsigned int count; 576462306a36Sopenharmony_ci int flags = CIFS_NO_RSP_BUF; 576562306a36Sopenharmony_ci unsigned int total_len; 576662306a36Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); 576762306a36Sopenharmony_ci 576862306a36Sopenharmony_ci cifs_dbg(FYI, "smb2_lockv num lock %d\n", num_lock); 576962306a36Sopenharmony_ci 577062306a36Sopenharmony_ci rc = smb2_plain_req_init(SMB2_LOCK, tcon, server, 577162306a36Sopenharmony_ci (void **) &req, &total_len); 577262306a36Sopenharmony_ci if (rc) 577362306a36Sopenharmony_ci return rc; 577462306a36Sopenharmony_ci 577562306a36Sopenharmony_ci if (smb3_encryption_required(tcon)) 577662306a36Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 577762306a36Sopenharmony_ci 577862306a36Sopenharmony_ci req->hdr.Id.SyncId.ProcessId = cpu_to_le32(pid); 577962306a36Sopenharmony_ci req->LockCount = cpu_to_le16(num_lock); 578062306a36Sopenharmony_ci 578162306a36Sopenharmony_ci req->PersistentFileId = persist_fid; 578262306a36Sopenharmony_ci req->VolatileFileId = volatile_fid; 578362306a36Sopenharmony_ci 578462306a36Sopenharmony_ci count = num_lock * sizeof(struct smb2_lock_element); 578562306a36Sopenharmony_ci 578662306a36Sopenharmony_ci iov[0].iov_base = (char *)req; 578762306a36Sopenharmony_ci iov[0].iov_len = total_len - sizeof(struct smb2_lock_element); 578862306a36Sopenharmony_ci iov[1].iov_base = (char *)buf; 578962306a36Sopenharmony_ci iov[1].iov_len = count; 579062306a36Sopenharmony_ci 579162306a36Sopenharmony_ci cifs_stats_inc(&tcon->stats.cifs_stats.num_locks); 579262306a36Sopenharmony_ci 579362306a36Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 579462306a36Sopenharmony_ci rqst.rq_iov = iov; 579562306a36Sopenharmony_ci rqst.rq_nvec = 2; 579662306a36Sopenharmony_ci 579762306a36Sopenharmony_ci rc = cifs_send_recv(xid, tcon->ses, server, 579862306a36Sopenharmony_ci &rqst, &resp_buf_type, flags, 579962306a36Sopenharmony_ci &rsp_iov); 580062306a36Sopenharmony_ci cifs_small_buf_release(req); 580162306a36Sopenharmony_ci if (rc) { 580262306a36Sopenharmony_ci cifs_dbg(FYI, "Send error in smb2_lockv = %d\n", rc); 580362306a36Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_LOCK_HE); 580462306a36Sopenharmony_ci trace_smb3_lock_err(xid, persist_fid, tcon->tid, 580562306a36Sopenharmony_ci tcon->ses->Suid, rc); 580662306a36Sopenharmony_ci } 580762306a36Sopenharmony_ci 580862306a36Sopenharmony_ci return rc; 580962306a36Sopenharmony_ci} 581062306a36Sopenharmony_ci 581162306a36Sopenharmony_ciint 581262306a36Sopenharmony_ciSMB2_lock(const unsigned int xid, struct cifs_tcon *tcon, 581362306a36Sopenharmony_ci const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid, 581462306a36Sopenharmony_ci const __u64 length, const __u64 offset, const __u32 lock_flags, 581562306a36Sopenharmony_ci const bool wait) 581662306a36Sopenharmony_ci{ 581762306a36Sopenharmony_ci struct smb2_lock_element lock; 581862306a36Sopenharmony_ci 581962306a36Sopenharmony_ci lock.Offset = cpu_to_le64(offset); 582062306a36Sopenharmony_ci lock.Length = cpu_to_le64(length); 582162306a36Sopenharmony_ci lock.Flags = cpu_to_le32(lock_flags); 582262306a36Sopenharmony_ci if (!wait && lock_flags != SMB2_LOCKFLAG_UNLOCK) 582362306a36Sopenharmony_ci lock.Flags |= cpu_to_le32(SMB2_LOCKFLAG_FAIL_IMMEDIATELY); 582462306a36Sopenharmony_ci 582562306a36Sopenharmony_ci return smb2_lockv(xid, tcon, persist_fid, volatile_fid, pid, 1, &lock); 582662306a36Sopenharmony_ci} 582762306a36Sopenharmony_ci 582862306a36Sopenharmony_ciint 582962306a36Sopenharmony_ciSMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon, 583062306a36Sopenharmony_ci __u8 *lease_key, const __le32 lease_state) 583162306a36Sopenharmony_ci{ 583262306a36Sopenharmony_ci struct smb_rqst rqst; 583362306a36Sopenharmony_ci int rc; 583462306a36Sopenharmony_ci struct smb2_lease_ack *req = NULL; 583562306a36Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 583662306a36Sopenharmony_ci int flags = CIFS_OBREAK_OP; 583762306a36Sopenharmony_ci unsigned int total_len; 583862306a36Sopenharmony_ci struct kvec iov[1]; 583962306a36Sopenharmony_ci struct kvec rsp_iov; 584062306a36Sopenharmony_ci int resp_buf_type; 584162306a36Sopenharmony_ci __u64 *please_key_high; 584262306a36Sopenharmony_ci __u64 *please_key_low; 584362306a36Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); 584462306a36Sopenharmony_ci 584562306a36Sopenharmony_ci cifs_dbg(FYI, "SMB2_lease_break\n"); 584662306a36Sopenharmony_ci rc = smb2_plain_req_init(SMB2_OPLOCK_BREAK, tcon, server, 584762306a36Sopenharmony_ci (void **) &req, &total_len); 584862306a36Sopenharmony_ci if (rc) 584962306a36Sopenharmony_ci return rc; 585062306a36Sopenharmony_ci 585162306a36Sopenharmony_ci if (smb3_encryption_required(tcon)) 585262306a36Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 585362306a36Sopenharmony_ci 585462306a36Sopenharmony_ci req->hdr.CreditRequest = cpu_to_le16(1); 585562306a36Sopenharmony_ci req->StructureSize = cpu_to_le16(36); 585662306a36Sopenharmony_ci total_len += 12; 585762306a36Sopenharmony_ci 585862306a36Sopenharmony_ci memcpy(req->LeaseKey, lease_key, 16); 585962306a36Sopenharmony_ci req->LeaseState = lease_state; 586062306a36Sopenharmony_ci 586162306a36Sopenharmony_ci flags |= CIFS_NO_RSP_BUF; 586262306a36Sopenharmony_ci 586362306a36Sopenharmony_ci iov[0].iov_base = (char *)req; 586462306a36Sopenharmony_ci iov[0].iov_len = total_len; 586562306a36Sopenharmony_ci 586662306a36Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 586762306a36Sopenharmony_ci rqst.rq_iov = iov; 586862306a36Sopenharmony_ci rqst.rq_nvec = 1; 586962306a36Sopenharmony_ci 587062306a36Sopenharmony_ci rc = cifs_send_recv(xid, ses, server, 587162306a36Sopenharmony_ci &rqst, &resp_buf_type, flags, &rsp_iov); 587262306a36Sopenharmony_ci cifs_small_buf_release(req); 587362306a36Sopenharmony_ci 587462306a36Sopenharmony_ci please_key_low = (__u64 *)lease_key; 587562306a36Sopenharmony_ci please_key_high = (__u64 *)(lease_key+8); 587662306a36Sopenharmony_ci if (rc) { 587762306a36Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE); 587862306a36Sopenharmony_ci trace_smb3_lease_err(le32_to_cpu(lease_state), tcon->tid, 587962306a36Sopenharmony_ci ses->Suid, *please_key_low, *please_key_high, rc); 588062306a36Sopenharmony_ci cifs_dbg(FYI, "Send error in Lease Break = %d\n", rc); 588162306a36Sopenharmony_ci } else 588262306a36Sopenharmony_ci trace_smb3_lease_done(le32_to_cpu(lease_state), tcon->tid, 588362306a36Sopenharmony_ci ses->Suid, *please_key_low, *please_key_high); 588462306a36Sopenharmony_ci 588562306a36Sopenharmony_ci return rc; 588662306a36Sopenharmony_ci} 5887