18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * fs/cifs/smb2pdu.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) International Business Machines Corp., 2009, 2013 58c2ecf20Sopenharmony_ci * Etersoft, 2012 68c2ecf20Sopenharmony_ci * Author(s): Steve French (sfrench@us.ibm.com) 78c2ecf20Sopenharmony_ci * Pavel Shilovsky (pshilovsky@samba.org) 2012 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Contains the routines for constructing the SMB2 PDUs themselves 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * This library is free software; you can redistribute it and/or modify 128c2ecf20Sopenharmony_ci * it under the terms of the GNU Lesser General Public License as published 138c2ecf20Sopenharmony_ci * by the Free Software Foundation; either version 2.1 of the License, or 148c2ecf20Sopenharmony_ci * (at your option) any later version. 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * This library is distributed in the hope that it will be useful, 178c2ecf20Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 188c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 198c2ecf20Sopenharmony_ci * the GNU Lesser General Public License for more details. 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * You should have received a copy of the GNU Lesser General Public License 228c2ecf20Sopenharmony_ci * along with this library; if not, write to the Free Software 238c2ecf20Sopenharmony_ci * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci /* SMB2 PDU handling routines here - except for leftovers (eg session setup) */ 278c2ecf20Sopenharmony_ci /* Note that there are handle based routines which must be */ 288c2ecf20Sopenharmony_ci /* treated slightly differently for reconnection purposes since we never */ 298c2ecf20Sopenharmony_ci /* want to reuse a stale file handle and only the caller knows the file info */ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include <linux/fs.h> 328c2ecf20Sopenharmony_ci#include <linux/kernel.h> 338c2ecf20Sopenharmony_ci#include <linux/vfs.h> 348c2ecf20Sopenharmony_ci#include <linux/task_io_accounting_ops.h> 358c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 368c2ecf20Sopenharmony_ci#include <linux/uuid.h> 378c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 388c2ecf20Sopenharmony_ci#include <linux/xattr.h> 398c2ecf20Sopenharmony_ci#include "smb2pdu.h" 408c2ecf20Sopenharmony_ci#include "cifsglob.h" 418c2ecf20Sopenharmony_ci#include "cifsacl.h" 428c2ecf20Sopenharmony_ci#include "cifsproto.h" 438c2ecf20Sopenharmony_ci#include "smb2proto.h" 448c2ecf20Sopenharmony_ci#include "cifs_unicode.h" 458c2ecf20Sopenharmony_ci#include "cifs_debug.h" 468c2ecf20Sopenharmony_ci#include "ntlmssp.h" 478c2ecf20Sopenharmony_ci#include "smb2status.h" 488c2ecf20Sopenharmony_ci#include "smb2glob.h" 498c2ecf20Sopenharmony_ci#include "cifspdu.h" 508c2ecf20Sopenharmony_ci#include "cifs_spnego.h" 518c2ecf20Sopenharmony_ci#include "smbdirect.h" 528c2ecf20Sopenharmony_ci#include "trace.h" 538c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_DFS_UPCALL 548c2ecf20Sopenharmony_ci#include "dfs_cache.h" 558c2ecf20Sopenharmony_ci#endif 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/* 588c2ecf20Sopenharmony_ci * The following table defines the expected "StructureSize" of SMB2 requests 598c2ecf20Sopenharmony_ci * in order by SMB2 command. This is similar to "wct" in SMB/CIFS requests. 608c2ecf20Sopenharmony_ci * 618c2ecf20Sopenharmony_ci * Note that commands are defined in smb2pdu.h in le16 but the array below is 628c2ecf20Sopenharmony_ci * indexed by command in host byte order. 638c2ecf20Sopenharmony_ci */ 648c2ecf20Sopenharmony_cistatic const int smb2_req_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = { 658c2ecf20Sopenharmony_ci /* SMB2_NEGOTIATE */ 36, 668c2ecf20Sopenharmony_ci /* SMB2_SESSION_SETUP */ 25, 678c2ecf20Sopenharmony_ci /* SMB2_LOGOFF */ 4, 688c2ecf20Sopenharmony_ci /* SMB2_TREE_CONNECT */ 9, 698c2ecf20Sopenharmony_ci /* SMB2_TREE_DISCONNECT */ 4, 708c2ecf20Sopenharmony_ci /* SMB2_CREATE */ 57, 718c2ecf20Sopenharmony_ci /* SMB2_CLOSE */ 24, 728c2ecf20Sopenharmony_ci /* SMB2_FLUSH */ 24, 738c2ecf20Sopenharmony_ci /* SMB2_READ */ 49, 748c2ecf20Sopenharmony_ci /* SMB2_WRITE */ 49, 758c2ecf20Sopenharmony_ci /* SMB2_LOCK */ 48, 768c2ecf20Sopenharmony_ci /* SMB2_IOCTL */ 57, 778c2ecf20Sopenharmony_ci /* SMB2_CANCEL */ 4, 788c2ecf20Sopenharmony_ci /* SMB2_ECHO */ 4, 798c2ecf20Sopenharmony_ci /* SMB2_QUERY_DIRECTORY */ 33, 808c2ecf20Sopenharmony_ci /* SMB2_CHANGE_NOTIFY */ 32, 818c2ecf20Sopenharmony_ci /* SMB2_QUERY_INFO */ 41, 828c2ecf20Sopenharmony_ci /* SMB2_SET_INFO */ 33, 838c2ecf20Sopenharmony_ci /* SMB2_OPLOCK_BREAK */ 24 /* BB this is 36 for LEASE_BREAK variant */ 848c2ecf20Sopenharmony_ci}; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ciint smb3_encryption_required(const struct cifs_tcon *tcon) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci if (!tcon || !tcon->ses) 898c2ecf20Sopenharmony_ci return 0; 908c2ecf20Sopenharmony_ci if ((tcon->ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) || 918c2ecf20Sopenharmony_ci (tcon->share_flags & SHI1005_FLAGS_ENCRYPT_DATA)) 928c2ecf20Sopenharmony_ci return 1; 938c2ecf20Sopenharmony_ci if (tcon->seal && 948c2ecf20Sopenharmony_ci (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) 958c2ecf20Sopenharmony_ci return 1; 968c2ecf20Sopenharmony_ci return 0; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic void 1008c2ecf20Sopenharmony_cismb2_hdr_assemble(struct smb2_sync_hdr *shdr, __le16 smb2_cmd, 1018c2ecf20Sopenharmony_ci const struct cifs_tcon *tcon, 1028c2ecf20Sopenharmony_ci struct TCP_Server_Info *server) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci shdr->ProtocolId = SMB2_PROTO_NUMBER; 1058c2ecf20Sopenharmony_ci shdr->StructureSize = cpu_to_le16(64); 1068c2ecf20Sopenharmony_ci shdr->Command = smb2_cmd; 1078c2ecf20Sopenharmony_ci if (server) { 1088c2ecf20Sopenharmony_ci spin_lock(&server->req_lock); 1098c2ecf20Sopenharmony_ci /* Request up to 10 credits but don't go over the limit. */ 1108c2ecf20Sopenharmony_ci if (server->credits >= server->max_credits) 1118c2ecf20Sopenharmony_ci shdr->CreditRequest = cpu_to_le16(0); 1128c2ecf20Sopenharmony_ci else 1138c2ecf20Sopenharmony_ci shdr->CreditRequest = cpu_to_le16( 1148c2ecf20Sopenharmony_ci min_t(int, server->max_credits - 1158c2ecf20Sopenharmony_ci server->credits, 10)); 1168c2ecf20Sopenharmony_ci spin_unlock(&server->req_lock); 1178c2ecf20Sopenharmony_ci } else { 1188c2ecf20Sopenharmony_ci shdr->CreditRequest = cpu_to_le16(2); 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci shdr->ProcessId = cpu_to_le32((__u16)current->tgid); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if (!tcon) 1238c2ecf20Sopenharmony_ci goto out; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* GLOBAL_CAP_LARGE_MTU will only be set if dialect > SMB2.02 */ 1268c2ecf20Sopenharmony_ci /* See sections 2.2.4 and 3.2.4.1.5 of MS-SMB2 */ 1278c2ecf20Sopenharmony_ci if (server && (server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) 1288c2ecf20Sopenharmony_ci shdr->CreditCharge = cpu_to_le16(1); 1298c2ecf20Sopenharmony_ci /* else CreditCharge MBZ */ 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci shdr->TreeId = tcon->tid; 1328c2ecf20Sopenharmony_ci /* Uid is not converted */ 1338c2ecf20Sopenharmony_ci if (tcon->ses) 1348c2ecf20Sopenharmony_ci shdr->SessionId = tcon->ses->Suid; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci /* 1378c2ecf20Sopenharmony_ci * If we would set SMB2_FLAGS_DFS_OPERATIONS on open we also would have 1388c2ecf20Sopenharmony_ci * to pass the path on the Open SMB prefixed by \\server\share. 1398c2ecf20Sopenharmony_ci * Not sure when we would need to do the augmented path (if ever) and 1408c2ecf20Sopenharmony_ci * setting this flag breaks the SMB2 open operation since it is 1418c2ecf20Sopenharmony_ci * illegal to send an empty path name (without \\server\share prefix) 1428c2ecf20Sopenharmony_ci * when the DFS flag is set in the SMB open header. We could 1438c2ecf20Sopenharmony_ci * consider setting the flag on all operations other than open 1448c2ecf20Sopenharmony_ci * but it is safer to net set it for now. 1458c2ecf20Sopenharmony_ci */ 1468c2ecf20Sopenharmony_ci/* if (tcon->share_flags & SHI1005_FLAGS_DFS) 1478c2ecf20Sopenharmony_ci shdr->Flags |= SMB2_FLAGS_DFS_OPERATIONS; */ 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (server && server->sign && !smb3_encryption_required(tcon)) 1508c2ecf20Sopenharmony_ci shdr->Flags |= SMB2_FLAGS_SIGNED; 1518c2ecf20Sopenharmony_ciout: 1528c2ecf20Sopenharmony_ci return; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic int 1568c2ecf20Sopenharmony_cismb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, 1578c2ecf20Sopenharmony_ci struct TCP_Server_Info *server) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci int rc; 1608c2ecf20Sopenharmony_ci struct nls_table *nls_codepage; 1618c2ecf20Sopenharmony_ci struct cifs_ses *ses; 1628c2ecf20Sopenharmony_ci int retries; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci /* 1658c2ecf20Sopenharmony_ci * SMB2s NegProt, SessSetup, Logoff do not have tcon yet so 1668c2ecf20Sopenharmony_ci * check for tcp and smb session status done differently 1678c2ecf20Sopenharmony_ci * for those three - in the calling routine. 1688c2ecf20Sopenharmony_ci */ 1698c2ecf20Sopenharmony_ci if (tcon == NULL) 1708c2ecf20Sopenharmony_ci return 0; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (smb2_command == SMB2_TREE_CONNECT) 1738c2ecf20Sopenharmony_ci return 0; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (tcon->tidStatus == CifsExiting) { 1768c2ecf20Sopenharmony_ci /* 1778c2ecf20Sopenharmony_ci * only tree disconnect, open, and write, 1788c2ecf20Sopenharmony_ci * (and ulogoff which does not have tcon) 1798c2ecf20Sopenharmony_ci * are allowed as we start force umount. 1808c2ecf20Sopenharmony_ci */ 1818c2ecf20Sopenharmony_ci if ((smb2_command != SMB2_WRITE) && 1828c2ecf20Sopenharmony_ci (smb2_command != SMB2_CREATE) && 1838c2ecf20Sopenharmony_ci (smb2_command != SMB2_TREE_DISCONNECT)) { 1848c2ecf20Sopenharmony_ci cifs_dbg(FYI, "can not send cmd %d while umounting\n", 1858c2ecf20Sopenharmony_ci smb2_command); 1868c2ecf20Sopenharmony_ci return -ENODEV; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci if ((!tcon->ses) || (tcon->ses->status == CifsExiting) || 1908c2ecf20Sopenharmony_ci (!tcon->ses->server) || !server) 1918c2ecf20Sopenharmony_ci return -EIO; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci ses = tcon->ses; 1948c2ecf20Sopenharmony_ci retries = server->nr_targets; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci /* 1978c2ecf20Sopenharmony_ci * Give demultiplex thread up to 10 seconds to each target available for 1988c2ecf20Sopenharmony_ci * reconnect -- should be greater than cifs socket timeout which is 7 1998c2ecf20Sopenharmony_ci * seconds. 2008c2ecf20Sopenharmony_ci */ 2018c2ecf20Sopenharmony_ci while (server->tcpStatus == CifsNeedReconnect) { 2028c2ecf20Sopenharmony_ci /* 2038c2ecf20Sopenharmony_ci * Return to caller for TREE_DISCONNECT and LOGOFF and CLOSE 2048c2ecf20Sopenharmony_ci * here since they are implicitly done when session drops. 2058c2ecf20Sopenharmony_ci */ 2068c2ecf20Sopenharmony_ci switch (smb2_command) { 2078c2ecf20Sopenharmony_ci /* 2088c2ecf20Sopenharmony_ci * BB Should we keep oplock break and add flush to exceptions? 2098c2ecf20Sopenharmony_ci */ 2108c2ecf20Sopenharmony_ci case SMB2_TREE_DISCONNECT: 2118c2ecf20Sopenharmony_ci case SMB2_CANCEL: 2128c2ecf20Sopenharmony_ci case SMB2_CLOSE: 2138c2ecf20Sopenharmony_ci case SMB2_OPLOCK_BREAK: 2148c2ecf20Sopenharmony_ci return -EAGAIN; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci rc = wait_event_interruptible_timeout(server->response_q, 2188c2ecf20Sopenharmony_ci (server->tcpStatus != CifsNeedReconnect), 2198c2ecf20Sopenharmony_ci 10 * HZ); 2208c2ecf20Sopenharmony_ci if (rc < 0) { 2218c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: aborting reconnect due to a received signal by the process\n", 2228c2ecf20Sopenharmony_ci __func__); 2238c2ecf20Sopenharmony_ci return -ERESTARTSYS; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci /* are we still trying to reconnect? */ 2278c2ecf20Sopenharmony_ci if (server->tcpStatus != CifsNeedReconnect) 2288c2ecf20Sopenharmony_ci break; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (retries && --retries) 2318c2ecf20Sopenharmony_ci continue; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci /* 2348c2ecf20Sopenharmony_ci * on "soft" mounts we wait once. Hard mounts keep 2358c2ecf20Sopenharmony_ci * retrying until process is killed or server comes 2368c2ecf20Sopenharmony_ci * back on-line 2378c2ecf20Sopenharmony_ci */ 2388c2ecf20Sopenharmony_ci if (!tcon->retry) { 2398c2ecf20Sopenharmony_ci cifs_dbg(FYI, "gave up waiting on reconnect in smb_init\n"); 2408c2ecf20Sopenharmony_ci return -EHOSTDOWN; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci retries = server->nr_targets; 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci if (!tcon->ses->need_reconnect && !tcon->need_reconnect) 2468c2ecf20Sopenharmony_ci return 0; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci nls_codepage = load_nls_default(); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci /* 2518c2ecf20Sopenharmony_ci * need to prevent multiple threads trying to simultaneously reconnect 2528c2ecf20Sopenharmony_ci * the same SMB session 2538c2ecf20Sopenharmony_ci */ 2548c2ecf20Sopenharmony_ci mutex_lock(&tcon->ses->session_mutex); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci /* 2578c2ecf20Sopenharmony_ci * Recheck after acquire mutex. If another thread is negotiating 2588c2ecf20Sopenharmony_ci * and the server never sends an answer the socket will be closed 2598c2ecf20Sopenharmony_ci * and tcpStatus set to reconnect. 2608c2ecf20Sopenharmony_ci */ 2618c2ecf20Sopenharmony_ci if (server->tcpStatus == CifsNeedReconnect) { 2628c2ecf20Sopenharmony_ci rc = -EHOSTDOWN; 2638c2ecf20Sopenharmony_ci mutex_unlock(&tcon->ses->session_mutex); 2648c2ecf20Sopenharmony_ci goto out; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci /* 2688c2ecf20Sopenharmony_ci * If we are reconnecting an extra channel, bind 2698c2ecf20Sopenharmony_ci */ 2708c2ecf20Sopenharmony_ci if (server->is_channel) { 2718c2ecf20Sopenharmony_ci ses->binding = true; 2728c2ecf20Sopenharmony_ci ses->binding_chan = cifs_ses_find_chan(ses, server); 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci rc = cifs_negotiate_protocol(0, tcon->ses); 2768c2ecf20Sopenharmony_ci if (!rc && tcon->ses->need_reconnect) { 2778c2ecf20Sopenharmony_ci rc = cifs_setup_session(0, tcon->ses, nls_codepage); 2788c2ecf20Sopenharmony_ci if ((rc == -EACCES) && !tcon->retry) { 2798c2ecf20Sopenharmony_ci rc = -EHOSTDOWN; 2808c2ecf20Sopenharmony_ci ses->binding = false; 2818c2ecf20Sopenharmony_ci ses->binding_chan = NULL; 2828c2ecf20Sopenharmony_ci mutex_unlock(&tcon->ses->session_mutex); 2838c2ecf20Sopenharmony_ci goto failed; 2848c2ecf20Sopenharmony_ci } else if (rc) { 2858c2ecf20Sopenharmony_ci mutex_unlock(&ses->session_mutex); 2868c2ecf20Sopenharmony_ci goto out; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci /* 2908c2ecf20Sopenharmony_ci * End of channel binding 2918c2ecf20Sopenharmony_ci */ 2928c2ecf20Sopenharmony_ci ses->binding = false; 2938c2ecf20Sopenharmony_ci ses->binding_chan = NULL; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci if (rc || !tcon->need_reconnect) { 2968c2ecf20Sopenharmony_ci mutex_unlock(&tcon->ses->session_mutex); 2978c2ecf20Sopenharmony_ci goto out; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci cifs_mark_open_files_invalid(tcon); 3018c2ecf20Sopenharmony_ci if (tcon->use_persistent) 3028c2ecf20Sopenharmony_ci tcon->need_reopen_files = true; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci rc = cifs_tree_connect(0, tcon, nls_codepage); 3058c2ecf20Sopenharmony_ci mutex_unlock(&tcon->ses->session_mutex); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc); 3088c2ecf20Sopenharmony_ci if (rc) { 3098c2ecf20Sopenharmony_ci /* If sess reconnected but tcon didn't, something strange ... */ 3108c2ecf20Sopenharmony_ci pr_warn_once("reconnect tcon failed rc = %d\n", rc); 3118c2ecf20Sopenharmony_ci goto out; 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci if (smb2_command != SMB2_INTERNAL_CMD) 3158c2ecf20Sopenharmony_ci mod_delayed_work(cifsiod_wq, &server->reconnect, 0); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci atomic_inc(&tconInfoReconnectCount); 3188c2ecf20Sopenharmony_ciout: 3198c2ecf20Sopenharmony_ci /* 3208c2ecf20Sopenharmony_ci * Check if handle based operation so we know whether we can continue 3218c2ecf20Sopenharmony_ci * or not without returning to caller to reset file handle. 3228c2ecf20Sopenharmony_ci */ 3238c2ecf20Sopenharmony_ci /* 3248c2ecf20Sopenharmony_ci * BB Is flush done by server on drop of tcp session? Should we special 3258c2ecf20Sopenharmony_ci * case it and skip above? 3268c2ecf20Sopenharmony_ci */ 3278c2ecf20Sopenharmony_ci switch (smb2_command) { 3288c2ecf20Sopenharmony_ci case SMB2_FLUSH: 3298c2ecf20Sopenharmony_ci case SMB2_READ: 3308c2ecf20Sopenharmony_ci case SMB2_WRITE: 3318c2ecf20Sopenharmony_ci case SMB2_LOCK: 3328c2ecf20Sopenharmony_ci case SMB2_IOCTL: 3338c2ecf20Sopenharmony_ci case SMB2_QUERY_DIRECTORY: 3348c2ecf20Sopenharmony_ci case SMB2_CHANGE_NOTIFY: 3358c2ecf20Sopenharmony_ci case SMB2_QUERY_INFO: 3368c2ecf20Sopenharmony_ci case SMB2_SET_INFO: 3378c2ecf20Sopenharmony_ci rc = -EAGAIN; 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_cifailed: 3408c2ecf20Sopenharmony_ci unload_nls(nls_codepage); 3418c2ecf20Sopenharmony_ci return rc; 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic void 3458c2ecf20Sopenharmony_cifill_small_buf(__le16 smb2_command, struct cifs_tcon *tcon, 3468c2ecf20Sopenharmony_ci struct TCP_Server_Info *server, 3478c2ecf20Sopenharmony_ci void *buf, 3488c2ecf20Sopenharmony_ci unsigned int *total_len) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci struct smb2_sync_pdu *spdu = (struct smb2_sync_pdu *)buf; 3518c2ecf20Sopenharmony_ci /* lookup word count ie StructureSize from table */ 3528c2ecf20Sopenharmony_ci __u16 parmsize = smb2_req_struct_sizes[le16_to_cpu(smb2_command)]; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci /* 3558c2ecf20Sopenharmony_ci * smaller than SMALL_BUFFER_SIZE but bigger than fixed area of 3568c2ecf20Sopenharmony_ci * largest operations (Create) 3578c2ecf20Sopenharmony_ci */ 3588c2ecf20Sopenharmony_ci memset(buf, 0, 256); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci smb2_hdr_assemble(&spdu->sync_hdr, smb2_command, tcon, server); 3618c2ecf20Sopenharmony_ci spdu->StructureSize2 = cpu_to_le16(parmsize); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci *total_len = parmsize + sizeof(struct smb2_sync_hdr); 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci/* 3678c2ecf20Sopenharmony_ci * Allocate and return pointer to an SMB request hdr, and set basic 3688c2ecf20Sopenharmony_ci * SMB information in the SMB header. If the return code is zero, this 3698c2ecf20Sopenharmony_ci * function must have filled in request_buf pointer. 3708c2ecf20Sopenharmony_ci */ 3718c2ecf20Sopenharmony_cistatic int __smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon, 3728c2ecf20Sopenharmony_ci struct TCP_Server_Info *server, 3738c2ecf20Sopenharmony_ci void **request_buf, unsigned int *total_len) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci /* BB eventually switch this to SMB2 specific small buf size */ 3768c2ecf20Sopenharmony_ci switch (smb2_command) { 3778c2ecf20Sopenharmony_ci case SMB2_SET_INFO: 3788c2ecf20Sopenharmony_ci case SMB2_QUERY_INFO: 3798c2ecf20Sopenharmony_ci *request_buf = cifs_buf_get(); 3808c2ecf20Sopenharmony_ci break; 3818c2ecf20Sopenharmony_ci default: 3828c2ecf20Sopenharmony_ci *request_buf = cifs_small_buf_get(); 3838c2ecf20Sopenharmony_ci break; 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci if (*request_buf == NULL) { 3868c2ecf20Sopenharmony_ci /* BB should we add a retry in here if not a writepage? */ 3878c2ecf20Sopenharmony_ci return -ENOMEM; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci fill_small_buf(smb2_command, tcon, server, 3918c2ecf20Sopenharmony_ci (struct smb2_sync_hdr *)(*request_buf), 3928c2ecf20Sopenharmony_ci total_len); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci if (tcon != NULL) { 3958c2ecf20Sopenharmony_ci uint16_t com_code = le16_to_cpu(smb2_command); 3968c2ecf20Sopenharmony_ci cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_sent[com_code]); 3978c2ecf20Sopenharmony_ci cifs_stats_inc(&tcon->num_smbs_sent); 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci return 0; 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cistatic int smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon, 4048c2ecf20Sopenharmony_ci struct TCP_Server_Info *server, 4058c2ecf20Sopenharmony_ci void **request_buf, unsigned int *total_len) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci int rc; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci rc = smb2_reconnect(smb2_command, tcon, server); 4108c2ecf20Sopenharmony_ci if (rc) 4118c2ecf20Sopenharmony_ci return rc; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci return __smb2_plain_req_init(smb2_command, tcon, server, request_buf, 4148c2ecf20Sopenharmony_ci total_len); 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistatic int smb2_ioctl_req_init(u32 opcode, struct cifs_tcon *tcon, 4188c2ecf20Sopenharmony_ci struct TCP_Server_Info *server, 4198c2ecf20Sopenharmony_ci void **request_buf, unsigned int *total_len) 4208c2ecf20Sopenharmony_ci{ 4218c2ecf20Sopenharmony_ci /* Skip reconnect only for FSCTL_VALIDATE_NEGOTIATE_INFO IOCTLs */ 4228c2ecf20Sopenharmony_ci if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO) { 4238c2ecf20Sopenharmony_ci return __smb2_plain_req_init(SMB2_IOCTL, tcon, server, 4248c2ecf20Sopenharmony_ci request_buf, total_len); 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci return smb2_plain_req_init(SMB2_IOCTL, tcon, server, 4278c2ecf20Sopenharmony_ci request_buf, total_len); 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci/* For explanation of negotiate contexts see MS-SMB2 section 2.2.3.1 */ 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic void 4338c2ecf20Sopenharmony_cibuild_preauth_ctxt(struct smb2_preauth_neg_context *pneg_ctxt) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci pneg_ctxt->ContextType = SMB2_PREAUTH_INTEGRITY_CAPABILITIES; 4368c2ecf20Sopenharmony_ci pneg_ctxt->DataLength = cpu_to_le16(38); 4378c2ecf20Sopenharmony_ci pneg_ctxt->HashAlgorithmCount = cpu_to_le16(1); 4388c2ecf20Sopenharmony_ci pneg_ctxt->SaltLength = cpu_to_le16(SMB311_LINUX_CLIENT_SALT_SIZE); 4398c2ecf20Sopenharmony_ci get_random_bytes(pneg_ctxt->Salt, SMB311_LINUX_CLIENT_SALT_SIZE); 4408c2ecf20Sopenharmony_ci pneg_ctxt->HashAlgorithms = SMB2_PREAUTH_INTEGRITY_SHA512; 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cistatic void 4448c2ecf20Sopenharmony_cibuild_compression_ctxt(struct smb2_compression_capabilities_context *pneg_ctxt) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci pneg_ctxt->ContextType = SMB2_COMPRESSION_CAPABILITIES; 4478c2ecf20Sopenharmony_ci pneg_ctxt->DataLength = 4488c2ecf20Sopenharmony_ci cpu_to_le16(sizeof(struct smb2_compression_capabilities_context) 4498c2ecf20Sopenharmony_ci - sizeof(struct smb2_neg_context)); 4508c2ecf20Sopenharmony_ci pneg_ctxt->CompressionAlgorithmCount = cpu_to_le16(3); 4518c2ecf20Sopenharmony_ci pneg_ctxt->CompressionAlgorithms[0] = SMB3_COMPRESS_LZ77; 4528c2ecf20Sopenharmony_ci pneg_ctxt->CompressionAlgorithms[1] = SMB3_COMPRESS_LZ77_HUFF; 4538c2ecf20Sopenharmony_ci pneg_ctxt->CompressionAlgorithms[2] = SMB3_COMPRESS_LZNT1; 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic void 4578c2ecf20Sopenharmony_cibuild_encrypt_ctxt(struct smb2_encryption_neg_context *pneg_ctxt) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci pneg_ctxt->ContextType = SMB2_ENCRYPTION_CAPABILITIES; 4608c2ecf20Sopenharmony_ci if (require_gcm_256) { 4618c2ecf20Sopenharmony_ci pneg_ctxt->DataLength = cpu_to_le16(4); /* Cipher Count + 1 cipher */ 4628c2ecf20Sopenharmony_ci pneg_ctxt->CipherCount = cpu_to_le16(1); 4638c2ecf20Sopenharmony_ci pneg_ctxt->Ciphers[0] = SMB2_ENCRYPTION_AES256_GCM; 4648c2ecf20Sopenharmony_ci } else if (enable_gcm_256) { 4658c2ecf20Sopenharmony_ci pneg_ctxt->DataLength = cpu_to_le16(8); /* Cipher Count + 3 ciphers */ 4668c2ecf20Sopenharmony_ci pneg_ctxt->CipherCount = cpu_to_le16(3); 4678c2ecf20Sopenharmony_ci pneg_ctxt->Ciphers[0] = SMB2_ENCRYPTION_AES128_GCM; 4688c2ecf20Sopenharmony_ci pneg_ctxt->Ciphers[1] = SMB2_ENCRYPTION_AES256_GCM; 4698c2ecf20Sopenharmony_ci pneg_ctxt->Ciphers[2] = SMB2_ENCRYPTION_AES128_CCM; 4708c2ecf20Sopenharmony_ci } else { 4718c2ecf20Sopenharmony_ci pneg_ctxt->DataLength = cpu_to_le16(6); /* Cipher Count + 2 ciphers */ 4728c2ecf20Sopenharmony_ci pneg_ctxt->CipherCount = cpu_to_le16(2); 4738c2ecf20Sopenharmony_ci pneg_ctxt->Ciphers[0] = SMB2_ENCRYPTION_AES128_GCM; 4748c2ecf20Sopenharmony_ci pneg_ctxt->Ciphers[1] = SMB2_ENCRYPTION_AES128_CCM; 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci} 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_cistatic unsigned int 4798c2ecf20Sopenharmony_cibuild_netname_ctxt(struct smb2_netname_neg_context *pneg_ctxt, char *hostname) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci struct nls_table *cp = load_nls_default(); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci pneg_ctxt->ContextType = SMB2_NETNAME_NEGOTIATE_CONTEXT_ID; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci /* copy up to max of first 100 bytes of server name to NetName field */ 4868c2ecf20Sopenharmony_ci pneg_ctxt->DataLength = cpu_to_le16(2 * cifs_strtoUTF16(pneg_ctxt->NetName, hostname, 100, cp)); 4878c2ecf20Sopenharmony_ci /* context size is DataLength + minimal smb2_neg_context */ 4888c2ecf20Sopenharmony_ci return DIV_ROUND_UP(le16_to_cpu(pneg_ctxt->DataLength) + 4898c2ecf20Sopenharmony_ci sizeof(struct smb2_neg_context), 8) * 8; 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_cistatic void 4938c2ecf20Sopenharmony_cibuild_posix_ctxt(struct smb2_posix_neg_context *pneg_ctxt) 4948c2ecf20Sopenharmony_ci{ 4958c2ecf20Sopenharmony_ci pneg_ctxt->ContextType = SMB2_POSIX_EXTENSIONS_AVAILABLE; 4968c2ecf20Sopenharmony_ci pneg_ctxt->DataLength = cpu_to_le16(POSIX_CTXT_DATA_LEN); 4978c2ecf20Sopenharmony_ci /* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */ 4988c2ecf20Sopenharmony_ci pneg_ctxt->Name[0] = 0x93; 4998c2ecf20Sopenharmony_ci pneg_ctxt->Name[1] = 0xAD; 5008c2ecf20Sopenharmony_ci pneg_ctxt->Name[2] = 0x25; 5018c2ecf20Sopenharmony_ci pneg_ctxt->Name[3] = 0x50; 5028c2ecf20Sopenharmony_ci pneg_ctxt->Name[4] = 0x9C; 5038c2ecf20Sopenharmony_ci pneg_ctxt->Name[5] = 0xB4; 5048c2ecf20Sopenharmony_ci pneg_ctxt->Name[6] = 0x11; 5058c2ecf20Sopenharmony_ci pneg_ctxt->Name[7] = 0xE7; 5068c2ecf20Sopenharmony_ci pneg_ctxt->Name[8] = 0xB4; 5078c2ecf20Sopenharmony_ci pneg_ctxt->Name[9] = 0x23; 5088c2ecf20Sopenharmony_ci pneg_ctxt->Name[10] = 0x83; 5098c2ecf20Sopenharmony_ci pneg_ctxt->Name[11] = 0xDE; 5108c2ecf20Sopenharmony_ci pneg_ctxt->Name[12] = 0x96; 5118c2ecf20Sopenharmony_ci pneg_ctxt->Name[13] = 0x8B; 5128c2ecf20Sopenharmony_ci pneg_ctxt->Name[14] = 0xCD; 5138c2ecf20Sopenharmony_ci pneg_ctxt->Name[15] = 0x7C; 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_cistatic void 5178c2ecf20Sopenharmony_ciassemble_neg_contexts(struct smb2_negotiate_req *req, 5188c2ecf20Sopenharmony_ci struct TCP_Server_Info *server, unsigned int *total_len) 5198c2ecf20Sopenharmony_ci{ 5208c2ecf20Sopenharmony_ci char *pneg_ctxt; 5218c2ecf20Sopenharmony_ci unsigned int ctxt_len; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci if (*total_len > 200) { 5248c2ecf20Sopenharmony_ci /* In case length corrupted don't want to overrun smb buffer */ 5258c2ecf20Sopenharmony_ci cifs_server_dbg(VFS, "Bad frame length assembling neg contexts\n"); 5268c2ecf20Sopenharmony_ci return; 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci /* 5308c2ecf20Sopenharmony_ci * round up total_len of fixed part of SMB3 negotiate request to 8 5318c2ecf20Sopenharmony_ci * byte boundary before adding negotiate contexts 5328c2ecf20Sopenharmony_ci */ 5338c2ecf20Sopenharmony_ci *total_len = roundup(*total_len, 8); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci pneg_ctxt = (*total_len) + (char *)req; 5368c2ecf20Sopenharmony_ci req->NegotiateContextOffset = cpu_to_le32(*total_len); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci build_preauth_ctxt((struct smb2_preauth_neg_context *)pneg_ctxt); 5398c2ecf20Sopenharmony_ci ctxt_len = DIV_ROUND_UP(sizeof(struct smb2_preauth_neg_context), 8) * 8; 5408c2ecf20Sopenharmony_ci *total_len += ctxt_len; 5418c2ecf20Sopenharmony_ci pneg_ctxt += ctxt_len; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci build_encrypt_ctxt((struct smb2_encryption_neg_context *)pneg_ctxt); 5448c2ecf20Sopenharmony_ci ctxt_len = DIV_ROUND_UP(sizeof(struct smb2_encryption_neg_context), 8) * 8; 5458c2ecf20Sopenharmony_ci *total_len += ctxt_len; 5468c2ecf20Sopenharmony_ci pneg_ctxt += ctxt_len; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci if (server->compress_algorithm) { 5498c2ecf20Sopenharmony_ci build_compression_ctxt((struct smb2_compression_capabilities_context *) 5508c2ecf20Sopenharmony_ci pneg_ctxt); 5518c2ecf20Sopenharmony_ci ctxt_len = DIV_ROUND_UP( 5528c2ecf20Sopenharmony_ci sizeof(struct smb2_compression_capabilities_context), 5538c2ecf20Sopenharmony_ci 8) * 8; 5548c2ecf20Sopenharmony_ci *total_len += ctxt_len; 5558c2ecf20Sopenharmony_ci pneg_ctxt += ctxt_len; 5568c2ecf20Sopenharmony_ci req->NegotiateContextCount = cpu_to_le16(5); 5578c2ecf20Sopenharmony_ci } else 5588c2ecf20Sopenharmony_ci req->NegotiateContextCount = cpu_to_le16(4); 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci ctxt_len = build_netname_ctxt((struct smb2_netname_neg_context *)pneg_ctxt, 5618c2ecf20Sopenharmony_ci server->hostname); 5628c2ecf20Sopenharmony_ci *total_len += ctxt_len; 5638c2ecf20Sopenharmony_ci pneg_ctxt += ctxt_len; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci build_posix_ctxt((struct smb2_posix_neg_context *)pneg_ctxt); 5668c2ecf20Sopenharmony_ci *total_len += sizeof(struct smb2_posix_neg_context); 5678c2ecf20Sopenharmony_ci} 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_cistatic void decode_preauth_context(struct smb2_preauth_neg_context *ctxt) 5708c2ecf20Sopenharmony_ci{ 5718c2ecf20Sopenharmony_ci unsigned int len = le16_to_cpu(ctxt->DataLength); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci /* If invalid preauth context warn but use what we requested, SHA-512 */ 5748c2ecf20Sopenharmony_ci if (len < MIN_PREAUTH_CTXT_DATA_LEN) { 5758c2ecf20Sopenharmony_ci pr_warn_once("server sent bad preauth context\n"); 5768c2ecf20Sopenharmony_ci return; 5778c2ecf20Sopenharmony_ci } else if (len < MIN_PREAUTH_CTXT_DATA_LEN + le16_to_cpu(ctxt->SaltLength)) { 5788c2ecf20Sopenharmony_ci pr_warn_once("server sent invalid SaltLength\n"); 5798c2ecf20Sopenharmony_ci return; 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci if (le16_to_cpu(ctxt->HashAlgorithmCount) != 1) 5828c2ecf20Sopenharmony_ci pr_warn_once("Invalid SMB3 hash algorithm count\n"); 5838c2ecf20Sopenharmony_ci if (ctxt->HashAlgorithms != SMB2_PREAUTH_INTEGRITY_SHA512) 5848c2ecf20Sopenharmony_ci pr_warn_once("unknown SMB3 hash algorithm\n"); 5858c2ecf20Sopenharmony_ci} 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_cistatic void decode_compress_ctx(struct TCP_Server_Info *server, 5888c2ecf20Sopenharmony_ci struct smb2_compression_capabilities_context *ctxt) 5898c2ecf20Sopenharmony_ci{ 5908c2ecf20Sopenharmony_ci unsigned int len = le16_to_cpu(ctxt->DataLength); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci /* sizeof compress context is a one element compression capbility struct */ 5938c2ecf20Sopenharmony_ci if (len < 10) { 5948c2ecf20Sopenharmony_ci pr_warn_once("server sent bad compression cntxt\n"); 5958c2ecf20Sopenharmony_ci return; 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci if (le16_to_cpu(ctxt->CompressionAlgorithmCount) != 1) { 5988c2ecf20Sopenharmony_ci pr_warn_once("Invalid SMB3 compress algorithm count\n"); 5998c2ecf20Sopenharmony_ci return; 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci if (le16_to_cpu(ctxt->CompressionAlgorithms[0]) > 3) { 6028c2ecf20Sopenharmony_ci pr_warn_once("unknown compression algorithm\n"); 6038c2ecf20Sopenharmony_ci return; 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci server->compress_algorithm = ctxt->CompressionAlgorithms[0]; 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_cistatic int decode_encrypt_ctx(struct TCP_Server_Info *server, 6098c2ecf20Sopenharmony_ci struct smb2_encryption_neg_context *ctxt) 6108c2ecf20Sopenharmony_ci{ 6118c2ecf20Sopenharmony_ci unsigned int len = le16_to_cpu(ctxt->DataLength); 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci cifs_dbg(FYI, "decode SMB3.11 encryption neg context of len %d\n", len); 6148c2ecf20Sopenharmony_ci if (len < MIN_ENCRYPT_CTXT_DATA_LEN) { 6158c2ecf20Sopenharmony_ci pr_warn_once("server sent bad crypto ctxt len\n"); 6168c2ecf20Sopenharmony_ci return -EINVAL; 6178c2ecf20Sopenharmony_ci } 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci if (le16_to_cpu(ctxt->CipherCount) != 1) { 6208c2ecf20Sopenharmony_ci pr_warn_once("Invalid SMB3.11 cipher count\n"); 6218c2ecf20Sopenharmony_ci return -EINVAL; 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci cifs_dbg(FYI, "SMB311 cipher type:%d\n", le16_to_cpu(ctxt->Ciphers[0])); 6248c2ecf20Sopenharmony_ci if (require_gcm_256) { 6258c2ecf20Sopenharmony_ci if (ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES256_GCM) { 6268c2ecf20Sopenharmony_ci cifs_dbg(VFS, "Server does not support requested encryption type (AES256 GCM)\n"); 6278c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 6288c2ecf20Sopenharmony_ci } 6298c2ecf20Sopenharmony_ci } else if (ctxt->Ciphers[0] == 0) { 6308c2ecf20Sopenharmony_ci /* 6318c2ecf20Sopenharmony_ci * e.g. if server only supported AES256_CCM (very unlikely) 6328c2ecf20Sopenharmony_ci * or server supported no encryption types or had all disabled. 6338c2ecf20Sopenharmony_ci * Since GLOBAL_CAP_ENCRYPTION will be not set, in the case 6348c2ecf20Sopenharmony_ci * in which mount requested encryption ("seal") checks later 6358c2ecf20Sopenharmony_ci * on during tree connection will return proper rc, but if 6368c2ecf20Sopenharmony_ci * seal not requested by client, since server is allowed to 6378c2ecf20Sopenharmony_ci * return 0 to indicate no supported cipher, we can't fail here 6388c2ecf20Sopenharmony_ci */ 6398c2ecf20Sopenharmony_ci server->cipher_type = 0; 6408c2ecf20Sopenharmony_ci server->capabilities &= ~SMB2_GLOBAL_CAP_ENCRYPTION; 6418c2ecf20Sopenharmony_ci pr_warn_once("Server does not support requested encryption types\n"); 6428c2ecf20Sopenharmony_ci return 0; 6438c2ecf20Sopenharmony_ci } else if ((ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES128_CCM) && 6448c2ecf20Sopenharmony_ci (ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES128_GCM) && 6458c2ecf20Sopenharmony_ci (ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES256_GCM)) { 6468c2ecf20Sopenharmony_ci /* server returned a cipher we didn't ask for */ 6478c2ecf20Sopenharmony_ci pr_warn_once("Invalid SMB3.11 cipher returned\n"); 6488c2ecf20Sopenharmony_ci return -EINVAL; 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ci server->cipher_type = ctxt->Ciphers[0]; 6518c2ecf20Sopenharmony_ci server->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; 6528c2ecf20Sopenharmony_ci return 0; 6538c2ecf20Sopenharmony_ci} 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_cistatic int smb311_decode_neg_context(struct smb2_negotiate_rsp *rsp, 6568c2ecf20Sopenharmony_ci struct TCP_Server_Info *server, 6578c2ecf20Sopenharmony_ci unsigned int len_of_smb) 6588c2ecf20Sopenharmony_ci{ 6598c2ecf20Sopenharmony_ci struct smb2_neg_context *pctx; 6608c2ecf20Sopenharmony_ci unsigned int offset = le32_to_cpu(rsp->NegotiateContextOffset); 6618c2ecf20Sopenharmony_ci unsigned int ctxt_cnt = le16_to_cpu(rsp->NegotiateContextCount); 6628c2ecf20Sopenharmony_ci unsigned int len_of_ctxts, i; 6638c2ecf20Sopenharmony_ci int rc = 0; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci cifs_dbg(FYI, "decoding %d negotiate contexts\n", ctxt_cnt); 6668c2ecf20Sopenharmony_ci if (len_of_smb <= offset) { 6678c2ecf20Sopenharmony_ci cifs_server_dbg(VFS, "Invalid response: negotiate context offset\n"); 6688c2ecf20Sopenharmony_ci return -EINVAL; 6698c2ecf20Sopenharmony_ci } 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci len_of_ctxts = len_of_smb - offset; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci for (i = 0; i < ctxt_cnt; i++) { 6748c2ecf20Sopenharmony_ci int clen; 6758c2ecf20Sopenharmony_ci /* check that offset is not beyond end of SMB */ 6768c2ecf20Sopenharmony_ci if (len_of_ctxts == 0) 6778c2ecf20Sopenharmony_ci break; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci if (len_of_ctxts < sizeof(struct smb2_neg_context)) 6808c2ecf20Sopenharmony_ci break; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci pctx = (struct smb2_neg_context *)(offset + (char *)rsp); 6838c2ecf20Sopenharmony_ci clen = le16_to_cpu(pctx->DataLength); 6848c2ecf20Sopenharmony_ci if (clen > len_of_ctxts) 6858c2ecf20Sopenharmony_ci break; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci if (pctx->ContextType == SMB2_PREAUTH_INTEGRITY_CAPABILITIES) 6888c2ecf20Sopenharmony_ci decode_preauth_context( 6898c2ecf20Sopenharmony_ci (struct smb2_preauth_neg_context *)pctx); 6908c2ecf20Sopenharmony_ci else if (pctx->ContextType == SMB2_ENCRYPTION_CAPABILITIES) 6918c2ecf20Sopenharmony_ci rc = decode_encrypt_ctx(server, 6928c2ecf20Sopenharmony_ci (struct smb2_encryption_neg_context *)pctx); 6938c2ecf20Sopenharmony_ci else if (pctx->ContextType == SMB2_COMPRESSION_CAPABILITIES) 6948c2ecf20Sopenharmony_ci decode_compress_ctx(server, 6958c2ecf20Sopenharmony_ci (struct smb2_compression_capabilities_context *)pctx); 6968c2ecf20Sopenharmony_ci else if (pctx->ContextType == SMB2_POSIX_EXTENSIONS_AVAILABLE) 6978c2ecf20Sopenharmony_ci server->posix_ext_supported = true; 6988c2ecf20Sopenharmony_ci else 6998c2ecf20Sopenharmony_ci cifs_server_dbg(VFS, "unknown negcontext of type %d ignored\n", 7008c2ecf20Sopenharmony_ci le16_to_cpu(pctx->ContextType)); 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci if (rc) 7038c2ecf20Sopenharmony_ci break; 7048c2ecf20Sopenharmony_ci /* offsets must be 8 byte aligned */ 7058c2ecf20Sopenharmony_ci clen = (clen + 7) & ~0x7; 7068c2ecf20Sopenharmony_ci offset += clen + sizeof(struct smb2_neg_context); 7078c2ecf20Sopenharmony_ci len_of_ctxts -= clen; 7088c2ecf20Sopenharmony_ci } 7098c2ecf20Sopenharmony_ci return rc; 7108c2ecf20Sopenharmony_ci} 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_cistatic struct create_posix * 7138c2ecf20Sopenharmony_cicreate_posix_buf(umode_t mode) 7148c2ecf20Sopenharmony_ci{ 7158c2ecf20Sopenharmony_ci struct create_posix *buf; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci buf = kzalloc(sizeof(struct create_posix), 7188c2ecf20Sopenharmony_ci GFP_KERNEL); 7198c2ecf20Sopenharmony_ci if (!buf) 7208c2ecf20Sopenharmony_ci return NULL; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci buf->ccontext.DataOffset = 7238c2ecf20Sopenharmony_ci cpu_to_le16(offsetof(struct create_posix, Mode)); 7248c2ecf20Sopenharmony_ci buf->ccontext.DataLength = cpu_to_le32(4); 7258c2ecf20Sopenharmony_ci buf->ccontext.NameOffset = 7268c2ecf20Sopenharmony_ci cpu_to_le16(offsetof(struct create_posix, Name)); 7278c2ecf20Sopenharmony_ci buf->ccontext.NameLength = cpu_to_le16(16); 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci /* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */ 7308c2ecf20Sopenharmony_ci buf->Name[0] = 0x93; 7318c2ecf20Sopenharmony_ci buf->Name[1] = 0xAD; 7328c2ecf20Sopenharmony_ci buf->Name[2] = 0x25; 7338c2ecf20Sopenharmony_ci buf->Name[3] = 0x50; 7348c2ecf20Sopenharmony_ci buf->Name[4] = 0x9C; 7358c2ecf20Sopenharmony_ci buf->Name[5] = 0xB4; 7368c2ecf20Sopenharmony_ci buf->Name[6] = 0x11; 7378c2ecf20Sopenharmony_ci buf->Name[7] = 0xE7; 7388c2ecf20Sopenharmony_ci buf->Name[8] = 0xB4; 7398c2ecf20Sopenharmony_ci buf->Name[9] = 0x23; 7408c2ecf20Sopenharmony_ci buf->Name[10] = 0x83; 7418c2ecf20Sopenharmony_ci buf->Name[11] = 0xDE; 7428c2ecf20Sopenharmony_ci buf->Name[12] = 0x96; 7438c2ecf20Sopenharmony_ci buf->Name[13] = 0x8B; 7448c2ecf20Sopenharmony_ci buf->Name[14] = 0xCD; 7458c2ecf20Sopenharmony_ci buf->Name[15] = 0x7C; 7468c2ecf20Sopenharmony_ci buf->Mode = cpu_to_le32(mode); 7478c2ecf20Sopenharmony_ci cifs_dbg(FYI, "mode on posix create 0%o\n", mode); 7488c2ecf20Sopenharmony_ci return buf; 7498c2ecf20Sopenharmony_ci} 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_cistatic int 7528c2ecf20Sopenharmony_ciadd_posix_context(struct kvec *iov, unsigned int *num_iovec, umode_t mode) 7538c2ecf20Sopenharmony_ci{ 7548c2ecf20Sopenharmony_ci struct smb2_create_req *req = iov[0].iov_base; 7558c2ecf20Sopenharmony_ci unsigned int num = *num_iovec; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci iov[num].iov_base = create_posix_buf(mode); 7588c2ecf20Sopenharmony_ci if (mode == ACL_NO_MODE) 7598c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Invalid mode\n"); 7608c2ecf20Sopenharmony_ci if (iov[num].iov_base == NULL) 7618c2ecf20Sopenharmony_ci return -ENOMEM; 7628c2ecf20Sopenharmony_ci iov[num].iov_len = sizeof(struct create_posix); 7638c2ecf20Sopenharmony_ci if (!req->CreateContextsOffset) 7648c2ecf20Sopenharmony_ci req->CreateContextsOffset = cpu_to_le32( 7658c2ecf20Sopenharmony_ci sizeof(struct smb2_create_req) + 7668c2ecf20Sopenharmony_ci iov[num - 1].iov_len); 7678c2ecf20Sopenharmony_ci le32_add_cpu(&req->CreateContextsLength, sizeof(struct create_posix)); 7688c2ecf20Sopenharmony_ci *num_iovec = num + 1; 7698c2ecf20Sopenharmony_ci return 0; 7708c2ecf20Sopenharmony_ci} 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci/* 7748c2ecf20Sopenharmony_ci * 7758c2ecf20Sopenharmony_ci * SMB2 Worker functions follow: 7768c2ecf20Sopenharmony_ci * 7778c2ecf20Sopenharmony_ci * The general structure of the worker functions is: 7788c2ecf20Sopenharmony_ci * 1) Call smb2_init (assembles SMB2 header) 7798c2ecf20Sopenharmony_ci * 2) Initialize SMB2 command specific fields in fixed length area of SMB 7808c2ecf20Sopenharmony_ci * 3) Call smb_sendrcv2 (sends request on socket and waits for response) 7818c2ecf20Sopenharmony_ci * 4) Decode SMB2 command specific fields in the fixed length area 7828c2ecf20Sopenharmony_ci * 5) Decode variable length data area (if any for this SMB2 command type) 7838c2ecf20Sopenharmony_ci * 6) Call free smb buffer 7848c2ecf20Sopenharmony_ci * 7) return 7858c2ecf20Sopenharmony_ci * 7868c2ecf20Sopenharmony_ci */ 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ciint 7898c2ecf20Sopenharmony_ciSMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) 7908c2ecf20Sopenharmony_ci{ 7918c2ecf20Sopenharmony_ci struct smb_rqst rqst; 7928c2ecf20Sopenharmony_ci struct smb2_negotiate_req *req; 7938c2ecf20Sopenharmony_ci struct smb2_negotiate_rsp *rsp; 7948c2ecf20Sopenharmony_ci struct kvec iov[1]; 7958c2ecf20Sopenharmony_ci struct kvec rsp_iov; 7968c2ecf20Sopenharmony_ci int rc = 0; 7978c2ecf20Sopenharmony_ci int resp_buftype; 7988c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = cifs_ses_server(ses); 7998c2ecf20Sopenharmony_ci int blob_offset, blob_length; 8008c2ecf20Sopenharmony_ci char *security_blob; 8018c2ecf20Sopenharmony_ci int flags = CIFS_NEG_OP; 8028c2ecf20Sopenharmony_ci unsigned int total_len; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Negotiate protocol\n"); 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci if (!server) { 8078c2ecf20Sopenharmony_ci WARN(1, "%s: server is NULL!\n", __func__); 8088c2ecf20Sopenharmony_ci return -EIO; 8098c2ecf20Sopenharmony_ci } 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci rc = smb2_plain_req_init(SMB2_NEGOTIATE, NULL, server, 8128c2ecf20Sopenharmony_ci (void **) &req, &total_len); 8138c2ecf20Sopenharmony_ci if (rc) 8148c2ecf20Sopenharmony_ci return rc; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci req->sync_hdr.SessionId = 0; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci memset(server->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE); 8198c2ecf20Sopenharmony_ci memset(ses->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE); 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci if (strcmp(server->vals->version_string, 8228c2ecf20Sopenharmony_ci SMB3ANY_VERSION_STRING) == 0) { 8238c2ecf20Sopenharmony_ci req->Dialects[0] = cpu_to_le16(SMB30_PROT_ID); 8248c2ecf20Sopenharmony_ci req->Dialects[1] = cpu_to_le16(SMB302_PROT_ID); 8258c2ecf20Sopenharmony_ci req->DialectCount = cpu_to_le16(2); 8268c2ecf20Sopenharmony_ci total_len += 4; 8278c2ecf20Sopenharmony_ci } else if (strcmp(server->vals->version_string, 8288c2ecf20Sopenharmony_ci SMBDEFAULT_VERSION_STRING) == 0) { 8298c2ecf20Sopenharmony_ci req->Dialects[0] = cpu_to_le16(SMB21_PROT_ID); 8308c2ecf20Sopenharmony_ci req->Dialects[1] = cpu_to_le16(SMB30_PROT_ID); 8318c2ecf20Sopenharmony_ci req->Dialects[2] = cpu_to_le16(SMB302_PROT_ID); 8328c2ecf20Sopenharmony_ci req->Dialects[3] = cpu_to_le16(SMB311_PROT_ID); 8338c2ecf20Sopenharmony_ci req->DialectCount = cpu_to_le16(4); 8348c2ecf20Sopenharmony_ci total_len += 8; 8358c2ecf20Sopenharmony_ci } else { 8368c2ecf20Sopenharmony_ci /* otherwise send specific dialect */ 8378c2ecf20Sopenharmony_ci req->Dialects[0] = cpu_to_le16(server->vals->protocol_id); 8388c2ecf20Sopenharmony_ci req->DialectCount = cpu_to_le16(1); 8398c2ecf20Sopenharmony_ci total_len += 2; 8408c2ecf20Sopenharmony_ci } 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci /* only one of SMB2 signing flags may be set in SMB2 request */ 8438c2ecf20Sopenharmony_ci if (ses->sign) 8448c2ecf20Sopenharmony_ci req->SecurityMode = cpu_to_le16(SMB2_NEGOTIATE_SIGNING_REQUIRED); 8458c2ecf20Sopenharmony_ci else if (global_secflags & CIFSSEC_MAY_SIGN) 8468c2ecf20Sopenharmony_ci req->SecurityMode = cpu_to_le16(SMB2_NEGOTIATE_SIGNING_ENABLED); 8478c2ecf20Sopenharmony_ci else 8488c2ecf20Sopenharmony_ci req->SecurityMode = 0; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci req->Capabilities = cpu_to_le32(server->vals->req_capabilities); 8518c2ecf20Sopenharmony_ci if (ses->chan_max > 1) 8528c2ecf20Sopenharmony_ci req->Capabilities |= cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL); 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci /* ClientGUID must be zero for SMB2.02 dialect */ 8558c2ecf20Sopenharmony_ci if (server->vals->protocol_id == SMB20_PROT_ID) 8568c2ecf20Sopenharmony_ci memset(req->ClientGUID, 0, SMB2_CLIENT_GUID_SIZE); 8578c2ecf20Sopenharmony_ci else { 8588c2ecf20Sopenharmony_ci memcpy(req->ClientGUID, server->client_guid, 8598c2ecf20Sopenharmony_ci SMB2_CLIENT_GUID_SIZE); 8608c2ecf20Sopenharmony_ci if ((server->vals->protocol_id == SMB311_PROT_ID) || 8618c2ecf20Sopenharmony_ci (strcmp(server->vals->version_string, 8628c2ecf20Sopenharmony_ci SMBDEFAULT_VERSION_STRING) == 0)) 8638c2ecf20Sopenharmony_ci assemble_neg_contexts(req, server, &total_len); 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci iov[0].iov_base = (char *)req; 8668c2ecf20Sopenharmony_ci iov[0].iov_len = total_len; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 8698c2ecf20Sopenharmony_ci rqst.rq_iov = iov; 8708c2ecf20Sopenharmony_ci rqst.rq_nvec = 1; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci rc = cifs_send_recv(xid, ses, server, 8738c2ecf20Sopenharmony_ci &rqst, &resp_buftype, flags, &rsp_iov); 8748c2ecf20Sopenharmony_ci cifs_small_buf_release(req); 8758c2ecf20Sopenharmony_ci rsp = (struct smb2_negotiate_rsp *)rsp_iov.iov_base; 8768c2ecf20Sopenharmony_ci /* 8778c2ecf20Sopenharmony_ci * No tcon so can't do 8788c2ecf20Sopenharmony_ci * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]); 8798c2ecf20Sopenharmony_ci */ 8808c2ecf20Sopenharmony_ci if (rc == -EOPNOTSUPP) { 8818c2ecf20Sopenharmony_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"); 8828c2ecf20Sopenharmony_ci goto neg_exit; 8838c2ecf20Sopenharmony_ci } else if (rc != 0) 8848c2ecf20Sopenharmony_ci goto neg_exit; 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci if (strcmp(server->vals->version_string, 8878c2ecf20Sopenharmony_ci SMB3ANY_VERSION_STRING) == 0) { 8888c2ecf20Sopenharmony_ci if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) { 8898c2ecf20Sopenharmony_ci cifs_server_dbg(VFS, 8908c2ecf20Sopenharmony_ci "SMB2 dialect returned but not requested\n"); 8918c2ecf20Sopenharmony_ci return -EIO; 8928c2ecf20Sopenharmony_ci } else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) { 8938c2ecf20Sopenharmony_ci cifs_server_dbg(VFS, 8948c2ecf20Sopenharmony_ci "SMB2.1 dialect returned but not requested\n"); 8958c2ecf20Sopenharmony_ci return -EIO; 8968c2ecf20Sopenharmony_ci } 8978c2ecf20Sopenharmony_ci } else if (strcmp(server->vals->version_string, 8988c2ecf20Sopenharmony_ci SMBDEFAULT_VERSION_STRING) == 0) { 8998c2ecf20Sopenharmony_ci if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) { 9008c2ecf20Sopenharmony_ci cifs_server_dbg(VFS, 9018c2ecf20Sopenharmony_ci "SMB2 dialect returned but not requested\n"); 9028c2ecf20Sopenharmony_ci return -EIO; 9038c2ecf20Sopenharmony_ci } else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) { 9048c2ecf20Sopenharmony_ci /* ops set to 3.0 by default for default so update */ 9058c2ecf20Sopenharmony_ci server->ops = &smb21_operations; 9068c2ecf20Sopenharmony_ci server->vals = &smb21_values; 9078c2ecf20Sopenharmony_ci } else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) { 9088c2ecf20Sopenharmony_ci server->ops = &smb311_operations; 9098c2ecf20Sopenharmony_ci server->vals = &smb311_values; 9108c2ecf20Sopenharmony_ci } 9118c2ecf20Sopenharmony_ci } else if (le16_to_cpu(rsp->DialectRevision) != 9128c2ecf20Sopenharmony_ci server->vals->protocol_id) { 9138c2ecf20Sopenharmony_ci /* if requested single dialect ensure returned dialect matched */ 9148c2ecf20Sopenharmony_ci cifs_server_dbg(VFS, "Invalid 0x%x dialect returned: not requested\n", 9158c2ecf20Sopenharmony_ci le16_to_cpu(rsp->DialectRevision)); 9168c2ecf20Sopenharmony_ci return -EIO; 9178c2ecf20Sopenharmony_ci } 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci cifs_dbg(FYI, "mode 0x%x\n", rsp->SecurityMode); 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) 9228c2ecf20Sopenharmony_ci cifs_dbg(FYI, "negotiated smb2.0 dialect\n"); 9238c2ecf20Sopenharmony_ci else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) 9248c2ecf20Sopenharmony_ci cifs_dbg(FYI, "negotiated smb2.1 dialect\n"); 9258c2ecf20Sopenharmony_ci else if (rsp->DialectRevision == cpu_to_le16(SMB30_PROT_ID)) 9268c2ecf20Sopenharmony_ci cifs_dbg(FYI, "negotiated smb3.0 dialect\n"); 9278c2ecf20Sopenharmony_ci else if (rsp->DialectRevision == cpu_to_le16(SMB302_PROT_ID)) 9288c2ecf20Sopenharmony_ci cifs_dbg(FYI, "negotiated smb3.02 dialect\n"); 9298c2ecf20Sopenharmony_ci else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) 9308c2ecf20Sopenharmony_ci cifs_dbg(FYI, "negotiated smb3.1.1 dialect\n"); 9318c2ecf20Sopenharmony_ci else { 9328c2ecf20Sopenharmony_ci cifs_server_dbg(VFS, "Invalid dialect returned by server 0x%x\n", 9338c2ecf20Sopenharmony_ci le16_to_cpu(rsp->DialectRevision)); 9348c2ecf20Sopenharmony_ci rc = -EIO; 9358c2ecf20Sopenharmony_ci goto neg_exit; 9368c2ecf20Sopenharmony_ci } 9378c2ecf20Sopenharmony_ci server->dialect = le16_to_cpu(rsp->DialectRevision); 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci /* 9408c2ecf20Sopenharmony_ci * Keep a copy of the hash after negprot. This hash will be 9418c2ecf20Sopenharmony_ci * the starting hash value for all sessions made from this 9428c2ecf20Sopenharmony_ci * server. 9438c2ecf20Sopenharmony_ci */ 9448c2ecf20Sopenharmony_ci memcpy(server->preauth_sha_hash, ses->preauth_sha_hash, 9458c2ecf20Sopenharmony_ci SMB2_PREAUTH_HASH_SIZE); 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci /* SMB2 only has an extended negflavor */ 9488c2ecf20Sopenharmony_ci server->negflavor = CIFS_NEGFLAVOR_EXTENDED; 9498c2ecf20Sopenharmony_ci /* set it to the maximum buffer size value we can send with 1 credit */ 9508c2ecf20Sopenharmony_ci server->maxBuf = min_t(unsigned int, le32_to_cpu(rsp->MaxTransactSize), 9518c2ecf20Sopenharmony_ci SMB2_MAX_BUFFER_SIZE); 9528c2ecf20Sopenharmony_ci server->max_read = le32_to_cpu(rsp->MaxReadSize); 9538c2ecf20Sopenharmony_ci server->max_write = le32_to_cpu(rsp->MaxWriteSize); 9548c2ecf20Sopenharmony_ci server->sec_mode = le16_to_cpu(rsp->SecurityMode); 9558c2ecf20Sopenharmony_ci if ((server->sec_mode & SMB2_SEC_MODE_FLAGS_ALL) != server->sec_mode) 9568c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Server returned unexpected security mode 0x%x\n", 9578c2ecf20Sopenharmony_ci server->sec_mode); 9588c2ecf20Sopenharmony_ci server->capabilities = le32_to_cpu(rsp->Capabilities); 9598c2ecf20Sopenharmony_ci /* Internal types */ 9608c2ecf20Sopenharmony_ci server->capabilities |= SMB2_NT_FIND | SMB2_LARGE_FILES; 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci /* 9638c2ecf20Sopenharmony_ci * SMB3.0 supports only 1 cipher and doesn't have a encryption neg context 9648c2ecf20Sopenharmony_ci * Set the cipher type manually. 9658c2ecf20Sopenharmony_ci */ 9668c2ecf20Sopenharmony_ci if (server->dialect == SMB30_PROT_ID && (server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) 9678c2ecf20Sopenharmony_ci server->cipher_type = SMB2_ENCRYPTION_AES128_CCM; 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci security_blob = smb2_get_data_area_len(&blob_offset, &blob_length, 9708c2ecf20Sopenharmony_ci (struct smb2_sync_hdr *)rsp); 9718c2ecf20Sopenharmony_ci /* 9728c2ecf20Sopenharmony_ci * See MS-SMB2 section 2.2.4: if no blob, client picks default which 9738c2ecf20Sopenharmony_ci * for us will be 9748c2ecf20Sopenharmony_ci * ses->sectype = RawNTLMSSP; 9758c2ecf20Sopenharmony_ci * but for time being this is our only auth choice so doesn't matter. 9768c2ecf20Sopenharmony_ci * We just found a server which sets blob length to zero expecting raw. 9778c2ecf20Sopenharmony_ci */ 9788c2ecf20Sopenharmony_ci if (blob_length == 0) { 9798c2ecf20Sopenharmony_ci cifs_dbg(FYI, "missing security blob on negprot\n"); 9808c2ecf20Sopenharmony_ci server->sec_ntlmssp = true; 9818c2ecf20Sopenharmony_ci } 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci rc = cifs_enable_signing(server, ses->sign); 9848c2ecf20Sopenharmony_ci if (rc) 9858c2ecf20Sopenharmony_ci goto neg_exit; 9868c2ecf20Sopenharmony_ci if (blob_length) { 9878c2ecf20Sopenharmony_ci rc = decode_negTokenInit(security_blob, blob_length, server); 9888c2ecf20Sopenharmony_ci if (rc == 1) 9898c2ecf20Sopenharmony_ci rc = 0; 9908c2ecf20Sopenharmony_ci else if (rc == 0) 9918c2ecf20Sopenharmony_ci rc = -EIO; 9928c2ecf20Sopenharmony_ci } 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) { 9958c2ecf20Sopenharmony_ci if (rsp->NegotiateContextCount) 9968c2ecf20Sopenharmony_ci rc = smb311_decode_neg_context(rsp, server, 9978c2ecf20Sopenharmony_ci rsp_iov.iov_len); 9988c2ecf20Sopenharmony_ci else 9998c2ecf20Sopenharmony_ci cifs_server_dbg(VFS, "Missing expected negotiate contexts\n"); 10008c2ecf20Sopenharmony_ci } 10018c2ecf20Sopenharmony_cineg_exit: 10028c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype, rsp); 10038c2ecf20Sopenharmony_ci return rc; 10048c2ecf20Sopenharmony_ci} 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ciint smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) 10078c2ecf20Sopenharmony_ci{ 10088c2ecf20Sopenharmony_ci int rc; 10098c2ecf20Sopenharmony_ci struct validate_negotiate_info_req *pneg_inbuf; 10108c2ecf20Sopenharmony_ci struct validate_negotiate_info_rsp *pneg_rsp = NULL; 10118c2ecf20Sopenharmony_ci u32 rsplen; 10128c2ecf20Sopenharmony_ci u32 inbuflen; /* max of 4 dialects */ 10138c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = tcon->ses->server; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci cifs_dbg(FYI, "validate negotiate\n"); 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci /* In SMB3.11 preauth integrity supersedes validate negotiate */ 10188c2ecf20Sopenharmony_ci if (server->dialect == SMB311_PROT_ID) 10198c2ecf20Sopenharmony_ci return 0; 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci /* 10228c2ecf20Sopenharmony_ci * validation ioctl must be signed, so no point sending this if we 10238c2ecf20Sopenharmony_ci * can not sign it (ie are not known user). Even if signing is not 10248c2ecf20Sopenharmony_ci * required (enabled but not negotiated), in those cases we selectively 10258c2ecf20Sopenharmony_ci * sign just this, the first and only signed request on a connection. 10268c2ecf20Sopenharmony_ci * Having validation of negotiate info helps reduce attack vectors. 10278c2ecf20Sopenharmony_ci */ 10288c2ecf20Sopenharmony_ci if (tcon->ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST) 10298c2ecf20Sopenharmony_ci return 0; /* validation requires signing */ 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci if (tcon->ses->user_name == NULL) { 10328c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Can't validate negotiate: null user mount\n"); 10338c2ecf20Sopenharmony_ci return 0; /* validation requires signing */ 10348c2ecf20Sopenharmony_ci } 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci if (tcon->ses->session_flags & SMB2_SESSION_FLAG_IS_NULL) 10378c2ecf20Sopenharmony_ci cifs_tcon_dbg(VFS, "Unexpected null user (anonymous) auth flag sent by server\n"); 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci pneg_inbuf = kmalloc(sizeof(*pneg_inbuf), GFP_NOFS); 10408c2ecf20Sopenharmony_ci if (!pneg_inbuf) 10418c2ecf20Sopenharmony_ci return -ENOMEM; 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci pneg_inbuf->Capabilities = 10448c2ecf20Sopenharmony_ci cpu_to_le32(server->vals->req_capabilities); 10458c2ecf20Sopenharmony_ci if (tcon->ses->chan_max > 1) 10468c2ecf20Sopenharmony_ci pneg_inbuf->Capabilities |= cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL); 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci memcpy(pneg_inbuf->Guid, server->client_guid, 10498c2ecf20Sopenharmony_ci SMB2_CLIENT_GUID_SIZE); 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci if (tcon->ses->sign) 10528c2ecf20Sopenharmony_ci pneg_inbuf->SecurityMode = 10538c2ecf20Sopenharmony_ci cpu_to_le16(SMB2_NEGOTIATE_SIGNING_REQUIRED); 10548c2ecf20Sopenharmony_ci else if (global_secflags & CIFSSEC_MAY_SIGN) 10558c2ecf20Sopenharmony_ci pneg_inbuf->SecurityMode = 10568c2ecf20Sopenharmony_ci cpu_to_le16(SMB2_NEGOTIATE_SIGNING_ENABLED); 10578c2ecf20Sopenharmony_ci else 10588c2ecf20Sopenharmony_ci pneg_inbuf->SecurityMode = 0; 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci if (strcmp(server->vals->version_string, 10628c2ecf20Sopenharmony_ci SMB3ANY_VERSION_STRING) == 0) { 10638c2ecf20Sopenharmony_ci pneg_inbuf->Dialects[0] = cpu_to_le16(SMB30_PROT_ID); 10648c2ecf20Sopenharmony_ci pneg_inbuf->Dialects[1] = cpu_to_le16(SMB302_PROT_ID); 10658c2ecf20Sopenharmony_ci pneg_inbuf->DialectCount = cpu_to_le16(2); 10668c2ecf20Sopenharmony_ci /* structure is big enough for 3 dialects, sending only 2 */ 10678c2ecf20Sopenharmony_ci inbuflen = sizeof(*pneg_inbuf) - 10688c2ecf20Sopenharmony_ci (2 * sizeof(pneg_inbuf->Dialects[0])); 10698c2ecf20Sopenharmony_ci } else if (strcmp(server->vals->version_string, 10708c2ecf20Sopenharmony_ci SMBDEFAULT_VERSION_STRING) == 0) { 10718c2ecf20Sopenharmony_ci pneg_inbuf->Dialects[0] = cpu_to_le16(SMB21_PROT_ID); 10728c2ecf20Sopenharmony_ci pneg_inbuf->Dialects[1] = cpu_to_le16(SMB30_PROT_ID); 10738c2ecf20Sopenharmony_ci pneg_inbuf->Dialects[2] = cpu_to_le16(SMB302_PROT_ID); 10748c2ecf20Sopenharmony_ci pneg_inbuf->Dialects[3] = cpu_to_le16(SMB311_PROT_ID); 10758c2ecf20Sopenharmony_ci pneg_inbuf->DialectCount = cpu_to_le16(4); 10768c2ecf20Sopenharmony_ci /* structure is big enough for 3 dialects */ 10778c2ecf20Sopenharmony_ci inbuflen = sizeof(*pneg_inbuf); 10788c2ecf20Sopenharmony_ci } else { 10798c2ecf20Sopenharmony_ci /* otherwise specific dialect was requested */ 10808c2ecf20Sopenharmony_ci pneg_inbuf->Dialects[0] = 10818c2ecf20Sopenharmony_ci cpu_to_le16(server->vals->protocol_id); 10828c2ecf20Sopenharmony_ci pneg_inbuf->DialectCount = cpu_to_le16(1); 10838c2ecf20Sopenharmony_ci /* structure is big enough for 4 dialects, sending only 1 */ 10848c2ecf20Sopenharmony_ci inbuflen = sizeof(*pneg_inbuf) - 10858c2ecf20Sopenharmony_ci sizeof(pneg_inbuf->Dialects[0]) * 3; 10868c2ecf20Sopenharmony_ci } 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, 10898c2ecf20Sopenharmony_ci FSCTL_VALIDATE_NEGOTIATE_INFO, 10908c2ecf20Sopenharmony_ci (char *)pneg_inbuf, inbuflen, CIFSMaxBufSize, 10918c2ecf20Sopenharmony_ci (char **)&pneg_rsp, &rsplen); 10928c2ecf20Sopenharmony_ci if (rc == -EOPNOTSUPP) { 10938c2ecf20Sopenharmony_ci /* 10948c2ecf20Sopenharmony_ci * Old Windows versions or Netapp SMB server can return 10958c2ecf20Sopenharmony_ci * not supported error. Client should accept it. 10968c2ecf20Sopenharmony_ci */ 10978c2ecf20Sopenharmony_ci cifs_tcon_dbg(VFS, "Server does not support validate negotiate\n"); 10988c2ecf20Sopenharmony_ci rc = 0; 10998c2ecf20Sopenharmony_ci goto out_free_inbuf; 11008c2ecf20Sopenharmony_ci } else if (rc != 0) { 11018c2ecf20Sopenharmony_ci cifs_tcon_dbg(VFS, "validate protocol negotiate failed: %d\n", 11028c2ecf20Sopenharmony_ci rc); 11038c2ecf20Sopenharmony_ci rc = -EIO; 11048c2ecf20Sopenharmony_ci goto out_free_inbuf; 11058c2ecf20Sopenharmony_ci } 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci rc = -EIO; 11088c2ecf20Sopenharmony_ci if (rsplen != sizeof(*pneg_rsp)) { 11098c2ecf20Sopenharmony_ci cifs_tcon_dbg(VFS, "Invalid protocol negotiate response size: %d\n", 11108c2ecf20Sopenharmony_ci rsplen); 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci /* relax check since Mac returns max bufsize allowed on ioctl */ 11138c2ecf20Sopenharmony_ci if (rsplen > CIFSMaxBufSize || rsplen < sizeof(*pneg_rsp)) 11148c2ecf20Sopenharmony_ci goto out_free_rsp; 11158c2ecf20Sopenharmony_ci } 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci /* check validate negotiate info response matches what we got earlier */ 11188c2ecf20Sopenharmony_ci if (pneg_rsp->Dialect != cpu_to_le16(server->dialect)) 11198c2ecf20Sopenharmony_ci goto vneg_out; 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci if (pneg_rsp->SecurityMode != cpu_to_le16(server->sec_mode)) 11228c2ecf20Sopenharmony_ci goto vneg_out; 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci /* do not validate server guid because not saved at negprot time yet */ 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci if ((le32_to_cpu(pneg_rsp->Capabilities) | SMB2_NT_FIND | 11278c2ecf20Sopenharmony_ci SMB2_LARGE_FILES) != server->capabilities) 11288c2ecf20Sopenharmony_ci goto vneg_out; 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci /* validate negotiate successful */ 11318c2ecf20Sopenharmony_ci rc = 0; 11328c2ecf20Sopenharmony_ci cifs_dbg(FYI, "validate negotiate info successful\n"); 11338c2ecf20Sopenharmony_ci goto out_free_rsp; 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_civneg_out: 11368c2ecf20Sopenharmony_ci cifs_tcon_dbg(VFS, "protocol revalidation - security settings mismatch\n"); 11378c2ecf20Sopenharmony_ciout_free_rsp: 11388c2ecf20Sopenharmony_ci kfree(pneg_rsp); 11398c2ecf20Sopenharmony_ciout_free_inbuf: 11408c2ecf20Sopenharmony_ci kfree(pneg_inbuf); 11418c2ecf20Sopenharmony_ci return rc; 11428c2ecf20Sopenharmony_ci} 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_cienum securityEnum 11458c2ecf20Sopenharmony_cismb2_select_sectype(struct TCP_Server_Info *server, enum securityEnum requested) 11468c2ecf20Sopenharmony_ci{ 11478c2ecf20Sopenharmony_ci switch (requested) { 11488c2ecf20Sopenharmony_ci case Kerberos: 11498c2ecf20Sopenharmony_ci case RawNTLMSSP: 11508c2ecf20Sopenharmony_ci return requested; 11518c2ecf20Sopenharmony_ci case NTLMv2: 11528c2ecf20Sopenharmony_ci return RawNTLMSSP; 11538c2ecf20Sopenharmony_ci case Unspecified: 11548c2ecf20Sopenharmony_ci if (server->sec_ntlmssp && 11558c2ecf20Sopenharmony_ci (global_secflags & CIFSSEC_MAY_NTLMSSP)) 11568c2ecf20Sopenharmony_ci return RawNTLMSSP; 11578c2ecf20Sopenharmony_ci if ((server->sec_kerberos || server->sec_mskerberos) && 11588c2ecf20Sopenharmony_ci (global_secflags & CIFSSEC_MAY_KRB5)) 11598c2ecf20Sopenharmony_ci return Kerberos; 11608c2ecf20Sopenharmony_ci fallthrough; 11618c2ecf20Sopenharmony_ci default: 11628c2ecf20Sopenharmony_ci return Unspecified; 11638c2ecf20Sopenharmony_ci } 11648c2ecf20Sopenharmony_ci} 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_cistruct SMB2_sess_data { 11678c2ecf20Sopenharmony_ci unsigned int xid; 11688c2ecf20Sopenharmony_ci struct cifs_ses *ses; 11698c2ecf20Sopenharmony_ci struct nls_table *nls_cp; 11708c2ecf20Sopenharmony_ci void (*func)(struct SMB2_sess_data *); 11718c2ecf20Sopenharmony_ci int result; 11728c2ecf20Sopenharmony_ci u64 previous_session; 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci /* we will send the SMB in three pieces: 11758c2ecf20Sopenharmony_ci * a fixed length beginning part, an optional 11768c2ecf20Sopenharmony_ci * SPNEGO blob (which can be zero length), and a 11778c2ecf20Sopenharmony_ci * last part which will include the strings 11788c2ecf20Sopenharmony_ci * and rest of bcc area. This allows us to avoid 11798c2ecf20Sopenharmony_ci * a large buffer 17K allocation 11808c2ecf20Sopenharmony_ci */ 11818c2ecf20Sopenharmony_ci int buf0_type; 11828c2ecf20Sopenharmony_ci struct kvec iov[2]; 11838c2ecf20Sopenharmony_ci}; 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_cistatic int 11868c2ecf20Sopenharmony_ciSMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data) 11878c2ecf20Sopenharmony_ci{ 11888c2ecf20Sopenharmony_ci int rc; 11898c2ecf20Sopenharmony_ci struct cifs_ses *ses = sess_data->ses; 11908c2ecf20Sopenharmony_ci struct smb2_sess_setup_req *req; 11918c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = cifs_ses_server(ses); 11928c2ecf20Sopenharmony_ci unsigned int total_len; 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci rc = smb2_plain_req_init(SMB2_SESSION_SETUP, NULL, server, 11958c2ecf20Sopenharmony_ci (void **) &req, 11968c2ecf20Sopenharmony_ci &total_len); 11978c2ecf20Sopenharmony_ci if (rc) 11988c2ecf20Sopenharmony_ci return rc; 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci if (sess_data->ses->binding) { 12018c2ecf20Sopenharmony_ci req->sync_hdr.SessionId = sess_data->ses->Suid; 12028c2ecf20Sopenharmony_ci req->sync_hdr.Flags |= SMB2_FLAGS_SIGNED; 12038c2ecf20Sopenharmony_ci req->PreviousSessionId = 0; 12048c2ecf20Sopenharmony_ci req->Flags = SMB2_SESSION_REQ_FLAG_BINDING; 12058c2ecf20Sopenharmony_ci } else { 12068c2ecf20Sopenharmony_ci /* First session, not a reauthenticate */ 12078c2ecf20Sopenharmony_ci req->sync_hdr.SessionId = 0; 12088c2ecf20Sopenharmony_ci /* 12098c2ecf20Sopenharmony_ci * if reconnect, we need to send previous sess id 12108c2ecf20Sopenharmony_ci * otherwise it is 0 12118c2ecf20Sopenharmony_ci */ 12128c2ecf20Sopenharmony_ci req->PreviousSessionId = sess_data->previous_session; 12138c2ecf20Sopenharmony_ci req->Flags = 0; /* MBZ */ 12148c2ecf20Sopenharmony_ci } 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci /* enough to enable echos and oplocks and one max size write */ 12178c2ecf20Sopenharmony_ci req->sync_hdr.CreditRequest = cpu_to_le16(130); 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci /* only one of SMB2 signing flags may be set in SMB2 request */ 12208c2ecf20Sopenharmony_ci if (server->sign) 12218c2ecf20Sopenharmony_ci req->SecurityMode = SMB2_NEGOTIATE_SIGNING_REQUIRED; 12228c2ecf20Sopenharmony_ci else if (global_secflags & CIFSSEC_MAY_SIGN) /* one flag unlike MUST_ */ 12238c2ecf20Sopenharmony_ci req->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED; 12248c2ecf20Sopenharmony_ci else 12258c2ecf20Sopenharmony_ci req->SecurityMode = 0; 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_DFS_UPCALL 12288c2ecf20Sopenharmony_ci req->Capabilities = cpu_to_le32(SMB2_GLOBAL_CAP_DFS); 12298c2ecf20Sopenharmony_ci#else 12308c2ecf20Sopenharmony_ci req->Capabilities = 0; 12318c2ecf20Sopenharmony_ci#endif /* DFS_UPCALL */ 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci req->Channel = 0; /* MBZ */ 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci sess_data->iov[0].iov_base = (char *)req; 12368c2ecf20Sopenharmony_ci /* 1 for pad */ 12378c2ecf20Sopenharmony_ci sess_data->iov[0].iov_len = total_len - 1; 12388c2ecf20Sopenharmony_ci /* 12398c2ecf20Sopenharmony_ci * This variable will be used to clear the buffer 12408c2ecf20Sopenharmony_ci * allocated above in case of any error in the calling function. 12418c2ecf20Sopenharmony_ci */ 12428c2ecf20Sopenharmony_ci sess_data->buf0_type = CIFS_SMALL_BUFFER; 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci return 0; 12458c2ecf20Sopenharmony_ci} 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_cistatic void 12488c2ecf20Sopenharmony_ciSMB2_sess_free_buffer(struct SMB2_sess_data *sess_data) 12498c2ecf20Sopenharmony_ci{ 12508c2ecf20Sopenharmony_ci free_rsp_buf(sess_data->buf0_type, sess_data->iov[0].iov_base); 12518c2ecf20Sopenharmony_ci sess_data->buf0_type = CIFS_NO_BUFFER; 12528c2ecf20Sopenharmony_ci} 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_cistatic int 12558c2ecf20Sopenharmony_ciSMB2_sess_sendreceive(struct SMB2_sess_data *sess_data) 12568c2ecf20Sopenharmony_ci{ 12578c2ecf20Sopenharmony_ci int rc; 12588c2ecf20Sopenharmony_ci struct smb_rqst rqst; 12598c2ecf20Sopenharmony_ci struct smb2_sess_setup_req *req = sess_data->iov[0].iov_base; 12608c2ecf20Sopenharmony_ci struct kvec rsp_iov = { NULL, 0 }; 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci /* Testing shows that buffer offset must be at location of Buffer[0] */ 12638c2ecf20Sopenharmony_ci req->SecurityBufferOffset = 12648c2ecf20Sopenharmony_ci cpu_to_le16(sizeof(struct smb2_sess_setup_req)); 12658c2ecf20Sopenharmony_ci req->SecurityBufferLength = cpu_to_le16(sess_data->iov[1].iov_len); 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 12688c2ecf20Sopenharmony_ci rqst.rq_iov = sess_data->iov; 12698c2ecf20Sopenharmony_ci rqst.rq_nvec = 2; 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci /* BB add code to build os and lm fields */ 12728c2ecf20Sopenharmony_ci rc = cifs_send_recv(sess_data->xid, sess_data->ses, 12738c2ecf20Sopenharmony_ci cifs_ses_server(sess_data->ses), 12748c2ecf20Sopenharmony_ci &rqst, 12758c2ecf20Sopenharmony_ci &sess_data->buf0_type, 12768c2ecf20Sopenharmony_ci CIFS_LOG_ERROR | CIFS_NEG_OP, &rsp_iov); 12778c2ecf20Sopenharmony_ci cifs_small_buf_release(sess_data->iov[0].iov_base); 12788c2ecf20Sopenharmony_ci memcpy(&sess_data->iov[0], &rsp_iov, sizeof(struct kvec)); 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci return rc; 12818c2ecf20Sopenharmony_ci} 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_cistatic int 12848c2ecf20Sopenharmony_ciSMB2_sess_establish_session(struct SMB2_sess_data *sess_data) 12858c2ecf20Sopenharmony_ci{ 12868c2ecf20Sopenharmony_ci int rc = 0; 12878c2ecf20Sopenharmony_ci struct cifs_ses *ses = sess_data->ses; 12888c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = cifs_ses_server(ses); 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci mutex_lock(&server->srv_mutex); 12918c2ecf20Sopenharmony_ci if (server->ops->generate_signingkey) { 12928c2ecf20Sopenharmony_ci rc = server->ops->generate_signingkey(ses); 12938c2ecf20Sopenharmony_ci if (rc) { 12948c2ecf20Sopenharmony_ci cifs_dbg(FYI, 12958c2ecf20Sopenharmony_ci "SMB3 session key generation failed\n"); 12968c2ecf20Sopenharmony_ci mutex_unlock(&server->srv_mutex); 12978c2ecf20Sopenharmony_ci return rc; 12988c2ecf20Sopenharmony_ci } 12998c2ecf20Sopenharmony_ci } 13008c2ecf20Sopenharmony_ci if (!server->session_estab) { 13018c2ecf20Sopenharmony_ci server->sequence_number = 0x2; 13028c2ecf20Sopenharmony_ci server->session_estab = true; 13038c2ecf20Sopenharmony_ci } 13048c2ecf20Sopenharmony_ci mutex_unlock(&server->srv_mutex); 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ci cifs_dbg(FYI, "SMB2/3 session established successfully\n"); 13078c2ecf20Sopenharmony_ci /* keep existing ses state if binding */ 13088c2ecf20Sopenharmony_ci if (!ses->binding) { 13098c2ecf20Sopenharmony_ci spin_lock(&GlobalMid_Lock); 13108c2ecf20Sopenharmony_ci ses->status = CifsGood; 13118c2ecf20Sopenharmony_ci ses->need_reconnect = false; 13128c2ecf20Sopenharmony_ci spin_unlock(&GlobalMid_Lock); 13138c2ecf20Sopenharmony_ci } 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci return rc; 13168c2ecf20Sopenharmony_ci} 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_UPCALL 13198c2ecf20Sopenharmony_cistatic void 13208c2ecf20Sopenharmony_ciSMB2_auth_kerberos(struct SMB2_sess_data *sess_data) 13218c2ecf20Sopenharmony_ci{ 13228c2ecf20Sopenharmony_ci int rc; 13238c2ecf20Sopenharmony_ci struct cifs_ses *ses = sess_data->ses; 13248c2ecf20Sopenharmony_ci struct cifs_spnego_msg *msg; 13258c2ecf20Sopenharmony_ci struct key *spnego_key = NULL; 13268c2ecf20Sopenharmony_ci struct smb2_sess_setup_rsp *rsp = NULL; 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci rc = SMB2_sess_alloc_buffer(sess_data); 13298c2ecf20Sopenharmony_ci if (rc) 13308c2ecf20Sopenharmony_ci goto out; 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci spnego_key = cifs_get_spnego_key(ses); 13338c2ecf20Sopenharmony_ci if (IS_ERR(spnego_key)) { 13348c2ecf20Sopenharmony_ci rc = PTR_ERR(spnego_key); 13358c2ecf20Sopenharmony_ci if (rc == -ENOKEY) 13368c2ecf20Sopenharmony_ci cifs_dbg(VFS, "Verify user has a krb5 ticket and keyutils is installed\n"); 13378c2ecf20Sopenharmony_ci spnego_key = NULL; 13388c2ecf20Sopenharmony_ci goto out; 13398c2ecf20Sopenharmony_ci } 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci msg = spnego_key->payload.data[0]; 13428c2ecf20Sopenharmony_ci /* 13438c2ecf20Sopenharmony_ci * check version field to make sure that cifs.upcall is 13448c2ecf20Sopenharmony_ci * sending us a response in an expected form 13458c2ecf20Sopenharmony_ci */ 13468c2ecf20Sopenharmony_ci if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) { 13478c2ecf20Sopenharmony_ci cifs_dbg(VFS, "bad cifs.upcall version. Expected %d got %d\n", 13488c2ecf20Sopenharmony_ci CIFS_SPNEGO_UPCALL_VERSION, msg->version); 13498c2ecf20Sopenharmony_ci rc = -EKEYREJECTED; 13508c2ecf20Sopenharmony_ci goto out_put_spnego_key; 13518c2ecf20Sopenharmony_ci } 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci /* keep session key if binding */ 13548c2ecf20Sopenharmony_ci if (!ses->binding) { 13558c2ecf20Sopenharmony_ci ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len, 13568c2ecf20Sopenharmony_ci GFP_KERNEL); 13578c2ecf20Sopenharmony_ci if (!ses->auth_key.response) { 13588c2ecf20Sopenharmony_ci cifs_dbg(VFS, "Kerberos can't allocate (%u bytes) memory\n", 13598c2ecf20Sopenharmony_ci msg->sesskey_len); 13608c2ecf20Sopenharmony_ci rc = -ENOMEM; 13618c2ecf20Sopenharmony_ci goto out_put_spnego_key; 13628c2ecf20Sopenharmony_ci } 13638c2ecf20Sopenharmony_ci ses->auth_key.len = msg->sesskey_len; 13648c2ecf20Sopenharmony_ci } 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci sess_data->iov[1].iov_base = msg->data + msg->sesskey_len; 13678c2ecf20Sopenharmony_ci sess_data->iov[1].iov_len = msg->secblob_len; 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci rc = SMB2_sess_sendreceive(sess_data); 13708c2ecf20Sopenharmony_ci if (rc) 13718c2ecf20Sopenharmony_ci goto out_put_spnego_key; 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; 13748c2ecf20Sopenharmony_ci /* keep session id and flags if binding */ 13758c2ecf20Sopenharmony_ci if (!ses->binding) { 13768c2ecf20Sopenharmony_ci ses->Suid = rsp->sync_hdr.SessionId; 13778c2ecf20Sopenharmony_ci ses->session_flags = le16_to_cpu(rsp->SessionFlags); 13788c2ecf20Sopenharmony_ci } 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci rc = SMB2_sess_establish_session(sess_data); 13818c2ecf20Sopenharmony_ciout_put_spnego_key: 13828c2ecf20Sopenharmony_ci key_invalidate(spnego_key); 13838c2ecf20Sopenharmony_ci key_put(spnego_key); 13848c2ecf20Sopenharmony_ciout: 13858c2ecf20Sopenharmony_ci sess_data->result = rc; 13868c2ecf20Sopenharmony_ci sess_data->func = NULL; 13878c2ecf20Sopenharmony_ci SMB2_sess_free_buffer(sess_data); 13888c2ecf20Sopenharmony_ci} 13898c2ecf20Sopenharmony_ci#else 13908c2ecf20Sopenharmony_cistatic void 13918c2ecf20Sopenharmony_ciSMB2_auth_kerberos(struct SMB2_sess_data *sess_data) 13928c2ecf20Sopenharmony_ci{ 13938c2ecf20Sopenharmony_ci cifs_dbg(VFS, "Kerberos negotiated but upcall support disabled!\n"); 13948c2ecf20Sopenharmony_ci sess_data->result = -EOPNOTSUPP; 13958c2ecf20Sopenharmony_ci sess_data->func = NULL; 13968c2ecf20Sopenharmony_ci} 13978c2ecf20Sopenharmony_ci#endif 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_cistatic void 14008c2ecf20Sopenharmony_ciSMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data); 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_cistatic void 14038c2ecf20Sopenharmony_ciSMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data) 14048c2ecf20Sopenharmony_ci{ 14058c2ecf20Sopenharmony_ci int rc; 14068c2ecf20Sopenharmony_ci struct cifs_ses *ses = sess_data->ses; 14078c2ecf20Sopenharmony_ci struct smb2_sess_setup_rsp *rsp = NULL; 14088c2ecf20Sopenharmony_ci char *ntlmssp_blob = NULL; 14098c2ecf20Sopenharmony_ci bool use_spnego = false; /* else use raw ntlmssp */ 14108c2ecf20Sopenharmony_ci u16 blob_length = 0; 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci /* 14138c2ecf20Sopenharmony_ci * If memory allocation is successful, caller of this function 14148c2ecf20Sopenharmony_ci * frees it. 14158c2ecf20Sopenharmony_ci */ 14168c2ecf20Sopenharmony_ci ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL); 14178c2ecf20Sopenharmony_ci if (!ses->ntlmssp) { 14188c2ecf20Sopenharmony_ci rc = -ENOMEM; 14198c2ecf20Sopenharmony_ci goto out_err; 14208c2ecf20Sopenharmony_ci } 14218c2ecf20Sopenharmony_ci ses->ntlmssp->sesskey_per_smbsess = true; 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci rc = SMB2_sess_alloc_buffer(sess_data); 14248c2ecf20Sopenharmony_ci if (rc) 14258c2ecf20Sopenharmony_ci goto out_err; 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_ci ntlmssp_blob = kmalloc(sizeof(struct _NEGOTIATE_MESSAGE), 14288c2ecf20Sopenharmony_ci GFP_KERNEL); 14298c2ecf20Sopenharmony_ci if (ntlmssp_blob == NULL) { 14308c2ecf20Sopenharmony_ci rc = -ENOMEM; 14318c2ecf20Sopenharmony_ci goto out; 14328c2ecf20Sopenharmony_ci } 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci build_ntlmssp_negotiate_blob(ntlmssp_blob, ses); 14358c2ecf20Sopenharmony_ci if (use_spnego) { 14368c2ecf20Sopenharmony_ci /* BB eventually need to add this */ 14378c2ecf20Sopenharmony_ci cifs_dbg(VFS, "spnego not supported for SMB2 yet\n"); 14388c2ecf20Sopenharmony_ci rc = -EOPNOTSUPP; 14398c2ecf20Sopenharmony_ci goto out; 14408c2ecf20Sopenharmony_ci } else { 14418c2ecf20Sopenharmony_ci blob_length = sizeof(struct _NEGOTIATE_MESSAGE); 14428c2ecf20Sopenharmony_ci /* with raw NTLMSSP we don't encapsulate in SPNEGO */ 14438c2ecf20Sopenharmony_ci } 14448c2ecf20Sopenharmony_ci sess_data->iov[1].iov_base = ntlmssp_blob; 14458c2ecf20Sopenharmony_ci sess_data->iov[1].iov_len = blob_length; 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci rc = SMB2_sess_sendreceive(sess_data); 14488c2ecf20Sopenharmony_ci rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci /* If true, rc here is expected and not an error */ 14518c2ecf20Sopenharmony_ci if (sess_data->buf0_type != CIFS_NO_BUFFER && 14528c2ecf20Sopenharmony_ci rsp->sync_hdr.Status == STATUS_MORE_PROCESSING_REQUIRED) 14538c2ecf20Sopenharmony_ci rc = 0; 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_ci if (rc) 14568c2ecf20Sopenharmony_ci goto out; 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_ci if (offsetof(struct smb2_sess_setup_rsp, Buffer) != 14598c2ecf20Sopenharmony_ci le16_to_cpu(rsp->SecurityBufferOffset)) { 14608c2ecf20Sopenharmony_ci cifs_dbg(VFS, "Invalid security buffer offset %d\n", 14618c2ecf20Sopenharmony_ci le16_to_cpu(rsp->SecurityBufferOffset)); 14628c2ecf20Sopenharmony_ci rc = -EIO; 14638c2ecf20Sopenharmony_ci goto out; 14648c2ecf20Sopenharmony_ci } 14658c2ecf20Sopenharmony_ci rc = decode_ntlmssp_challenge(rsp->Buffer, 14668c2ecf20Sopenharmony_ci le16_to_cpu(rsp->SecurityBufferLength), ses); 14678c2ecf20Sopenharmony_ci if (rc) 14688c2ecf20Sopenharmony_ci goto out; 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n"); 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ci /* keep existing ses id and flags if binding */ 14738c2ecf20Sopenharmony_ci if (!ses->binding) { 14748c2ecf20Sopenharmony_ci ses->Suid = rsp->sync_hdr.SessionId; 14758c2ecf20Sopenharmony_ci ses->session_flags = le16_to_cpu(rsp->SessionFlags); 14768c2ecf20Sopenharmony_ci } 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ciout: 14798c2ecf20Sopenharmony_ci kfree(ntlmssp_blob); 14808c2ecf20Sopenharmony_ci SMB2_sess_free_buffer(sess_data); 14818c2ecf20Sopenharmony_ci if (!rc) { 14828c2ecf20Sopenharmony_ci sess_data->result = 0; 14838c2ecf20Sopenharmony_ci sess_data->func = SMB2_sess_auth_rawntlmssp_authenticate; 14848c2ecf20Sopenharmony_ci return; 14858c2ecf20Sopenharmony_ci } 14868c2ecf20Sopenharmony_ciout_err: 14878c2ecf20Sopenharmony_ci kfree(ses->ntlmssp); 14888c2ecf20Sopenharmony_ci ses->ntlmssp = NULL; 14898c2ecf20Sopenharmony_ci sess_data->result = rc; 14908c2ecf20Sopenharmony_ci sess_data->func = NULL; 14918c2ecf20Sopenharmony_ci} 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_cistatic void 14948c2ecf20Sopenharmony_ciSMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data) 14958c2ecf20Sopenharmony_ci{ 14968c2ecf20Sopenharmony_ci int rc; 14978c2ecf20Sopenharmony_ci struct cifs_ses *ses = sess_data->ses; 14988c2ecf20Sopenharmony_ci struct smb2_sess_setup_req *req; 14998c2ecf20Sopenharmony_ci struct smb2_sess_setup_rsp *rsp = NULL; 15008c2ecf20Sopenharmony_ci unsigned char *ntlmssp_blob = NULL; 15018c2ecf20Sopenharmony_ci bool use_spnego = false; /* else use raw ntlmssp */ 15028c2ecf20Sopenharmony_ci u16 blob_length = 0; 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci rc = SMB2_sess_alloc_buffer(sess_data); 15058c2ecf20Sopenharmony_ci if (rc) 15068c2ecf20Sopenharmony_ci goto out; 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci req = (struct smb2_sess_setup_req *) sess_data->iov[0].iov_base; 15098c2ecf20Sopenharmony_ci req->sync_hdr.SessionId = ses->Suid; 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_ci rc = build_ntlmssp_auth_blob(&ntlmssp_blob, &blob_length, ses, 15128c2ecf20Sopenharmony_ci sess_data->nls_cp); 15138c2ecf20Sopenharmony_ci if (rc) { 15148c2ecf20Sopenharmony_ci cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n", rc); 15158c2ecf20Sopenharmony_ci goto out; 15168c2ecf20Sopenharmony_ci } 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci if (use_spnego) { 15198c2ecf20Sopenharmony_ci /* BB eventually need to add this */ 15208c2ecf20Sopenharmony_ci cifs_dbg(VFS, "spnego not supported for SMB2 yet\n"); 15218c2ecf20Sopenharmony_ci rc = -EOPNOTSUPP; 15228c2ecf20Sopenharmony_ci goto out; 15238c2ecf20Sopenharmony_ci } 15248c2ecf20Sopenharmony_ci sess_data->iov[1].iov_base = ntlmssp_blob; 15258c2ecf20Sopenharmony_ci sess_data->iov[1].iov_len = blob_length; 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ci rc = SMB2_sess_sendreceive(sess_data); 15288c2ecf20Sopenharmony_ci if (rc) 15298c2ecf20Sopenharmony_ci goto out; 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ci /* keep existing ses id and flags if binding */ 15348c2ecf20Sopenharmony_ci if (!ses->binding) { 15358c2ecf20Sopenharmony_ci ses->Suid = rsp->sync_hdr.SessionId; 15368c2ecf20Sopenharmony_ci ses->session_flags = le16_to_cpu(rsp->SessionFlags); 15378c2ecf20Sopenharmony_ci } 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ci rc = SMB2_sess_establish_session(sess_data); 15408c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_DEBUG_DUMP_KEYS 15418c2ecf20Sopenharmony_ci if (ses->server->dialect < SMB30_PROT_ID) { 15428c2ecf20Sopenharmony_ci cifs_dbg(VFS, "%s: dumping generated SMB2 session keys\n", __func__); 15438c2ecf20Sopenharmony_ci /* 15448c2ecf20Sopenharmony_ci * The session id is opaque in terms of endianness, so we can't 15458c2ecf20Sopenharmony_ci * print it as a long long. we dump it as we got it on the wire 15468c2ecf20Sopenharmony_ci */ 15478c2ecf20Sopenharmony_ci cifs_dbg(VFS, "Session Id %*ph\n", (int)sizeof(ses->Suid), 15488c2ecf20Sopenharmony_ci &ses->Suid); 15498c2ecf20Sopenharmony_ci cifs_dbg(VFS, "Session Key %*ph\n", 15508c2ecf20Sopenharmony_ci SMB2_NTLMV2_SESSKEY_SIZE, ses->auth_key.response); 15518c2ecf20Sopenharmony_ci cifs_dbg(VFS, "Signing Key %*ph\n", 15528c2ecf20Sopenharmony_ci SMB3_SIGN_KEY_SIZE, ses->auth_key.response); 15538c2ecf20Sopenharmony_ci } 15548c2ecf20Sopenharmony_ci#endif 15558c2ecf20Sopenharmony_ciout: 15568c2ecf20Sopenharmony_ci kfree(ntlmssp_blob); 15578c2ecf20Sopenharmony_ci SMB2_sess_free_buffer(sess_data); 15588c2ecf20Sopenharmony_ci kfree(ses->ntlmssp); 15598c2ecf20Sopenharmony_ci ses->ntlmssp = NULL; 15608c2ecf20Sopenharmony_ci sess_data->result = rc; 15618c2ecf20Sopenharmony_ci sess_data->func = NULL; 15628c2ecf20Sopenharmony_ci} 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_cistatic int 15658c2ecf20Sopenharmony_ciSMB2_select_sec(struct cifs_ses *ses, struct SMB2_sess_data *sess_data) 15668c2ecf20Sopenharmony_ci{ 15678c2ecf20Sopenharmony_ci int type; 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci type = smb2_select_sectype(cifs_ses_server(ses), ses->sectype); 15708c2ecf20Sopenharmony_ci cifs_dbg(FYI, "sess setup type %d\n", type); 15718c2ecf20Sopenharmony_ci if (type == Unspecified) { 15728c2ecf20Sopenharmony_ci cifs_dbg(VFS, "Unable to select appropriate authentication method!\n"); 15738c2ecf20Sopenharmony_ci return -EINVAL; 15748c2ecf20Sopenharmony_ci } 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci switch (type) { 15778c2ecf20Sopenharmony_ci case Kerberos: 15788c2ecf20Sopenharmony_ci sess_data->func = SMB2_auth_kerberos; 15798c2ecf20Sopenharmony_ci break; 15808c2ecf20Sopenharmony_ci case RawNTLMSSP: 15818c2ecf20Sopenharmony_ci sess_data->func = SMB2_sess_auth_rawntlmssp_negotiate; 15828c2ecf20Sopenharmony_ci break; 15838c2ecf20Sopenharmony_ci default: 15848c2ecf20Sopenharmony_ci cifs_dbg(VFS, "secType %d not supported!\n", type); 15858c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 15868c2ecf20Sopenharmony_ci } 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci return 0; 15898c2ecf20Sopenharmony_ci} 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_ciint 15928c2ecf20Sopenharmony_ciSMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, 15938c2ecf20Sopenharmony_ci const struct nls_table *nls_cp) 15948c2ecf20Sopenharmony_ci{ 15958c2ecf20Sopenharmony_ci int rc = 0; 15968c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = cifs_ses_server(ses); 15978c2ecf20Sopenharmony_ci struct SMB2_sess_data *sess_data; 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Session Setup\n"); 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_ci if (!server) { 16028c2ecf20Sopenharmony_ci WARN(1, "%s: server is NULL!\n", __func__); 16038c2ecf20Sopenharmony_ci return -EIO; 16048c2ecf20Sopenharmony_ci } 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci sess_data = kzalloc(sizeof(struct SMB2_sess_data), GFP_KERNEL); 16078c2ecf20Sopenharmony_ci if (!sess_data) 16088c2ecf20Sopenharmony_ci return -ENOMEM; 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci rc = SMB2_select_sec(ses, sess_data); 16118c2ecf20Sopenharmony_ci if (rc) 16128c2ecf20Sopenharmony_ci goto out; 16138c2ecf20Sopenharmony_ci sess_data->xid = xid; 16148c2ecf20Sopenharmony_ci sess_data->ses = ses; 16158c2ecf20Sopenharmony_ci sess_data->buf0_type = CIFS_NO_BUFFER; 16168c2ecf20Sopenharmony_ci sess_data->nls_cp = (struct nls_table *) nls_cp; 16178c2ecf20Sopenharmony_ci sess_data->previous_session = ses->Suid; 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_ci /* 16208c2ecf20Sopenharmony_ci * Initialize the session hash with the server one. 16218c2ecf20Sopenharmony_ci */ 16228c2ecf20Sopenharmony_ci memcpy(ses->preauth_sha_hash, server->preauth_sha_hash, 16238c2ecf20Sopenharmony_ci SMB2_PREAUTH_HASH_SIZE); 16248c2ecf20Sopenharmony_ci 16258c2ecf20Sopenharmony_ci while (sess_data->func) 16268c2ecf20Sopenharmony_ci sess_data->func(sess_data); 16278c2ecf20Sopenharmony_ci 16288c2ecf20Sopenharmony_ci if ((ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST) && (ses->sign)) 16298c2ecf20Sopenharmony_ci cifs_server_dbg(VFS, "signing requested but authenticated as guest\n"); 16308c2ecf20Sopenharmony_ci rc = sess_data->result; 16318c2ecf20Sopenharmony_ciout: 16328c2ecf20Sopenharmony_ci kfree(sess_data); 16338c2ecf20Sopenharmony_ci return rc; 16348c2ecf20Sopenharmony_ci} 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_ciint 16378c2ecf20Sopenharmony_ciSMB2_logoff(const unsigned int xid, struct cifs_ses *ses) 16388c2ecf20Sopenharmony_ci{ 16398c2ecf20Sopenharmony_ci struct smb_rqst rqst; 16408c2ecf20Sopenharmony_ci struct smb2_logoff_req *req; /* response is also trivial struct */ 16418c2ecf20Sopenharmony_ci int rc = 0; 16428c2ecf20Sopenharmony_ci struct TCP_Server_Info *server; 16438c2ecf20Sopenharmony_ci int flags = 0; 16448c2ecf20Sopenharmony_ci unsigned int total_len; 16458c2ecf20Sopenharmony_ci struct kvec iov[1]; 16468c2ecf20Sopenharmony_ci struct kvec rsp_iov; 16478c2ecf20Sopenharmony_ci int resp_buf_type; 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci cifs_dbg(FYI, "disconnect session %p\n", ses); 16508c2ecf20Sopenharmony_ci 16518c2ecf20Sopenharmony_ci if (ses && (ses->server)) 16528c2ecf20Sopenharmony_ci server = ses->server; 16538c2ecf20Sopenharmony_ci else 16548c2ecf20Sopenharmony_ci return -EIO; 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_ci /* no need to send SMB logoff if uid already closed due to reconnect */ 16578c2ecf20Sopenharmony_ci if (ses->need_reconnect) 16588c2ecf20Sopenharmony_ci goto smb2_session_already_dead; 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_ci rc = smb2_plain_req_init(SMB2_LOGOFF, NULL, ses->server, 16618c2ecf20Sopenharmony_ci (void **) &req, &total_len); 16628c2ecf20Sopenharmony_ci if (rc) 16638c2ecf20Sopenharmony_ci return rc; 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_ci /* since no tcon, smb2_init can not do this, so do here */ 16668c2ecf20Sopenharmony_ci req->sync_hdr.SessionId = ses->Suid; 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_ci if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) 16698c2ecf20Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 16708c2ecf20Sopenharmony_ci else if (server->sign) 16718c2ecf20Sopenharmony_ci req->sync_hdr.Flags |= SMB2_FLAGS_SIGNED; 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci flags |= CIFS_NO_RSP_BUF; 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci iov[0].iov_base = (char *)req; 16768c2ecf20Sopenharmony_ci iov[0].iov_len = total_len; 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 16798c2ecf20Sopenharmony_ci rqst.rq_iov = iov; 16808c2ecf20Sopenharmony_ci rqst.rq_nvec = 1; 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci rc = cifs_send_recv(xid, ses, ses->server, 16838c2ecf20Sopenharmony_ci &rqst, &resp_buf_type, flags, &rsp_iov); 16848c2ecf20Sopenharmony_ci cifs_small_buf_release(req); 16858c2ecf20Sopenharmony_ci /* 16868c2ecf20Sopenharmony_ci * No tcon so can't do 16878c2ecf20Sopenharmony_ci * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]); 16888c2ecf20Sopenharmony_ci */ 16898c2ecf20Sopenharmony_ci 16908c2ecf20Sopenharmony_cismb2_session_already_dead: 16918c2ecf20Sopenharmony_ci return rc; 16928c2ecf20Sopenharmony_ci} 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_cistatic inline void cifs_stats_fail_inc(struct cifs_tcon *tcon, uint16_t code) 16958c2ecf20Sopenharmony_ci{ 16968c2ecf20Sopenharmony_ci cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_failed[code]); 16978c2ecf20Sopenharmony_ci} 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_ci#define MAX_SHARENAME_LENGTH (255 /* server */ + 80 /* share */ + 1 /* NULL */) 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_ci/* These are similar values to what Windows uses */ 17028c2ecf20Sopenharmony_cistatic inline void init_copy_chunk_defaults(struct cifs_tcon *tcon) 17038c2ecf20Sopenharmony_ci{ 17048c2ecf20Sopenharmony_ci tcon->max_chunks = 256; 17058c2ecf20Sopenharmony_ci tcon->max_bytes_chunk = 1048576; 17068c2ecf20Sopenharmony_ci tcon->max_bytes_copy = 16777216; 17078c2ecf20Sopenharmony_ci} 17088c2ecf20Sopenharmony_ci 17098c2ecf20Sopenharmony_ciint 17108c2ecf20Sopenharmony_ciSMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, 17118c2ecf20Sopenharmony_ci struct cifs_tcon *tcon, const struct nls_table *cp) 17128c2ecf20Sopenharmony_ci{ 17138c2ecf20Sopenharmony_ci struct smb_rqst rqst; 17148c2ecf20Sopenharmony_ci struct smb2_tree_connect_req *req; 17158c2ecf20Sopenharmony_ci struct smb2_tree_connect_rsp *rsp = NULL; 17168c2ecf20Sopenharmony_ci struct kvec iov[2]; 17178c2ecf20Sopenharmony_ci struct kvec rsp_iov = { NULL, 0 }; 17188c2ecf20Sopenharmony_ci int rc = 0; 17198c2ecf20Sopenharmony_ci int resp_buftype; 17208c2ecf20Sopenharmony_ci int unc_path_len; 17218c2ecf20Sopenharmony_ci __le16 *unc_path = NULL; 17228c2ecf20Sopenharmony_ci int flags = 0; 17238c2ecf20Sopenharmony_ci unsigned int total_len; 17248c2ecf20Sopenharmony_ci struct TCP_Server_Info *server; 17258c2ecf20Sopenharmony_ci 17268c2ecf20Sopenharmony_ci /* always use master channel */ 17278c2ecf20Sopenharmony_ci server = ses->server; 17288c2ecf20Sopenharmony_ci 17298c2ecf20Sopenharmony_ci cifs_dbg(FYI, "TCON\n"); 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci if (!server || !tree) 17328c2ecf20Sopenharmony_ci return -EIO; 17338c2ecf20Sopenharmony_ci 17348c2ecf20Sopenharmony_ci unc_path = kmalloc(MAX_SHARENAME_LENGTH * 2, GFP_KERNEL); 17358c2ecf20Sopenharmony_ci if (unc_path == NULL) 17368c2ecf20Sopenharmony_ci return -ENOMEM; 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_ci unc_path_len = cifs_strtoUTF16(unc_path, tree, strlen(tree), cp) + 1; 17398c2ecf20Sopenharmony_ci unc_path_len *= 2; 17408c2ecf20Sopenharmony_ci if (unc_path_len < 2) { 17418c2ecf20Sopenharmony_ci kfree(unc_path); 17428c2ecf20Sopenharmony_ci return -EINVAL; 17438c2ecf20Sopenharmony_ci } 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_ci /* SMB2 TREE_CONNECT request must be called with TreeId == 0 */ 17468c2ecf20Sopenharmony_ci tcon->tid = 0; 17478c2ecf20Sopenharmony_ci atomic_set(&tcon->num_remote_opens, 0); 17488c2ecf20Sopenharmony_ci rc = smb2_plain_req_init(SMB2_TREE_CONNECT, tcon, server, 17498c2ecf20Sopenharmony_ci (void **) &req, &total_len); 17508c2ecf20Sopenharmony_ci if (rc) { 17518c2ecf20Sopenharmony_ci kfree(unc_path); 17528c2ecf20Sopenharmony_ci return rc; 17538c2ecf20Sopenharmony_ci } 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci if (smb3_encryption_required(tcon)) 17568c2ecf20Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 17578c2ecf20Sopenharmony_ci 17588c2ecf20Sopenharmony_ci iov[0].iov_base = (char *)req; 17598c2ecf20Sopenharmony_ci /* 1 for pad */ 17608c2ecf20Sopenharmony_ci iov[0].iov_len = total_len - 1; 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci /* Testing shows that buffer offset must be at location of Buffer[0] */ 17638c2ecf20Sopenharmony_ci req->PathOffset = cpu_to_le16(sizeof(struct smb2_tree_connect_req)); 17648c2ecf20Sopenharmony_ci req->PathLength = cpu_to_le16(unc_path_len - 2); 17658c2ecf20Sopenharmony_ci iov[1].iov_base = unc_path; 17668c2ecf20Sopenharmony_ci iov[1].iov_len = unc_path_len; 17678c2ecf20Sopenharmony_ci 17688c2ecf20Sopenharmony_ci /* 17698c2ecf20Sopenharmony_ci * 3.11 tcon req must be signed if not encrypted. See MS-SMB2 3.2.4.1.1 17708c2ecf20Sopenharmony_ci * unless it is guest or anonymous user. See MS-SMB2 3.2.5.3.1 17718c2ecf20Sopenharmony_ci * (Samba servers don't always set the flag so also check if null user) 17728c2ecf20Sopenharmony_ci */ 17738c2ecf20Sopenharmony_ci if ((server->dialect == SMB311_PROT_ID) && 17748c2ecf20Sopenharmony_ci !smb3_encryption_required(tcon) && 17758c2ecf20Sopenharmony_ci !(ses->session_flags & 17768c2ecf20Sopenharmony_ci (SMB2_SESSION_FLAG_IS_GUEST|SMB2_SESSION_FLAG_IS_NULL)) && 17778c2ecf20Sopenharmony_ci ((ses->user_name != NULL) || (ses->sectype == Kerberos))) 17788c2ecf20Sopenharmony_ci req->sync_hdr.Flags |= SMB2_FLAGS_SIGNED; 17798c2ecf20Sopenharmony_ci 17808c2ecf20Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 17818c2ecf20Sopenharmony_ci rqst.rq_iov = iov; 17828c2ecf20Sopenharmony_ci rqst.rq_nvec = 2; 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_ci /* Need 64 for max size write so ask for more in case not there yet */ 17858c2ecf20Sopenharmony_ci req->sync_hdr.CreditRequest = cpu_to_le16(64); 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_ci rc = cifs_send_recv(xid, ses, server, 17888c2ecf20Sopenharmony_ci &rqst, &resp_buftype, flags, &rsp_iov); 17898c2ecf20Sopenharmony_ci cifs_small_buf_release(req); 17908c2ecf20Sopenharmony_ci rsp = (struct smb2_tree_connect_rsp *)rsp_iov.iov_base; 17918c2ecf20Sopenharmony_ci trace_smb3_tcon(xid, tcon->tid, ses->Suid, tree, rc); 17928c2ecf20Sopenharmony_ci if (rc != 0) { 17938c2ecf20Sopenharmony_ci if (tcon) { 17948c2ecf20Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_TREE_CONNECT_HE); 17958c2ecf20Sopenharmony_ci tcon->need_reconnect = true; 17968c2ecf20Sopenharmony_ci } 17978c2ecf20Sopenharmony_ci goto tcon_error_exit; 17988c2ecf20Sopenharmony_ci } 17998c2ecf20Sopenharmony_ci 18008c2ecf20Sopenharmony_ci switch (rsp->ShareType) { 18018c2ecf20Sopenharmony_ci case SMB2_SHARE_TYPE_DISK: 18028c2ecf20Sopenharmony_ci cifs_dbg(FYI, "connection to disk share\n"); 18038c2ecf20Sopenharmony_ci break; 18048c2ecf20Sopenharmony_ci case SMB2_SHARE_TYPE_PIPE: 18058c2ecf20Sopenharmony_ci tcon->pipe = true; 18068c2ecf20Sopenharmony_ci cifs_dbg(FYI, "connection to pipe share\n"); 18078c2ecf20Sopenharmony_ci break; 18088c2ecf20Sopenharmony_ci case SMB2_SHARE_TYPE_PRINT: 18098c2ecf20Sopenharmony_ci tcon->print = true; 18108c2ecf20Sopenharmony_ci cifs_dbg(FYI, "connection to printer\n"); 18118c2ecf20Sopenharmony_ci break; 18128c2ecf20Sopenharmony_ci default: 18138c2ecf20Sopenharmony_ci cifs_server_dbg(VFS, "unknown share type %d\n", rsp->ShareType); 18148c2ecf20Sopenharmony_ci rc = -EOPNOTSUPP; 18158c2ecf20Sopenharmony_ci goto tcon_error_exit; 18168c2ecf20Sopenharmony_ci } 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci tcon->share_flags = le32_to_cpu(rsp->ShareFlags); 18198c2ecf20Sopenharmony_ci tcon->capabilities = rsp->Capabilities; /* we keep caps little endian */ 18208c2ecf20Sopenharmony_ci tcon->maximal_access = le32_to_cpu(rsp->MaximalAccess); 18218c2ecf20Sopenharmony_ci tcon->tidStatus = CifsGood; 18228c2ecf20Sopenharmony_ci tcon->need_reconnect = false; 18238c2ecf20Sopenharmony_ci tcon->tid = rsp->sync_hdr.TreeId; 18248c2ecf20Sopenharmony_ci strlcpy(tcon->treeName, tree, sizeof(tcon->treeName)); 18258c2ecf20Sopenharmony_ci 18268c2ecf20Sopenharmony_ci if ((rsp->Capabilities & SMB2_SHARE_CAP_DFS) && 18278c2ecf20Sopenharmony_ci ((tcon->share_flags & SHI1005_FLAGS_DFS) == 0)) 18288c2ecf20Sopenharmony_ci cifs_tcon_dbg(VFS, "DFS capability contradicts DFS flag\n"); 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci if (tcon->seal && 18318c2ecf20Sopenharmony_ci !(server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) 18328c2ecf20Sopenharmony_ci cifs_tcon_dbg(VFS, "Encryption is requested but not supported\n"); 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_ci init_copy_chunk_defaults(tcon); 18358c2ecf20Sopenharmony_ci if (server->ops->validate_negotiate) 18368c2ecf20Sopenharmony_ci rc = server->ops->validate_negotiate(xid, tcon); 18378c2ecf20Sopenharmony_citcon_exit: 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype, rsp); 18408c2ecf20Sopenharmony_ci kfree(unc_path); 18418c2ecf20Sopenharmony_ci return rc; 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_citcon_error_exit: 18448c2ecf20Sopenharmony_ci if (rsp && rsp->sync_hdr.Status == STATUS_BAD_NETWORK_NAME) { 18458c2ecf20Sopenharmony_ci cifs_tcon_dbg(VFS, "BAD_NETWORK_NAME: %s\n", tree); 18468c2ecf20Sopenharmony_ci } 18478c2ecf20Sopenharmony_ci goto tcon_exit; 18488c2ecf20Sopenharmony_ci} 18498c2ecf20Sopenharmony_ci 18508c2ecf20Sopenharmony_ciint 18518c2ecf20Sopenharmony_ciSMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon) 18528c2ecf20Sopenharmony_ci{ 18538c2ecf20Sopenharmony_ci struct smb_rqst rqst; 18548c2ecf20Sopenharmony_ci struct smb2_tree_disconnect_req *req; /* response is trivial */ 18558c2ecf20Sopenharmony_ci int rc = 0; 18568c2ecf20Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 18578c2ecf20Sopenharmony_ci int flags = 0; 18588c2ecf20Sopenharmony_ci unsigned int total_len; 18598c2ecf20Sopenharmony_ci struct kvec iov[1]; 18608c2ecf20Sopenharmony_ci struct kvec rsp_iov; 18618c2ecf20Sopenharmony_ci int resp_buf_type; 18628c2ecf20Sopenharmony_ci 18638c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Tree Disconnect\n"); 18648c2ecf20Sopenharmony_ci 18658c2ecf20Sopenharmony_ci if (!ses || !(ses->server)) 18668c2ecf20Sopenharmony_ci return -EIO; 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_ci if ((tcon->need_reconnect) || (tcon->ses->need_reconnect)) 18698c2ecf20Sopenharmony_ci return 0; 18708c2ecf20Sopenharmony_ci 18718c2ecf20Sopenharmony_ci close_shroot_lease(&tcon->crfid); 18728c2ecf20Sopenharmony_ci 18738c2ecf20Sopenharmony_ci rc = smb2_plain_req_init(SMB2_TREE_DISCONNECT, tcon, ses->server, 18748c2ecf20Sopenharmony_ci (void **) &req, 18758c2ecf20Sopenharmony_ci &total_len); 18768c2ecf20Sopenharmony_ci if (rc) 18778c2ecf20Sopenharmony_ci return rc; 18788c2ecf20Sopenharmony_ci 18798c2ecf20Sopenharmony_ci if (smb3_encryption_required(tcon)) 18808c2ecf20Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 18818c2ecf20Sopenharmony_ci 18828c2ecf20Sopenharmony_ci flags |= CIFS_NO_RSP_BUF; 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_ci iov[0].iov_base = (char *)req; 18858c2ecf20Sopenharmony_ci iov[0].iov_len = total_len; 18868c2ecf20Sopenharmony_ci 18878c2ecf20Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 18888c2ecf20Sopenharmony_ci rqst.rq_iov = iov; 18898c2ecf20Sopenharmony_ci rqst.rq_nvec = 1; 18908c2ecf20Sopenharmony_ci 18918c2ecf20Sopenharmony_ci rc = cifs_send_recv(xid, ses, ses->server, 18928c2ecf20Sopenharmony_ci &rqst, &resp_buf_type, flags, &rsp_iov); 18938c2ecf20Sopenharmony_ci cifs_small_buf_release(req); 18948c2ecf20Sopenharmony_ci if (rc) 18958c2ecf20Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_TREE_DISCONNECT_HE); 18968c2ecf20Sopenharmony_ci 18978c2ecf20Sopenharmony_ci return rc; 18988c2ecf20Sopenharmony_ci} 18998c2ecf20Sopenharmony_ci 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_cistatic struct create_durable * 19028c2ecf20Sopenharmony_cicreate_durable_buf(void) 19038c2ecf20Sopenharmony_ci{ 19048c2ecf20Sopenharmony_ci struct create_durable *buf; 19058c2ecf20Sopenharmony_ci 19068c2ecf20Sopenharmony_ci buf = kzalloc(sizeof(struct create_durable), GFP_KERNEL); 19078c2ecf20Sopenharmony_ci if (!buf) 19088c2ecf20Sopenharmony_ci return NULL; 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_ci buf->ccontext.DataOffset = cpu_to_le16(offsetof 19118c2ecf20Sopenharmony_ci (struct create_durable, Data)); 19128c2ecf20Sopenharmony_ci buf->ccontext.DataLength = cpu_to_le32(16); 19138c2ecf20Sopenharmony_ci buf->ccontext.NameOffset = cpu_to_le16(offsetof 19148c2ecf20Sopenharmony_ci (struct create_durable, Name)); 19158c2ecf20Sopenharmony_ci buf->ccontext.NameLength = cpu_to_le16(4); 19168c2ecf20Sopenharmony_ci /* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DHnQ" */ 19178c2ecf20Sopenharmony_ci buf->Name[0] = 'D'; 19188c2ecf20Sopenharmony_ci buf->Name[1] = 'H'; 19198c2ecf20Sopenharmony_ci buf->Name[2] = 'n'; 19208c2ecf20Sopenharmony_ci buf->Name[3] = 'Q'; 19218c2ecf20Sopenharmony_ci return buf; 19228c2ecf20Sopenharmony_ci} 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_cistatic struct create_durable * 19258c2ecf20Sopenharmony_cicreate_reconnect_durable_buf(struct cifs_fid *fid) 19268c2ecf20Sopenharmony_ci{ 19278c2ecf20Sopenharmony_ci struct create_durable *buf; 19288c2ecf20Sopenharmony_ci 19298c2ecf20Sopenharmony_ci buf = kzalloc(sizeof(struct create_durable), GFP_KERNEL); 19308c2ecf20Sopenharmony_ci if (!buf) 19318c2ecf20Sopenharmony_ci return NULL; 19328c2ecf20Sopenharmony_ci 19338c2ecf20Sopenharmony_ci buf->ccontext.DataOffset = cpu_to_le16(offsetof 19348c2ecf20Sopenharmony_ci (struct create_durable, Data)); 19358c2ecf20Sopenharmony_ci buf->ccontext.DataLength = cpu_to_le32(16); 19368c2ecf20Sopenharmony_ci buf->ccontext.NameOffset = cpu_to_le16(offsetof 19378c2ecf20Sopenharmony_ci (struct create_durable, Name)); 19388c2ecf20Sopenharmony_ci buf->ccontext.NameLength = cpu_to_le16(4); 19398c2ecf20Sopenharmony_ci buf->Data.Fid.PersistentFileId = fid->persistent_fid; 19408c2ecf20Sopenharmony_ci buf->Data.Fid.VolatileFileId = fid->volatile_fid; 19418c2ecf20Sopenharmony_ci /* SMB2_CREATE_DURABLE_HANDLE_RECONNECT is "DHnC" */ 19428c2ecf20Sopenharmony_ci buf->Name[0] = 'D'; 19438c2ecf20Sopenharmony_ci buf->Name[1] = 'H'; 19448c2ecf20Sopenharmony_ci buf->Name[2] = 'n'; 19458c2ecf20Sopenharmony_ci buf->Name[3] = 'C'; 19468c2ecf20Sopenharmony_ci return buf; 19478c2ecf20Sopenharmony_ci} 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_cistatic void 19508c2ecf20Sopenharmony_ciparse_query_id_ctxt(struct create_context *cc, struct smb2_file_all_info *buf) 19518c2ecf20Sopenharmony_ci{ 19528c2ecf20Sopenharmony_ci struct create_on_disk_id *pdisk_id = (struct create_on_disk_id *)cc; 19538c2ecf20Sopenharmony_ci 19548c2ecf20Sopenharmony_ci cifs_dbg(FYI, "parse query id context 0x%llx 0x%llx\n", 19558c2ecf20Sopenharmony_ci pdisk_id->DiskFileId, pdisk_id->VolumeId); 19568c2ecf20Sopenharmony_ci buf->IndexNumber = pdisk_id->DiskFileId; 19578c2ecf20Sopenharmony_ci} 19588c2ecf20Sopenharmony_ci 19598c2ecf20Sopenharmony_cistatic void 19608c2ecf20Sopenharmony_ciparse_posix_ctxt(struct create_context *cc, struct smb2_file_all_info *info, 19618c2ecf20Sopenharmony_ci struct create_posix_rsp *posix) 19628c2ecf20Sopenharmony_ci{ 19638c2ecf20Sopenharmony_ci int sid_len; 19648c2ecf20Sopenharmony_ci u8 *beg = (u8 *)cc + le16_to_cpu(cc->DataOffset); 19658c2ecf20Sopenharmony_ci u8 *end = beg + le32_to_cpu(cc->DataLength); 19668c2ecf20Sopenharmony_ci u8 *sid; 19678c2ecf20Sopenharmony_ci 19688c2ecf20Sopenharmony_ci memset(posix, 0, sizeof(*posix)); 19698c2ecf20Sopenharmony_ci 19708c2ecf20Sopenharmony_ci posix->nlink = le32_to_cpu(*(__le32 *)(beg + 0)); 19718c2ecf20Sopenharmony_ci posix->reparse_tag = le32_to_cpu(*(__le32 *)(beg + 4)); 19728c2ecf20Sopenharmony_ci posix->mode = le32_to_cpu(*(__le32 *)(beg + 8)); 19738c2ecf20Sopenharmony_ci 19748c2ecf20Sopenharmony_ci sid = beg + 12; 19758c2ecf20Sopenharmony_ci sid_len = posix_info_sid_size(sid, end); 19768c2ecf20Sopenharmony_ci if (sid_len < 0) { 19778c2ecf20Sopenharmony_ci cifs_dbg(VFS, "bad owner sid in posix create response\n"); 19788c2ecf20Sopenharmony_ci return; 19798c2ecf20Sopenharmony_ci } 19808c2ecf20Sopenharmony_ci memcpy(&posix->owner, sid, sid_len); 19818c2ecf20Sopenharmony_ci 19828c2ecf20Sopenharmony_ci sid = sid + sid_len; 19838c2ecf20Sopenharmony_ci sid_len = posix_info_sid_size(sid, end); 19848c2ecf20Sopenharmony_ci if (sid_len < 0) { 19858c2ecf20Sopenharmony_ci cifs_dbg(VFS, "bad group sid in posix create response\n"); 19868c2ecf20Sopenharmony_ci return; 19878c2ecf20Sopenharmony_ci } 19888c2ecf20Sopenharmony_ci memcpy(&posix->group, sid, sid_len); 19898c2ecf20Sopenharmony_ci 19908c2ecf20Sopenharmony_ci cifs_dbg(FYI, "nlink=%d mode=%o reparse_tag=%x\n", 19918c2ecf20Sopenharmony_ci posix->nlink, posix->mode, posix->reparse_tag); 19928c2ecf20Sopenharmony_ci} 19938c2ecf20Sopenharmony_ci 19948c2ecf20Sopenharmony_civoid 19958c2ecf20Sopenharmony_cismb2_parse_contexts(struct TCP_Server_Info *server, 19968c2ecf20Sopenharmony_ci struct smb2_create_rsp *rsp, 19978c2ecf20Sopenharmony_ci unsigned int *epoch, char *lease_key, __u8 *oplock, 19988c2ecf20Sopenharmony_ci struct smb2_file_all_info *buf, 19998c2ecf20Sopenharmony_ci struct create_posix_rsp *posix) 20008c2ecf20Sopenharmony_ci{ 20018c2ecf20Sopenharmony_ci char *data_offset; 20028c2ecf20Sopenharmony_ci struct create_context *cc; 20038c2ecf20Sopenharmony_ci unsigned int next; 20048c2ecf20Sopenharmony_ci unsigned int remaining; 20058c2ecf20Sopenharmony_ci char *name; 20068c2ecf20Sopenharmony_ci static const char smb3_create_tag_posix[] = { 20078c2ecf20Sopenharmony_ci 0x93, 0xAD, 0x25, 0x50, 0x9C, 20088c2ecf20Sopenharmony_ci 0xB4, 0x11, 0xE7, 0xB4, 0x23, 0x83, 20098c2ecf20Sopenharmony_ci 0xDE, 0x96, 0x8B, 0xCD, 0x7C 20108c2ecf20Sopenharmony_ci }; 20118c2ecf20Sopenharmony_ci 20128c2ecf20Sopenharmony_ci *oplock = 0; 20138c2ecf20Sopenharmony_ci data_offset = (char *)rsp + le32_to_cpu(rsp->CreateContextsOffset); 20148c2ecf20Sopenharmony_ci remaining = le32_to_cpu(rsp->CreateContextsLength); 20158c2ecf20Sopenharmony_ci cc = (struct create_context *)data_offset; 20168c2ecf20Sopenharmony_ci 20178c2ecf20Sopenharmony_ci /* Initialize inode number to 0 in case no valid data in qfid context */ 20188c2ecf20Sopenharmony_ci if (buf) 20198c2ecf20Sopenharmony_ci buf->IndexNumber = 0; 20208c2ecf20Sopenharmony_ci 20218c2ecf20Sopenharmony_ci while (remaining >= sizeof(struct create_context)) { 20228c2ecf20Sopenharmony_ci name = le16_to_cpu(cc->NameOffset) + (char *)cc; 20238c2ecf20Sopenharmony_ci if (le16_to_cpu(cc->NameLength) == 4 && 20248c2ecf20Sopenharmony_ci strncmp(name, SMB2_CREATE_REQUEST_LEASE, 4) == 0) 20258c2ecf20Sopenharmony_ci *oplock = server->ops->parse_lease_buf(cc, epoch, 20268c2ecf20Sopenharmony_ci lease_key); 20278c2ecf20Sopenharmony_ci else if (buf && (le16_to_cpu(cc->NameLength) == 4) && 20288c2ecf20Sopenharmony_ci strncmp(name, SMB2_CREATE_QUERY_ON_DISK_ID, 4) == 0) 20298c2ecf20Sopenharmony_ci parse_query_id_ctxt(cc, buf); 20308c2ecf20Sopenharmony_ci else if ((le16_to_cpu(cc->NameLength) == 16)) { 20318c2ecf20Sopenharmony_ci if (posix && 20328c2ecf20Sopenharmony_ci memcmp(name, smb3_create_tag_posix, 16) == 0) 20338c2ecf20Sopenharmony_ci parse_posix_ctxt(cc, buf, posix); 20348c2ecf20Sopenharmony_ci } 20358c2ecf20Sopenharmony_ci /* else { 20368c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Context not matched with len %d\n", 20378c2ecf20Sopenharmony_ci le16_to_cpu(cc->NameLength)); 20388c2ecf20Sopenharmony_ci cifs_dump_mem("Cctxt name: ", name, 4); 20398c2ecf20Sopenharmony_ci } */ 20408c2ecf20Sopenharmony_ci 20418c2ecf20Sopenharmony_ci next = le32_to_cpu(cc->Next); 20428c2ecf20Sopenharmony_ci if (!next) 20438c2ecf20Sopenharmony_ci break; 20448c2ecf20Sopenharmony_ci remaining -= next; 20458c2ecf20Sopenharmony_ci cc = (struct create_context *)((char *)cc + next); 20468c2ecf20Sopenharmony_ci } 20478c2ecf20Sopenharmony_ci 20488c2ecf20Sopenharmony_ci if (rsp->OplockLevel != SMB2_OPLOCK_LEVEL_LEASE) 20498c2ecf20Sopenharmony_ci *oplock = rsp->OplockLevel; 20508c2ecf20Sopenharmony_ci 20518c2ecf20Sopenharmony_ci return; 20528c2ecf20Sopenharmony_ci} 20538c2ecf20Sopenharmony_ci 20548c2ecf20Sopenharmony_cistatic int 20558c2ecf20Sopenharmony_ciadd_lease_context(struct TCP_Server_Info *server, struct kvec *iov, 20568c2ecf20Sopenharmony_ci unsigned int *num_iovec, u8 *lease_key, __u8 *oplock) 20578c2ecf20Sopenharmony_ci{ 20588c2ecf20Sopenharmony_ci struct smb2_create_req *req = iov[0].iov_base; 20598c2ecf20Sopenharmony_ci unsigned int num = *num_iovec; 20608c2ecf20Sopenharmony_ci 20618c2ecf20Sopenharmony_ci iov[num].iov_base = server->ops->create_lease_buf(lease_key, *oplock); 20628c2ecf20Sopenharmony_ci if (iov[num].iov_base == NULL) 20638c2ecf20Sopenharmony_ci return -ENOMEM; 20648c2ecf20Sopenharmony_ci iov[num].iov_len = server->vals->create_lease_size; 20658c2ecf20Sopenharmony_ci req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_LEASE; 20668c2ecf20Sopenharmony_ci if (!req->CreateContextsOffset) 20678c2ecf20Sopenharmony_ci req->CreateContextsOffset = cpu_to_le32( 20688c2ecf20Sopenharmony_ci sizeof(struct smb2_create_req) + 20698c2ecf20Sopenharmony_ci iov[num - 1].iov_len); 20708c2ecf20Sopenharmony_ci le32_add_cpu(&req->CreateContextsLength, 20718c2ecf20Sopenharmony_ci server->vals->create_lease_size); 20728c2ecf20Sopenharmony_ci *num_iovec = num + 1; 20738c2ecf20Sopenharmony_ci return 0; 20748c2ecf20Sopenharmony_ci} 20758c2ecf20Sopenharmony_ci 20768c2ecf20Sopenharmony_cistatic struct create_durable_v2 * 20778c2ecf20Sopenharmony_cicreate_durable_v2_buf(struct cifs_open_parms *oparms) 20788c2ecf20Sopenharmony_ci{ 20798c2ecf20Sopenharmony_ci struct cifs_fid *pfid = oparms->fid; 20808c2ecf20Sopenharmony_ci struct create_durable_v2 *buf; 20818c2ecf20Sopenharmony_ci 20828c2ecf20Sopenharmony_ci buf = kzalloc(sizeof(struct create_durable_v2), GFP_KERNEL); 20838c2ecf20Sopenharmony_ci if (!buf) 20848c2ecf20Sopenharmony_ci return NULL; 20858c2ecf20Sopenharmony_ci 20868c2ecf20Sopenharmony_ci buf->ccontext.DataOffset = cpu_to_le16(offsetof 20878c2ecf20Sopenharmony_ci (struct create_durable_v2, dcontext)); 20888c2ecf20Sopenharmony_ci buf->ccontext.DataLength = cpu_to_le32(sizeof(struct durable_context_v2)); 20898c2ecf20Sopenharmony_ci buf->ccontext.NameOffset = cpu_to_le16(offsetof 20908c2ecf20Sopenharmony_ci (struct create_durable_v2, Name)); 20918c2ecf20Sopenharmony_ci buf->ccontext.NameLength = cpu_to_le16(4); 20928c2ecf20Sopenharmony_ci 20938c2ecf20Sopenharmony_ci /* 20948c2ecf20Sopenharmony_ci * NB: Handle timeout defaults to 0, which allows server to choose 20958c2ecf20Sopenharmony_ci * (most servers default to 120 seconds) and most clients default to 0. 20968c2ecf20Sopenharmony_ci * This can be overridden at mount ("handletimeout=") if the user wants 20978c2ecf20Sopenharmony_ci * a different persistent (or resilient) handle timeout for all opens 20988c2ecf20Sopenharmony_ci * opens on a particular SMB3 mount. 20998c2ecf20Sopenharmony_ci */ 21008c2ecf20Sopenharmony_ci buf->dcontext.Timeout = cpu_to_le32(oparms->tcon->handle_timeout); 21018c2ecf20Sopenharmony_ci buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT); 21028c2ecf20Sopenharmony_ci generate_random_uuid(buf->dcontext.CreateGuid); 21038c2ecf20Sopenharmony_ci memcpy(pfid->create_guid, buf->dcontext.CreateGuid, 16); 21048c2ecf20Sopenharmony_ci 21058c2ecf20Sopenharmony_ci /* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DH2Q" */ 21068c2ecf20Sopenharmony_ci buf->Name[0] = 'D'; 21078c2ecf20Sopenharmony_ci buf->Name[1] = 'H'; 21088c2ecf20Sopenharmony_ci buf->Name[2] = '2'; 21098c2ecf20Sopenharmony_ci buf->Name[3] = 'Q'; 21108c2ecf20Sopenharmony_ci return buf; 21118c2ecf20Sopenharmony_ci} 21128c2ecf20Sopenharmony_ci 21138c2ecf20Sopenharmony_cistatic struct create_durable_handle_reconnect_v2 * 21148c2ecf20Sopenharmony_cicreate_reconnect_durable_v2_buf(struct cifs_fid *fid) 21158c2ecf20Sopenharmony_ci{ 21168c2ecf20Sopenharmony_ci struct create_durable_handle_reconnect_v2 *buf; 21178c2ecf20Sopenharmony_ci 21188c2ecf20Sopenharmony_ci buf = kzalloc(sizeof(struct create_durable_handle_reconnect_v2), 21198c2ecf20Sopenharmony_ci GFP_KERNEL); 21208c2ecf20Sopenharmony_ci if (!buf) 21218c2ecf20Sopenharmony_ci return NULL; 21228c2ecf20Sopenharmony_ci 21238c2ecf20Sopenharmony_ci buf->ccontext.DataOffset = 21248c2ecf20Sopenharmony_ci cpu_to_le16(offsetof(struct create_durable_handle_reconnect_v2, 21258c2ecf20Sopenharmony_ci dcontext)); 21268c2ecf20Sopenharmony_ci buf->ccontext.DataLength = 21278c2ecf20Sopenharmony_ci cpu_to_le32(sizeof(struct durable_reconnect_context_v2)); 21288c2ecf20Sopenharmony_ci buf->ccontext.NameOffset = 21298c2ecf20Sopenharmony_ci cpu_to_le16(offsetof(struct create_durable_handle_reconnect_v2, 21308c2ecf20Sopenharmony_ci Name)); 21318c2ecf20Sopenharmony_ci buf->ccontext.NameLength = cpu_to_le16(4); 21328c2ecf20Sopenharmony_ci 21338c2ecf20Sopenharmony_ci buf->dcontext.Fid.PersistentFileId = fid->persistent_fid; 21348c2ecf20Sopenharmony_ci buf->dcontext.Fid.VolatileFileId = fid->volatile_fid; 21358c2ecf20Sopenharmony_ci buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT); 21368c2ecf20Sopenharmony_ci memcpy(buf->dcontext.CreateGuid, fid->create_guid, 16); 21378c2ecf20Sopenharmony_ci 21388c2ecf20Sopenharmony_ci /* SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 is "DH2C" */ 21398c2ecf20Sopenharmony_ci buf->Name[0] = 'D'; 21408c2ecf20Sopenharmony_ci buf->Name[1] = 'H'; 21418c2ecf20Sopenharmony_ci buf->Name[2] = '2'; 21428c2ecf20Sopenharmony_ci buf->Name[3] = 'C'; 21438c2ecf20Sopenharmony_ci return buf; 21448c2ecf20Sopenharmony_ci} 21458c2ecf20Sopenharmony_ci 21468c2ecf20Sopenharmony_cistatic int 21478c2ecf20Sopenharmony_ciadd_durable_v2_context(struct kvec *iov, unsigned int *num_iovec, 21488c2ecf20Sopenharmony_ci struct cifs_open_parms *oparms) 21498c2ecf20Sopenharmony_ci{ 21508c2ecf20Sopenharmony_ci struct smb2_create_req *req = iov[0].iov_base; 21518c2ecf20Sopenharmony_ci unsigned int num = *num_iovec; 21528c2ecf20Sopenharmony_ci 21538c2ecf20Sopenharmony_ci iov[num].iov_base = create_durable_v2_buf(oparms); 21548c2ecf20Sopenharmony_ci if (iov[num].iov_base == NULL) 21558c2ecf20Sopenharmony_ci return -ENOMEM; 21568c2ecf20Sopenharmony_ci iov[num].iov_len = sizeof(struct create_durable_v2); 21578c2ecf20Sopenharmony_ci if (!req->CreateContextsOffset) 21588c2ecf20Sopenharmony_ci req->CreateContextsOffset = 21598c2ecf20Sopenharmony_ci cpu_to_le32(sizeof(struct smb2_create_req) + 21608c2ecf20Sopenharmony_ci iov[1].iov_len); 21618c2ecf20Sopenharmony_ci le32_add_cpu(&req->CreateContextsLength, sizeof(struct create_durable_v2)); 21628c2ecf20Sopenharmony_ci *num_iovec = num + 1; 21638c2ecf20Sopenharmony_ci return 0; 21648c2ecf20Sopenharmony_ci} 21658c2ecf20Sopenharmony_ci 21668c2ecf20Sopenharmony_cistatic int 21678c2ecf20Sopenharmony_ciadd_durable_reconnect_v2_context(struct kvec *iov, unsigned int *num_iovec, 21688c2ecf20Sopenharmony_ci struct cifs_open_parms *oparms) 21698c2ecf20Sopenharmony_ci{ 21708c2ecf20Sopenharmony_ci struct smb2_create_req *req = iov[0].iov_base; 21718c2ecf20Sopenharmony_ci unsigned int num = *num_iovec; 21728c2ecf20Sopenharmony_ci 21738c2ecf20Sopenharmony_ci /* indicate that we don't need to relock the file */ 21748c2ecf20Sopenharmony_ci oparms->reconnect = false; 21758c2ecf20Sopenharmony_ci 21768c2ecf20Sopenharmony_ci iov[num].iov_base = create_reconnect_durable_v2_buf(oparms->fid); 21778c2ecf20Sopenharmony_ci if (iov[num].iov_base == NULL) 21788c2ecf20Sopenharmony_ci return -ENOMEM; 21798c2ecf20Sopenharmony_ci iov[num].iov_len = sizeof(struct create_durable_handle_reconnect_v2); 21808c2ecf20Sopenharmony_ci if (!req->CreateContextsOffset) 21818c2ecf20Sopenharmony_ci req->CreateContextsOffset = 21828c2ecf20Sopenharmony_ci cpu_to_le32(sizeof(struct smb2_create_req) + 21838c2ecf20Sopenharmony_ci iov[1].iov_len); 21848c2ecf20Sopenharmony_ci le32_add_cpu(&req->CreateContextsLength, 21858c2ecf20Sopenharmony_ci sizeof(struct create_durable_handle_reconnect_v2)); 21868c2ecf20Sopenharmony_ci *num_iovec = num + 1; 21878c2ecf20Sopenharmony_ci return 0; 21888c2ecf20Sopenharmony_ci} 21898c2ecf20Sopenharmony_ci 21908c2ecf20Sopenharmony_cistatic int 21918c2ecf20Sopenharmony_ciadd_durable_context(struct kvec *iov, unsigned int *num_iovec, 21928c2ecf20Sopenharmony_ci struct cifs_open_parms *oparms, bool use_persistent) 21938c2ecf20Sopenharmony_ci{ 21948c2ecf20Sopenharmony_ci struct smb2_create_req *req = iov[0].iov_base; 21958c2ecf20Sopenharmony_ci unsigned int num = *num_iovec; 21968c2ecf20Sopenharmony_ci 21978c2ecf20Sopenharmony_ci if (use_persistent) { 21988c2ecf20Sopenharmony_ci if (oparms->reconnect) 21998c2ecf20Sopenharmony_ci return add_durable_reconnect_v2_context(iov, num_iovec, 22008c2ecf20Sopenharmony_ci oparms); 22018c2ecf20Sopenharmony_ci else 22028c2ecf20Sopenharmony_ci return add_durable_v2_context(iov, num_iovec, oparms); 22038c2ecf20Sopenharmony_ci } 22048c2ecf20Sopenharmony_ci 22058c2ecf20Sopenharmony_ci if (oparms->reconnect) { 22068c2ecf20Sopenharmony_ci iov[num].iov_base = create_reconnect_durable_buf(oparms->fid); 22078c2ecf20Sopenharmony_ci /* indicate that we don't need to relock the file */ 22088c2ecf20Sopenharmony_ci oparms->reconnect = false; 22098c2ecf20Sopenharmony_ci } else 22108c2ecf20Sopenharmony_ci iov[num].iov_base = create_durable_buf(); 22118c2ecf20Sopenharmony_ci if (iov[num].iov_base == NULL) 22128c2ecf20Sopenharmony_ci return -ENOMEM; 22138c2ecf20Sopenharmony_ci iov[num].iov_len = sizeof(struct create_durable); 22148c2ecf20Sopenharmony_ci if (!req->CreateContextsOffset) 22158c2ecf20Sopenharmony_ci req->CreateContextsOffset = 22168c2ecf20Sopenharmony_ci cpu_to_le32(sizeof(struct smb2_create_req) + 22178c2ecf20Sopenharmony_ci iov[1].iov_len); 22188c2ecf20Sopenharmony_ci le32_add_cpu(&req->CreateContextsLength, sizeof(struct create_durable)); 22198c2ecf20Sopenharmony_ci *num_iovec = num + 1; 22208c2ecf20Sopenharmony_ci return 0; 22218c2ecf20Sopenharmony_ci} 22228c2ecf20Sopenharmony_ci 22238c2ecf20Sopenharmony_ci/* See MS-SMB2 2.2.13.2.7 */ 22248c2ecf20Sopenharmony_cistatic struct crt_twarp_ctxt * 22258c2ecf20Sopenharmony_cicreate_twarp_buf(__u64 timewarp) 22268c2ecf20Sopenharmony_ci{ 22278c2ecf20Sopenharmony_ci struct crt_twarp_ctxt *buf; 22288c2ecf20Sopenharmony_ci 22298c2ecf20Sopenharmony_ci buf = kzalloc(sizeof(struct crt_twarp_ctxt), GFP_KERNEL); 22308c2ecf20Sopenharmony_ci if (!buf) 22318c2ecf20Sopenharmony_ci return NULL; 22328c2ecf20Sopenharmony_ci 22338c2ecf20Sopenharmony_ci buf->ccontext.DataOffset = cpu_to_le16(offsetof 22348c2ecf20Sopenharmony_ci (struct crt_twarp_ctxt, Timestamp)); 22358c2ecf20Sopenharmony_ci buf->ccontext.DataLength = cpu_to_le32(8); 22368c2ecf20Sopenharmony_ci buf->ccontext.NameOffset = cpu_to_le16(offsetof 22378c2ecf20Sopenharmony_ci (struct crt_twarp_ctxt, Name)); 22388c2ecf20Sopenharmony_ci buf->ccontext.NameLength = cpu_to_le16(4); 22398c2ecf20Sopenharmony_ci /* SMB2_CREATE_TIMEWARP_TOKEN is "TWrp" */ 22408c2ecf20Sopenharmony_ci buf->Name[0] = 'T'; 22418c2ecf20Sopenharmony_ci buf->Name[1] = 'W'; 22428c2ecf20Sopenharmony_ci buf->Name[2] = 'r'; 22438c2ecf20Sopenharmony_ci buf->Name[3] = 'p'; 22448c2ecf20Sopenharmony_ci buf->Timestamp = cpu_to_le64(timewarp); 22458c2ecf20Sopenharmony_ci return buf; 22468c2ecf20Sopenharmony_ci} 22478c2ecf20Sopenharmony_ci 22488c2ecf20Sopenharmony_ci/* See MS-SMB2 2.2.13.2.7 */ 22498c2ecf20Sopenharmony_cistatic int 22508c2ecf20Sopenharmony_ciadd_twarp_context(struct kvec *iov, unsigned int *num_iovec, __u64 timewarp) 22518c2ecf20Sopenharmony_ci{ 22528c2ecf20Sopenharmony_ci struct smb2_create_req *req = iov[0].iov_base; 22538c2ecf20Sopenharmony_ci unsigned int num = *num_iovec; 22548c2ecf20Sopenharmony_ci 22558c2ecf20Sopenharmony_ci iov[num].iov_base = create_twarp_buf(timewarp); 22568c2ecf20Sopenharmony_ci if (iov[num].iov_base == NULL) 22578c2ecf20Sopenharmony_ci return -ENOMEM; 22588c2ecf20Sopenharmony_ci iov[num].iov_len = sizeof(struct crt_twarp_ctxt); 22598c2ecf20Sopenharmony_ci if (!req->CreateContextsOffset) 22608c2ecf20Sopenharmony_ci req->CreateContextsOffset = cpu_to_le32( 22618c2ecf20Sopenharmony_ci sizeof(struct smb2_create_req) + 22628c2ecf20Sopenharmony_ci iov[num - 1].iov_len); 22638c2ecf20Sopenharmony_ci le32_add_cpu(&req->CreateContextsLength, sizeof(struct crt_twarp_ctxt)); 22648c2ecf20Sopenharmony_ci *num_iovec = num + 1; 22658c2ecf20Sopenharmony_ci return 0; 22668c2ecf20Sopenharmony_ci} 22678c2ecf20Sopenharmony_ci 22688c2ecf20Sopenharmony_ci/* See See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx */ 22698c2ecf20Sopenharmony_cistatic void setup_owner_group_sids(char *buf) 22708c2ecf20Sopenharmony_ci{ 22718c2ecf20Sopenharmony_ci struct owner_group_sids *sids = (struct owner_group_sids *)buf; 22728c2ecf20Sopenharmony_ci 22738c2ecf20Sopenharmony_ci /* Populate the user ownership fields S-1-5-88-1 */ 22748c2ecf20Sopenharmony_ci sids->owner.Revision = 1; 22758c2ecf20Sopenharmony_ci sids->owner.NumAuth = 3; 22768c2ecf20Sopenharmony_ci sids->owner.Authority[5] = 5; 22778c2ecf20Sopenharmony_ci sids->owner.SubAuthorities[0] = cpu_to_le32(88); 22788c2ecf20Sopenharmony_ci sids->owner.SubAuthorities[1] = cpu_to_le32(1); 22798c2ecf20Sopenharmony_ci sids->owner.SubAuthorities[2] = cpu_to_le32(current_fsuid().val); 22808c2ecf20Sopenharmony_ci 22818c2ecf20Sopenharmony_ci /* Populate the group ownership fields S-1-5-88-2 */ 22828c2ecf20Sopenharmony_ci sids->group.Revision = 1; 22838c2ecf20Sopenharmony_ci sids->group.NumAuth = 3; 22848c2ecf20Sopenharmony_ci sids->group.Authority[5] = 5; 22858c2ecf20Sopenharmony_ci sids->group.SubAuthorities[0] = cpu_to_le32(88); 22868c2ecf20Sopenharmony_ci sids->group.SubAuthorities[1] = cpu_to_le32(2); 22878c2ecf20Sopenharmony_ci sids->group.SubAuthorities[2] = cpu_to_le32(current_fsgid().val); 22888c2ecf20Sopenharmony_ci 22898c2ecf20Sopenharmony_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); 22908c2ecf20Sopenharmony_ci} 22918c2ecf20Sopenharmony_ci 22928c2ecf20Sopenharmony_ci/* See MS-SMB2 2.2.13.2.2 and MS-DTYP 2.4.6 */ 22938c2ecf20Sopenharmony_cistatic struct crt_sd_ctxt * 22948c2ecf20Sopenharmony_cicreate_sd_buf(umode_t mode, bool set_owner, unsigned int *len) 22958c2ecf20Sopenharmony_ci{ 22968c2ecf20Sopenharmony_ci struct crt_sd_ctxt *buf; 22978c2ecf20Sopenharmony_ci __u8 *ptr, *aclptr; 22988c2ecf20Sopenharmony_ci unsigned int acelen, acl_size, ace_count; 22998c2ecf20Sopenharmony_ci unsigned int owner_offset = 0; 23008c2ecf20Sopenharmony_ci unsigned int group_offset = 0; 23018c2ecf20Sopenharmony_ci struct smb3_acl acl = {}; 23028c2ecf20Sopenharmony_ci 23038c2ecf20Sopenharmony_ci *len = roundup(sizeof(struct crt_sd_ctxt) + (sizeof(struct cifs_ace) * 4), 8); 23048c2ecf20Sopenharmony_ci 23058c2ecf20Sopenharmony_ci if (set_owner) { 23068c2ecf20Sopenharmony_ci /* sizeof(struct owner_group_sids) is already multiple of 8 so no need to round */ 23078c2ecf20Sopenharmony_ci *len += sizeof(struct owner_group_sids); 23088c2ecf20Sopenharmony_ci } 23098c2ecf20Sopenharmony_ci 23108c2ecf20Sopenharmony_ci buf = kzalloc(*len, GFP_KERNEL); 23118c2ecf20Sopenharmony_ci if (buf == NULL) 23128c2ecf20Sopenharmony_ci return buf; 23138c2ecf20Sopenharmony_ci 23148c2ecf20Sopenharmony_ci ptr = (__u8 *)&buf[1]; 23158c2ecf20Sopenharmony_ci if (set_owner) { 23168c2ecf20Sopenharmony_ci /* offset fields are from beginning of security descriptor not of create context */ 23178c2ecf20Sopenharmony_ci owner_offset = ptr - (__u8 *)&buf->sd; 23188c2ecf20Sopenharmony_ci buf->sd.OffsetOwner = cpu_to_le32(owner_offset); 23198c2ecf20Sopenharmony_ci group_offset = owner_offset + offsetof(struct owner_group_sids, group); 23208c2ecf20Sopenharmony_ci buf->sd.OffsetGroup = cpu_to_le32(group_offset); 23218c2ecf20Sopenharmony_ci 23228c2ecf20Sopenharmony_ci setup_owner_group_sids(ptr); 23238c2ecf20Sopenharmony_ci ptr += sizeof(struct owner_group_sids); 23248c2ecf20Sopenharmony_ci } else { 23258c2ecf20Sopenharmony_ci buf->sd.OffsetOwner = 0; 23268c2ecf20Sopenharmony_ci buf->sd.OffsetGroup = 0; 23278c2ecf20Sopenharmony_ci } 23288c2ecf20Sopenharmony_ci 23298c2ecf20Sopenharmony_ci buf->ccontext.DataOffset = cpu_to_le16(offsetof(struct crt_sd_ctxt, sd)); 23308c2ecf20Sopenharmony_ci buf->ccontext.NameOffset = cpu_to_le16(offsetof(struct crt_sd_ctxt, Name)); 23318c2ecf20Sopenharmony_ci buf->ccontext.NameLength = cpu_to_le16(4); 23328c2ecf20Sopenharmony_ci /* SMB2_CREATE_SD_BUFFER_TOKEN is "SecD" */ 23338c2ecf20Sopenharmony_ci buf->Name[0] = 'S'; 23348c2ecf20Sopenharmony_ci buf->Name[1] = 'e'; 23358c2ecf20Sopenharmony_ci buf->Name[2] = 'c'; 23368c2ecf20Sopenharmony_ci buf->Name[3] = 'D'; 23378c2ecf20Sopenharmony_ci buf->sd.Revision = 1; /* Must be one see MS-DTYP 2.4.6 */ 23388c2ecf20Sopenharmony_ci 23398c2ecf20Sopenharmony_ci /* 23408c2ecf20Sopenharmony_ci * ACL is "self relative" ie ACL is stored in contiguous block of memory 23418c2ecf20Sopenharmony_ci * and "DP" ie the DACL is present 23428c2ecf20Sopenharmony_ci */ 23438c2ecf20Sopenharmony_ci buf->sd.Control = cpu_to_le16(ACL_CONTROL_SR | ACL_CONTROL_DP); 23448c2ecf20Sopenharmony_ci 23458c2ecf20Sopenharmony_ci /* offset owner, group and Sbz1 and SACL are all zero */ 23468c2ecf20Sopenharmony_ci buf->sd.OffsetDacl = cpu_to_le32(ptr - (__u8 *)&buf->sd); 23478c2ecf20Sopenharmony_ci /* Ship the ACL for now. we will copy it into buf later. */ 23488c2ecf20Sopenharmony_ci aclptr = ptr; 23498c2ecf20Sopenharmony_ci ptr += sizeof(struct smb3_acl); 23508c2ecf20Sopenharmony_ci 23518c2ecf20Sopenharmony_ci /* create one ACE to hold the mode embedded in reserved special SID */ 23528c2ecf20Sopenharmony_ci acelen = setup_special_mode_ACE((struct cifs_ace *)ptr, (__u64)mode); 23538c2ecf20Sopenharmony_ci ptr += acelen; 23548c2ecf20Sopenharmony_ci acl_size = acelen + sizeof(struct smb3_acl); 23558c2ecf20Sopenharmony_ci ace_count = 1; 23568c2ecf20Sopenharmony_ci 23578c2ecf20Sopenharmony_ci if (set_owner) { 23588c2ecf20Sopenharmony_ci /* we do not need to reallocate buffer to add the two more ACEs. plenty of space */ 23598c2ecf20Sopenharmony_ci acelen = setup_special_user_owner_ACE((struct cifs_ace *)ptr); 23608c2ecf20Sopenharmony_ci ptr += acelen; 23618c2ecf20Sopenharmony_ci acl_size += acelen; 23628c2ecf20Sopenharmony_ci ace_count += 1; 23638c2ecf20Sopenharmony_ci } 23648c2ecf20Sopenharmony_ci 23658c2ecf20Sopenharmony_ci /* and one more ACE to allow access for authenticated users */ 23668c2ecf20Sopenharmony_ci acelen = setup_authusers_ACE((struct cifs_ace *)ptr); 23678c2ecf20Sopenharmony_ci ptr += acelen; 23688c2ecf20Sopenharmony_ci acl_size += acelen; 23698c2ecf20Sopenharmony_ci ace_count += 1; 23708c2ecf20Sopenharmony_ci 23718c2ecf20Sopenharmony_ci acl.AclRevision = ACL_REVISION; /* See 2.4.4.1 of MS-DTYP */ 23728c2ecf20Sopenharmony_ci acl.AclSize = cpu_to_le16(acl_size); 23738c2ecf20Sopenharmony_ci acl.AceCount = cpu_to_le16(ace_count); 23748c2ecf20Sopenharmony_ci /* acl.Sbz1 and Sbz2 MBZ so are not set here, but initialized above */ 23758c2ecf20Sopenharmony_ci memcpy(aclptr, &acl, sizeof(struct smb3_acl)); 23768c2ecf20Sopenharmony_ci 23778c2ecf20Sopenharmony_ci buf->ccontext.DataLength = cpu_to_le32(ptr - (__u8 *)&buf->sd); 23788c2ecf20Sopenharmony_ci *len = roundup(ptr - (__u8 *)buf, 8); 23798c2ecf20Sopenharmony_ci 23808c2ecf20Sopenharmony_ci return buf; 23818c2ecf20Sopenharmony_ci} 23828c2ecf20Sopenharmony_ci 23838c2ecf20Sopenharmony_cistatic int 23848c2ecf20Sopenharmony_ciadd_sd_context(struct kvec *iov, unsigned int *num_iovec, umode_t mode, bool set_owner) 23858c2ecf20Sopenharmony_ci{ 23868c2ecf20Sopenharmony_ci struct smb2_create_req *req = iov[0].iov_base; 23878c2ecf20Sopenharmony_ci unsigned int num = *num_iovec; 23888c2ecf20Sopenharmony_ci unsigned int len = 0; 23898c2ecf20Sopenharmony_ci 23908c2ecf20Sopenharmony_ci iov[num].iov_base = create_sd_buf(mode, set_owner, &len); 23918c2ecf20Sopenharmony_ci if (iov[num].iov_base == NULL) 23928c2ecf20Sopenharmony_ci return -ENOMEM; 23938c2ecf20Sopenharmony_ci iov[num].iov_len = len; 23948c2ecf20Sopenharmony_ci if (!req->CreateContextsOffset) 23958c2ecf20Sopenharmony_ci req->CreateContextsOffset = cpu_to_le32( 23968c2ecf20Sopenharmony_ci sizeof(struct smb2_create_req) + 23978c2ecf20Sopenharmony_ci iov[num - 1].iov_len); 23988c2ecf20Sopenharmony_ci le32_add_cpu(&req->CreateContextsLength, len); 23998c2ecf20Sopenharmony_ci *num_iovec = num + 1; 24008c2ecf20Sopenharmony_ci return 0; 24018c2ecf20Sopenharmony_ci} 24028c2ecf20Sopenharmony_ci 24038c2ecf20Sopenharmony_cistatic struct crt_query_id_ctxt * 24048c2ecf20Sopenharmony_cicreate_query_id_buf(void) 24058c2ecf20Sopenharmony_ci{ 24068c2ecf20Sopenharmony_ci struct crt_query_id_ctxt *buf; 24078c2ecf20Sopenharmony_ci 24088c2ecf20Sopenharmony_ci buf = kzalloc(sizeof(struct crt_query_id_ctxt), GFP_KERNEL); 24098c2ecf20Sopenharmony_ci if (!buf) 24108c2ecf20Sopenharmony_ci return NULL; 24118c2ecf20Sopenharmony_ci 24128c2ecf20Sopenharmony_ci buf->ccontext.DataOffset = cpu_to_le16(0); 24138c2ecf20Sopenharmony_ci buf->ccontext.DataLength = cpu_to_le32(0); 24148c2ecf20Sopenharmony_ci buf->ccontext.NameOffset = cpu_to_le16(offsetof 24158c2ecf20Sopenharmony_ci (struct crt_query_id_ctxt, Name)); 24168c2ecf20Sopenharmony_ci buf->ccontext.NameLength = cpu_to_le16(4); 24178c2ecf20Sopenharmony_ci /* SMB2_CREATE_QUERY_ON_DISK_ID is "QFid" */ 24188c2ecf20Sopenharmony_ci buf->Name[0] = 'Q'; 24198c2ecf20Sopenharmony_ci buf->Name[1] = 'F'; 24208c2ecf20Sopenharmony_ci buf->Name[2] = 'i'; 24218c2ecf20Sopenharmony_ci buf->Name[3] = 'd'; 24228c2ecf20Sopenharmony_ci return buf; 24238c2ecf20Sopenharmony_ci} 24248c2ecf20Sopenharmony_ci 24258c2ecf20Sopenharmony_ci/* See MS-SMB2 2.2.13.2.9 */ 24268c2ecf20Sopenharmony_cistatic int 24278c2ecf20Sopenharmony_ciadd_query_id_context(struct kvec *iov, unsigned int *num_iovec) 24288c2ecf20Sopenharmony_ci{ 24298c2ecf20Sopenharmony_ci struct smb2_create_req *req = iov[0].iov_base; 24308c2ecf20Sopenharmony_ci unsigned int num = *num_iovec; 24318c2ecf20Sopenharmony_ci 24328c2ecf20Sopenharmony_ci iov[num].iov_base = create_query_id_buf(); 24338c2ecf20Sopenharmony_ci if (iov[num].iov_base == NULL) 24348c2ecf20Sopenharmony_ci return -ENOMEM; 24358c2ecf20Sopenharmony_ci iov[num].iov_len = sizeof(struct crt_query_id_ctxt); 24368c2ecf20Sopenharmony_ci if (!req->CreateContextsOffset) 24378c2ecf20Sopenharmony_ci req->CreateContextsOffset = cpu_to_le32( 24388c2ecf20Sopenharmony_ci sizeof(struct smb2_create_req) + 24398c2ecf20Sopenharmony_ci iov[num - 1].iov_len); 24408c2ecf20Sopenharmony_ci le32_add_cpu(&req->CreateContextsLength, sizeof(struct crt_query_id_ctxt)); 24418c2ecf20Sopenharmony_ci *num_iovec = num + 1; 24428c2ecf20Sopenharmony_ci return 0; 24438c2ecf20Sopenharmony_ci} 24448c2ecf20Sopenharmony_ci 24458c2ecf20Sopenharmony_cistatic int 24468c2ecf20Sopenharmony_cialloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len, 24478c2ecf20Sopenharmony_ci const char *treename, const __le16 *path) 24488c2ecf20Sopenharmony_ci{ 24498c2ecf20Sopenharmony_ci int treename_len, path_len; 24508c2ecf20Sopenharmony_ci struct nls_table *cp; 24518c2ecf20Sopenharmony_ci const __le16 sep[] = {cpu_to_le16('\\'), cpu_to_le16(0x0000)}; 24528c2ecf20Sopenharmony_ci 24538c2ecf20Sopenharmony_ci /* 24548c2ecf20Sopenharmony_ci * skip leading "\\" 24558c2ecf20Sopenharmony_ci */ 24568c2ecf20Sopenharmony_ci treename_len = strlen(treename); 24578c2ecf20Sopenharmony_ci if (treename_len < 2 || !(treename[0] == '\\' && treename[1] == '\\')) 24588c2ecf20Sopenharmony_ci return -EINVAL; 24598c2ecf20Sopenharmony_ci 24608c2ecf20Sopenharmony_ci treename += 2; 24618c2ecf20Sopenharmony_ci treename_len -= 2; 24628c2ecf20Sopenharmony_ci 24638c2ecf20Sopenharmony_ci path_len = UniStrnlen((wchar_t *)path, PATH_MAX); 24648c2ecf20Sopenharmony_ci 24658c2ecf20Sopenharmony_ci /* 24668c2ecf20Sopenharmony_ci * make room for one path separator between the treename and 24678c2ecf20Sopenharmony_ci * path 24688c2ecf20Sopenharmony_ci */ 24698c2ecf20Sopenharmony_ci *out_len = treename_len + 1 + path_len; 24708c2ecf20Sopenharmony_ci 24718c2ecf20Sopenharmony_ci /* 24728c2ecf20Sopenharmony_ci * final path needs to be null-terminated UTF16 with a 24738c2ecf20Sopenharmony_ci * size aligned to 8 24748c2ecf20Sopenharmony_ci */ 24758c2ecf20Sopenharmony_ci 24768c2ecf20Sopenharmony_ci *out_size = roundup((*out_len+1)*2, 8); 24778c2ecf20Sopenharmony_ci *out_path = kzalloc(*out_size, GFP_KERNEL); 24788c2ecf20Sopenharmony_ci if (!*out_path) 24798c2ecf20Sopenharmony_ci return -ENOMEM; 24808c2ecf20Sopenharmony_ci 24818c2ecf20Sopenharmony_ci cp = load_nls_default(); 24828c2ecf20Sopenharmony_ci cifs_strtoUTF16(*out_path, treename, treename_len, cp); 24838c2ecf20Sopenharmony_ci UniStrcat(*out_path, sep); 24848c2ecf20Sopenharmony_ci UniStrcat(*out_path, path); 24858c2ecf20Sopenharmony_ci unload_nls(cp); 24868c2ecf20Sopenharmony_ci 24878c2ecf20Sopenharmony_ci return 0; 24888c2ecf20Sopenharmony_ci} 24898c2ecf20Sopenharmony_ci 24908c2ecf20Sopenharmony_ciint smb311_posix_mkdir(const unsigned int xid, struct inode *inode, 24918c2ecf20Sopenharmony_ci umode_t mode, struct cifs_tcon *tcon, 24928c2ecf20Sopenharmony_ci const char *full_path, 24938c2ecf20Sopenharmony_ci struct cifs_sb_info *cifs_sb) 24948c2ecf20Sopenharmony_ci{ 24958c2ecf20Sopenharmony_ci struct smb_rqst rqst; 24968c2ecf20Sopenharmony_ci struct smb2_create_req *req; 24978c2ecf20Sopenharmony_ci struct smb2_create_rsp *rsp = NULL; 24988c2ecf20Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 24998c2ecf20Sopenharmony_ci struct kvec iov[3]; /* make sure at least one for each open context */ 25008c2ecf20Sopenharmony_ci struct kvec rsp_iov = {NULL, 0}; 25018c2ecf20Sopenharmony_ci int resp_buftype; 25028c2ecf20Sopenharmony_ci int uni_path_len; 25038c2ecf20Sopenharmony_ci __le16 *copy_path = NULL; 25048c2ecf20Sopenharmony_ci int copy_size; 25058c2ecf20Sopenharmony_ci int rc = 0; 25068c2ecf20Sopenharmony_ci unsigned int n_iov = 2; 25078c2ecf20Sopenharmony_ci __u32 file_attributes = 0; 25088c2ecf20Sopenharmony_ci char *pc_buf = NULL; 25098c2ecf20Sopenharmony_ci int flags = 0; 25108c2ecf20Sopenharmony_ci unsigned int total_len; 25118c2ecf20Sopenharmony_ci __le16 *utf16_path = NULL; 25128c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(ses); 25138c2ecf20Sopenharmony_ci 25148c2ecf20Sopenharmony_ci cifs_dbg(FYI, "mkdir\n"); 25158c2ecf20Sopenharmony_ci 25168c2ecf20Sopenharmony_ci /* resource #1: path allocation */ 25178c2ecf20Sopenharmony_ci utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); 25188c2ecf20Sopenharmony_ci if (!utf16_path) 25198c2ecf20Sopenharmony_ci return -ENOMEM; 25208c2ecf20Sopenharmony_ci 25218c2ecf20Sopenharmony_ci if (!ses || !server) { 25228c2ecf20Sopenharmony_ci rc = -EIO; 25238c2ecf20Sopenharmony_ci goto err_free_path; 25248c2ecf20Sopenharmony_ci } 25258c2ecf20Sopenharmony_ci 25268c2ecf20Sopenharmony_ci /* resource #2: request */ 25278c2ecf20Sopenharmony_ci rc = smb2_plain_req_init(SMB2_CREATE, tcon, server, 25288c2ecf20Sopenharmony_ci (void **) &req, &total_len); 25298c2ecf20Sopenharmony_ci if (rc) 25308c2ecf20Sopenharmony_ci goto err_free_path; 25318c2ecf20Sopenharmony_ci 25328c2ecf20Sopenharmony_ci 25338c2ecf20Sopenharmony_ci if (smb3_encryption_required(tcon)) 25348c2ecf20Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 25358c2ecf20Sopenharmony_ci 25368c2ecf20Sopenharmony_ci req->ImpersonationLevel = IL_IMPERSONATION; 25378c2ecf20Sopenharmony_ci req->DesiredAccess = cpu_to_le32(FILE_WRITE_ATTRIBUTES); 25388c2ecf20Sopenharmony_ci /* File attributes ignored on open (used in create though) */ 25398c2ecf20Sopenharmony_ci req->FileAttributes = cpu_to_le32(file_attributes); 25408c2ecf20Sopenharmony_ci req->ShareAccess = FILE_SHARE_ALL_LE; 25418c2ecf20Sopenharmony_ci req->CreateDisposition = cpu_to_le32(FILE_CREATE); 25428c2ecf20Sopenharmony_ci req->CreateOptions = cpu_to_le32(CREATE_NOT_FILE); 25438c2ecf20Sopenharmony_ci 25448c2ecf20Sopenharmony_ci iov[0].iov_base = (char *)req; 25458c2ecf20Sopenharmony_ci /* -1 since last byte is buf[0] which is sent below (path) */ 25468c2ecf20Sopenharmony_ci iov[0].iov_len = total_len - 1; 25478c2ecf20Sopenharmony_ci 25488c2ecf20Sopenharmony_ci req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req)); 25498c2ecf20Sopenharmony_ci 25508c2ecf20Sopenharmony_ci /* [MS-SMB2] 2.2.13 NameOffset: 25518c2ecf20Sopenharmony_ci * If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of 25528c2ecf20Sopenharmony_ci * the SMB2 header, the file name includes a prefix that will 25538c2ecf20Sopenharmony_ci * be processed during DFS name normalization as specified in 25548c2ecf20Sopenharmony_ci * section 3.3.5.9. Otherwise, the file name is relative to 25558c2ecf20Sopenharmony_ci * the share that is identified by the TreeId in the SMB2 25568c2ecf20Sopenharmony_ci * header. 25578c2ecf20Sopenharmony_ci */ 25588c2ecf20Sopenharmony_ci if (tcon->share_flags & SHI1005_FLAGS_DFS) { 25598c2ecf20Sopenharmony_ci int name_len; 25608c2ecf20Sopenharmony_ci 25618c2ecf20Sopenharmony_ci req->sync_hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS; 25628c2ecf20Sopenharmony_ci rc = alloc_path_with_tree_prefix(©_path, ©_size, 25638c2ecf20Sopenharmony_ci &name_len, 25648c2ecf20Sopenharmony_ci tcon->treeName, utf16_path); 25658c2ecf20Sopenharmony_ci if (rc) 25668c2ecf20Sopenharmony_ci goto err_free_req; 25678c2ecf20Sopenharmony_ci 25688c2ecf20Sopenharmony_ci req->NameLength = cpu_to_le16(name_len * 2); 25698c2ecf20Sopenharmony_ci uni_path_len = copy_size; 25708c2ecf20Sopenharmony_ci /* free before overwriting resource */ 25718c2ecf20Sopenharmony_ci kfree(utf16_path); 25728c2ecf20Sopenharmony_ci utf16_path = copy_path; 25738c2ecf20Sopenharmony_ci } else { 25748c2ecf20Sopenharmony_ci uni_path_len = (2 * UniStrnlen((wchar_t *)utf16_path, PATH_MAX)) + 2; 25758c2ecf20Sopenharmony_ci /* MUST set path len (NameLength) to 0 opening root of share */ 25768c2ecf20Sopenharmony_ci req->NameLength = cpu_to_le16(uni_path_len - 2); 25778c2ecf20Sopenharmony_ci if (uni_path_len % 8 != 0) { 25788c2ecf20Sopenharmony_ci copy_size = roundup(uni_path_len, 8); 25798c2ecf20Sopenharmony_ci copy_path = kzalloc(copy_size, GFP_KERNEL); 25808c2ecf20Sopenharmony_ci if (!copy_path) { 25818c2ecf20Sopenharmony_ci rc = -ENOMEM; 25828c2ecf20Sopenharmony_ci goto err_free_req; 25838c2ecf20Sopenharmony_ci } 25848c2ecf20Sopenharmony_ci memcpy((char *)copy_path, (const char *)utf16_path, 25858c2ecf20Sopenharmony_ci uni_path_len); 25868c2ecf20Sopenharmony_ci uni_path_len = copy_size; 25878c2ecf20Sopenharmony_ci /* free before overwriting resource */ 25888c2ecf20Sopenharmony_ci kfree(utf16_path); 25898c2ecf20Sopenharmony_ci utf16_path = copy_path; 25908c2ecf20Sopenharmony_ci } 25918c2ecf20Sopenharmony_ci } 25928c2ecf20Sopenharmony_ci 25938c2ecf20Sopenharmony_ci iov[1].iov_len = uni_path_len; 25948c2ecf20Sopenharmony_ci iov[1].iov_base = utf16_path; 25958c2ecf20Sopenharmony_ci req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_NONE; 25968c2ecf20Sopenharmony_ci 25978c2ecf20Sopenharmony_ci if (tcon->posix_extensions) { 25988c2ecf20Sopenharmony_ci /* resource #3: posix buf */ 25998c2ecf20Sopenharmony_ci rc = add_posix_context(iov, &n_iov, mode); 26008c2ecf20Sopenharmony_ci if (rc) 26018c2ecf20Sopenharmony_ci goto err_free_req; 26028c2ecf20Sopenharmony_ci pc_buf = iov[n_iov-1].iov_base; 26038c2ecf20Sopenharmony_ci } 26048c2ecf20Sopenharmony_ci 26058c2ecf20Sopenharmony_ci 26068c2ecf20Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 26078c2ecf20Sopenharmony_ci rqst.rq_iov = iov; 26088c2ecf20Sopenharmony_ci rqst.rq_nvec = n_iov; 26098c2ecf20Sopenharmony_ci 26108c2ecf20Sopenharmony_ci /* no need to inc num_remote_opens because we close it just below */ 26118c2ecf20Sopenharmony_ci trace_smb3_posix_mkdir_enter(xid, tcon->tid, ses->Suid, CREATE_NOT_FILE, 26128c2ecf20Sopenharmony_ci FILE_WRITE_ATTRIBUTES); 26138c2ecf20Sopenharmony_ci /* resource #4: response buffer */ 26148c2ecf20Sopenharmony_ci rc = cifs_send_recv(xid, ses, server, 26158c2ecf20Sopenharmony_ci &rqst, &resp_buftype, flags, &rsp_iov); 26168c2ecf20Sopenharmony_ci if (rc) { 26178c2ecf20Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_CREATE_HE); 26188c2ecf20Sopenharmony_ci trace_smb3_posix_mkdir_err(xid, tcon->tid, ses->Suid, 26198c2ecf20Sopenharmony_ci CREATE_NOT_FILE, 26208c2ecf20Sopenharmony_ci FILE_WRITE_ATTRIBUTES, rc); 26218c2ecf20Sopenharmony_ci goto err_free_rsp_buf; 26228c2ecf20Sopenharmony_ci } 26238c2ecf20Sopenharmony_ci 26248c2ecf20Sopenharmony_ci rsp = (struct smb2_create_rsp *)rsp_iov.iov_base; 26258c2ecf20Sopenharmony_ci trace_smb3_posix_mkdir_done(xid, rsp->PersistentFileId, tcon->tid, 26268c2ecf20Sopenharmony_ci ses->Suid, CREATE_NOT_FILE, 26278c2ecf20Sopenharmony_ci FILE_WRITE_ATTRIBUTES); 26288c2ecf20Sopenharmony_ci 26298c2ecf20Sopenharmony_ci SMB2_close(xid, tcon, rsp->PersistentFileId, rsp->VolatileFileId); 26308c2ecf20Sopenharmony_ci 26318c2ecf20Sopenharmony_ci /* Eventually save off posix specific response info and timestaps */ 26328c2ecf20Sopenharmony_ci 26338c2ecf20Sopenharmony_cierr_free_rsp_buf: 26348c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype, rsp); 26358c2ecf20Sopenharmony_ci kfree(pc_buf); 26368c2ecf20Sopenharmony_cierr_free_req: 26378c2ecf20Sopenharmony_ci cifs_small_buf_release(req); 26388c2ecf20Sopenharmony_cierr_free_path: 26398c2ecf20Sopenharmony_ci kfree(utf16_path); 26408c2ecf20Sopenharmony_ci return rc; 26418c2ecf20Sopenharmony_ci} 26428c2ecf20Sopenharmony_ci 26438c2ecf20Sopenharmony_ciint 26448c2ecf20Sopenharmony_ciSMB2_open_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, 26458c2ecf20Sopenharmony_ci struct smb_rqst *rqst, __u8 *oplock, 26468c2ecf20Sopenharmony_ci struct cifs_open_parms *oparms, __le16 *path) 26478c2ecf20Sopenharmony_ci{ 26488c2ecf20Sopenharmony_ci struct smb2_create_req *req; 26498c2ecf20Sopenharmony_ci unsigned int n_iov = 2; 26508c2ecf20Sopenharmony_ci __u32 file_attributes = 0; 26518c2ecf20Sopenharmony_ci int copy_size; 26528c2ecf20Sopenharmony_ci int uni_path_len; 26538c2ecf20Sopenharmony_ci unsigned int total_len; 26548c2ecf20Sopenharmony_ci struct kvec *iov = rqst->rq_iov; 26558c2ecf20Sopenharmony_ci __le16 *copy_path; 26568c2ecf20Sopenharmony_ci int rc; 26578c2ecf20Sopenharmony_ci 26588c2ecf20Sopenharmony_ci rc = smb2_plain_req_init(SMB2_CREATE, tcon, server, 26598c2ecf20Sopenharmony_ci (void **) &req, &total_len); 26608c2ecf20Sopenharmony_ci if (rc) 26618c2ecf20Sopenharmony_ci return rc; 26628c2ecf20Sopenharmony_ci 26638c2ecf20Sopenharmony_ci iov[0].iov_base = (char *)req; 26648c2ecf20Sopenharmony_ci /* -1 since last byte is buf[0] which is sent below (path) */ 26658c2ecf20Sopenharmony_ci iov[0].iov_len = total_len - 1; 26668c2ecf20Sopenharmony_ci 26678c2ecf20Sopenharmony_ci if (oparms->create_options & CREATE_OPTION_READONLY) 26688c2ecf20Sopenharmony_ci file_attributes |= ATTR_READONLY; 26698c2ecf20Sopenharmony_ci if (oparms->create_options & CREATE_OPTION_SPECIAL) 26708c2ecf20Sopenharmony_ci file_attributes |= ATTR_SYSTEM; 26718c2ecf20Sopenharmony_ci 26728c2ecf20Sopenharmony_ci req->ImpersonationLevel = IL_IMPERSONATION; 26738c2ecf20Sopenharmony_ci req->DesiredAccess = cpu_to_le32(oparms->desired_access); 26748c2ecf20Sopenharmony_ci /* File attributes ignored on open (used in create though) */ 26758c2ecf20Sopenharmony_ci req->FileAttributes = cpu_to_le32(file_attributes); 26768c2ecf20Sopenharmony_ci req->ShareAccess = FILE_SHARE_ALL_LE; 26778c2ecf20Sopenharmony_ci 26788c2ecf20Sopenharmony_ci req->CreateDisposition = cpu_to_le32(oparms->disposition); 26798c2ecf20Sopenharmony_ci req->CreateOptions = cpu_to_le32(oparms->create_options & CREATE_OPTIONS_MASK); 26808c2ecf20Sopenharmony_ci req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req)); 26818c2ecf20Sopenharmony_ci 26828c2ecf20Sopenharmony_ci /* [MS-SMB2] 2.2.13 NameOffset: 26838c2ecf20Sopenharmony_ci * If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of 26848c2ecf20Sopenharmony_ci * the SMB2 header, the file name includes a prefix that will 26858c2ecf20Sopenharmony_ci * be processed during DFS name normalization as specified in 26868c2ecf20Sopenharmony_ci * section 3.3.5.9. Otherwise, the file name is relative to 26878c2ecf20Sopenharmony_ci * the share that is identified by the TreeId in the SMB2 26888c2ecf20Sopenharmony_ci * header. 26898c2ecf20Sopenharmony_ci */ 26908c2ecf20Sopenharmony_ci if (tcon->share_flags & SHI1005_FLAGS_DFS) { 26918c2ecf20Sopenharmony_ci int name_len; 26928c2ecf20Sopenharmony_ci 26938c2ecf20Sopenharmony_ci req->sync_hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS; 26948c2ecf20Sopenharmony_ci rc = alloc_path_with_tree_prefix(©_path, ©_size, 26958c2ecf20Sopenharmony_ci &name_len, 26968c2ecf20Sopenharmony_ci tcon->treeName, path); 26978c2ecf20Sopenharmony_ci if (rc) 26988c2ecf20Sopenharmony_ci return rc; 26998c2ecf20Sopenharmony_ci req->NameLength = cpu_to_le16(name_len * 2); 27008c2ecf20Sopenharmony_ci uni_path_len = copy_size; 27018c2ecf20Sopenharmony_ci path = copy_path; 27028c2ecf20Sopenharmony_ci } else { 27038c2ecf20Sopenharmony_ci uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2; 27048c2ecf20Sopenharmony_ci /* MUST set path len (NameLength) to 0 opening root of share */ 27058c2ecf20Sopenharmony_ci req->NameLength = cpu_to_le16(uni_path_len - 2); 27068c2ecf20Sopenharmony_ci copy_size = uni_path_len; 27078c2ecf20Sopenharmony_ci if (copy_size % 8 != 0) 27088c2ecf20Sopenharmony_ci copy_size = roundup(copy_size, 8); 27098c2ecf20Sopenharmony_ci copy_path = kzalloc(copy_size, GFP_KERNEL); 27108c2ecf20Sopenharmony_ci if (!copy_path) 27118c2ecf20Sopenharmony_ci return -ENOMEM; 27128c2ecf20Sopenharmony_ci memcpy((char *)copy_path, (const char *)path, 27138c2ecf20Sopenharmony_ci uni_path_len); 27148c2ecf20Sopenharmony_ci uni_path_len = copy_size; 27158c2ecf20Sopenharmony_ci path = copy_path; 27168c2ecf20Sopenharmony_ci } 27178c2ecf20Sopenharmony_ci 27188c2ecf20Sopenharmony_ci iov[1].iov_len = uni_path_len; 27198c2ecf20Sopenharmony_ci iov[1].iov_base = path; 27208c2ecf20Sopenharmony_ci 27218c2ecf20Sopenharmony_ci if ((!server->oplocks) || (tcon->no_lease)) 27228c2ecf20Sopenharmony_ci *oplock = SMB2_OPLOCK_LEVEL_NONE; 27238c2ecf20Sopenharmony_ci 27248c2ecf20Sopenharmony_ci if (!(server->capabilities & SMB2_GLOBAL_CAP_LEASING) || 27258c2ecf20Sopenharmony_ci *oplock == SMB2_OPLOCK_LEVEL_NONE) 27268c2ecf20Sopenharmony_ci req->RequestedOplockLevel = *oplock; 27278c2ecf20Sopenharmony_ci else if (!(server->capabilities & SMB2_GLOBAL_CAP_DIRECTORY_LEASING) && 27288c2ecf20Sopenharmony_ci (oparms->create_options & CREATE_NOT_FILE)) 27298c2ecf20Sopenharmony_ci req->RequestedOplockLevel = *oplock; /* no srv lease support */ 27308c2ecf20Sopenharmony_ci else { 27318c2ecf20Sopenharmony_ci rc = add_lease_context(server, iov, &n_iov, 27328c2ecf20Sopenharmony_ci oparms->fid->lease_key, oplock); 27338c2ecf20Sopenharmony_ci if (rc) 27348c2ecf20Sopenharmony_ci return rc; 27358c2ecf20Sopenharmony_ci } 27368c2ecf20Sopenharmony_ci 27378c2ecf20Sopenharmony_ci if (*oplock == SMB2_OPLOCK_LEVEL_BATCH) { 27388c2ecf20Sopenharmony_ci /* need to set Next field of lease context if we request it */ 27398c2ecf20Sopenharmony_ci if (server->capabilities & SMB2_GLOBAL_CAP_LEASING) { 27408c2ecf20Sopenharmony_ci struct create_context *ccontext = 27418c2ecf20Sopenharmony_ci (struct create_context *)iov[n_iov-1].iov_base; 27428c2ecf20Sopenharmony_ci ccontext->Next = 27438c2ecf20Sopenharmony_ci cpu_to_le32(server->vals->create_lease_size); 27448c2ecf20Sopenharmony_ci } 27458c2ecf20Sopenharmony_ci 27468c2ecf20Sopenharmony_ci rc = add_durable_context(iov, &n_iov, oparms, 27478c2ecf20Sopenharmony_ci tcon->use_persistent); 27488c2ecf20Sopenharmony_ci if (rc) 27498c2ecf20Sopenharmony_ci return rc; 27508c2ecf20Sopenharmony_ci } 27518c2ecf20Sopenharmony_ci 27528c2ecf20Sopenharmony_ci if (tcon->posix_extensions) { 27538c2ecf20Sopenharmony_ci if (n_iov > 2) { 27548c2ecf20Sopenharmony_ci struct create_context *ccontext = 27558c2ecf20Sopenharmony_ci (struct create_context *)iov[n_iov-1].iov_base; 27568c2ecf20Sopenharmony_ci ccontext->Next = 27578c2ecf20Sopenharmony_ci cpu_to_le32(iov[n_iov-1].iov_len); 27588c2ecf20Sopenharmony_ci } 27598c2ecf20Sopenharmony_ci 27608c2ecf20Sopenharmony_ci rc = add_posix_context(iov, &n_iov, oparms->mode); 27618c2ecf20Sopenharmony_ci if (rc) 27628c2ecf20Sopenharmony_ci return rc; 27638c2ecf20Sopenharmony_ci } 27648c2ecf20Sopenharmony_ci 27658c2ecf20Sopenharmony_ci if (tcon->snapshot_time) { 27668c2ecf20Sopenharmony_ci cifs_dbg(FYI, "adding snapshot context\n"); 27678c2ecf20Sopenharmony_ci if (n_iov > 2) { 27688c2ecf20Sopenharmony_ci struct create_context *ccontext = 27698c2ecf20Sopenharmony_ci (struct create_context *)iov[n_iov-1].iov_base; 27708c2ecf20Sopenharmony_ci ccontext->Next = 27718c2ecf20Sopenharmony_ci cpu_to_le32(iov[n_iov-1].iov_len); 27728c2ecf20Sopenharmony_ci } 27738c2ecf20Sopenharmony_ci 27748c2ecf20Sopenharmony_ci rc = add_twarp_context(iov, &n_iov, tcon->snapshot_time); 27758c2ecf20Sopenharmony_ci if (rc) 27768c2ecf20Sopenharmony_ci return rc; 27778c2ecf20Sopenharmony_ci } 27788c2ecf20Sopenharmony_ci 27798c2ecf20Sopenharmony_ci if ((oparms->disposition != FILE_OPEN) && (oparms->cifs_sb)) { 27808c2ecf20Sopenharmony_ci bool set_mode; 27818c2ecf20Sopenharmony_ci bool set_owner; 27828c2ecf20Sopenharmony_ci 27838c2ecf20Sopenharmony_ci if ((oparms->cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID) && 27848c2ecf20Sopenharmony_ci (oparms->mode != ACL_NO_MODE)) 27858c2ecf20Sopenharmony_ci set_mode = true; 27868c2ecf20Sopenharmony_ci else { 27878c2ecf20Sopenharmony_ci set_mode = false; 27888c2ecf20Sopenharmony_ci oparms->mode = ACL_NO_MODE; 27898c2ecf20Sopenharmony_ci } 27908c2ecf20Sopenharmony_ci 27918c2ecf20Sopenharmony_ci if (oparms->cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL) 27928c2ecf20Sopenharmony_ci set_owner = true; 27938c2ecf20Sopenharmony_ci else 27948c2ecf20Sopenharmony_ci set_owner = false; 27958c2ecf20Sopenharmony_ci 27968c2ecf20Sopenharmony_ci if (set_owner | set_mode) { 27978c2ecf20Sopenharmony_ci if (n_iov > 2) { 27988c2ecf20Sopenharmony_ci struct create_context *ccontext = 27998c2ecf20Sopenharmony_ci (struct create_context *)iov[n_iov-1].iov_base; 28008c2ecf20Sopenharmony_ci ccontext->Next = cpu_to_le32(iov[n_iov-1].iov_len); 28018c2ecf20Sopenharmony_ci } 28028c2ecf20Sopenharmony_ci 28038c2ecf20Sopenharmony_ci cifs_dbg(FYI, "add sd with mode 0x%x\n", oparms->mode); 28048c2ecf20Sopenharmony_ci rc = add_sd_context(iov, &n_iov, oparms->mode, set_owner); 28058c2ecf20Sopenharmony_ci if (rc) 28068c2ecf20Sopenharmony_ci return rc; 28078c2ecf20Sopenharmony_ci } 28088c2ecf20Sopenharmony_ci } 28098c2ecf20Sopenharmony_ci 28108c2ecf20Sopenharmony_ci if (n_iov > 2) { 28118c2ecf20Sopenharmony_ci struct create_context *ccontext = 28128c2ecf20Sopenharmony_ci (struct create_context *)iov[n_iov-1].iov_base; 28138c2ecf20Sopenharmony_ci ccontext->Next = cpu_to_le32(iov[n_iov-1].iov_len); 28148c2ecf20Sopenharmony_ci } 28158c2ecf20Sopenharmony_ci add_query_id_context(iov, &n_iov); 28168c2ecf20Sopenharmony_ci 28178c2ecf20Sopenharmony_ci rqst->rq_nvec = n_iov; 28188c2ecf20Sopenharmony_ci return 0; 28198c2ecf20Sopenharmony_ci} 28208c2ecf20Sopenharmony_ci 28218c2ecf20Sopenharmony_ci/* rq_iov[0] is the request and is released by cifs_small_buf_release(). 28228c2ecf20Sopenharmony_ci * All other vectors are freed by kfree(). 28238c2ecf20Sopenharmony_ci */ 28248c2ecf20Sopenharmony_civoid 28258c2ecf20Sopenharmony_ciSMB2_open_free(struct smb_rqst *rqst) 28268c2ecf20Sopenharmony_ci{ 28278c2ecf20Sopenharmony_ci int i; 28288c2ecf20Sopenharmony_ci 28298c2ecf20Sopenharmony_ci if (rqst && rqst->rq_iov) { 28308c2ecf20Sopenharmony_ci cifs_small_buf_release(rqst->rq_iov[0].iov_base); 28318c2ecf20Sopenharmony_ci for (i = 1; i < rqst->rq_nvec; i++) 28328c2ecf20Sopenharmony_ci if (rqst->rq_iov[i].iov_base != smb2_padding) 28338c2ecf20Sopenharmony_ci kfree(rqst->rq_iov[i].iov_base); 28348c2ecf20Sopenharmony_ci } 28358c2ecf20Sopenharmony_ci} 28368c2ecf20Sopenharmony_ci 28378c2ecf20Sopenharmony_ciint 28388c2ecf20Sopenharmony_ciSMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, 28398c2ecf20Sopenharmony_ci __u8 *oplock, struct smb2_file_all_info *buf, 28408c2ecf20Sopenharmony_ci struct create_posix_rsp *posix, 28418c2ecf20Sopenharmony_ci struct kvec *err_iov, int *buftype) 28428c2ecf20Sopenharmony_ci{ 28438c2ecf20Sopenharmony_ci struct smb_rqst rqst; 28448c2ecf20Sopenharmony_ci struct smb2_create_rsp *rsp = NULL; 28458c2ecf20Sopenharmony_ci struct cifs_tcon *tcon = oparms->tcon; 28468c2ecf20Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 28478c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(ses); 28488c2ecf20Sopenharmony_ci struct kvec iov[SMB2_CREATE_IOV_SIZE]; 28498c2ecf20Sopenharmony_ci struct kvec rsp_iov = {NULL, 0}; 28508c2ecf20Sopenharmony_ci int resp_buftype = CIFS_NO_BUFFER; 28518c2ecf20Sopenharmony_ci int rc = 0; 28528c2ecf20Sopenharmony_ci int flags = 0; 28538c2ecf20Sopenharmony_ci 28548c2ecf20Sopenharmony_ci cifs_dbg(FYI, "create/open\n"); 28558c2ecf20Sopenharmony_ci if (!ses || !server) 28568c2ecf20Sopenharmony_ci return -EIO; 28578c2ecf20Sopenharmony_ci 28588c2ecf20Sopenharmony_ci if (smb3_encryption_required(tcon)) 28598c2ecf20Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 28608c2ecf20Sopenharmony_ci 28618c2ecf20Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 28628c2ecf20Sopenharmony_ci memset(&iov, 0, sizeof(iov)); 28638c2ecf20Sopenharmony_ci rqst.rq_iov = iov; 28648c2ecf20Sopenharmony_ci rqst.rq_nvec = SMB2_CREATE_IOV_SIZE; 28658c2ecf20Sopenharmony_ci 28668c2ecf20Sopenharmony_ci rc = SMB2_open_init(tcon, server, 28678c2ecf20Sopenharmony_ci &rqst, oplock, oparms, path); 28688c2ecf20Sopenharmony_ci if (rc) 28698c2ecf20Sopenharmony_ci goto creat_exit; 28708c2ecf20Sopenharmony_ci 28718c2ecf20Sopenharmony_ci trace_smb3_open_enter(xid, tcon->tid, tcon->ses->Suid, 28728c2ecf20Sopenharmony_ci oparms->create_options, oparms->desired_access); 28738c2ecf20Sopenharmony_ci 28748c2ecf20Sopenharmony_ci rc = cifs_send_recv(xid, ses, server, 28758c2ecf20Sopenharmony_ci &rqst, &resp_buftype, flags, 28768c2ecf20Sopenharmony_ci &rsp_iov); 28778c2ecf20Sopenharmony_ci rsp = (struct smb2_create_rsp *)rsp_iov.iov_base; 28788c2ecf20Sopenharmony_ci 28798c2ecf20Sopenharmony_ci if (rc != 0) { 28808c2ecf20Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_CREATE_HE); 28818c2ecf20Sopenharmony_ci if (err_iov && rsp) { 28828c2ecf20Sopenharmony_ci *err_iov = rsp_iov; 28838c2ecf20Sopenharmony_ci *buftype = resp_buftype; 28848c2ecf20Sopenharmony_ci resp_buftype = CIFS_NO_BUFFER; 28858c2ecf20Sopenharmony_ci rsp = NULL; 28868c2ecf20Sopenharmony_ci } 28878c2ecf20Sopenharmony_ci trace_smb3_open_err(xid, tcon->tid, ses->Suid, 28888c2ecf20Sopenharmony_ci oparms->create_options, oparms->desired_access, rc); 28898c2ecf20Sopenharmony_ci if (rc == -EREMCHG) { 28908c2ecf20Sopenharmony_ci pr_warn_once("server share %s deleted\n", 28918c2ecf20Sopenharmony_ci tcon->treeName); 28928c2ecf20Sopenharmony_ci tcon->need_reconnect = true; 28938c2ecf20Sopenharmony_ci } 28948c2ecf20Sopenharmony_ci goto creat_exit; 28958c2ecf20Sopenharmony_ci } else 28968c2ecf20Sopenharmony_ci trace_smb3_open_done(xid, rsp->PersistentFileId, tcon->tid, 28978c2ecf20Sopenharmony_ci ses->Suid, oparms->create_options, 28988c2ecf20Sopenharmony_ci oparms->desired_access); 28998c2ecf20Sopenharmony_ci 29008c2ecf20Sopenharmony_ci atomic_inc(&tcon->num_remote_opens); 29018c2ecf20Sopenharmony_ci oparms->fid->persistent_fid = rsp->PersistentFileId; 29028c2ecf20Sopenharmony_ci oparms->fid->volatile_fid = rsp->VolatileFileId; 29038c2ecf20Sopenharmony_ci oparms->fid->access = oparms->desired_access; 29048c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_DEBUG2 29058c2ecf20Sopenharmony_ci oparms->fid->mid = le64_to_cpu(rsp->sync_hdr.MessageId); 29068c2ecf20Sopenharmony_ci#endif /* CIFS_DEBUG2 */ 29078c2ecf20Sopenharmony_ci 29088c2ecf20Sopenharmony_ci if (buf) { 29098c2ecf20Sopenharmony_ci memcpy(buf, &rsp->CreationTime, 32); 29108c2ecf20Sopenharmony_ci buf->AllocationSize = rsp->AllocationSize; 29118c2ecf20Sopenharmony_ci buf->EndOfFile = rsp->EndofFile; 29128c2ecf20Sopenharmony_ci buf->Attributes = rsp->FileAttributes; 29138c2ecf20Sopenharmony_ci buf->NumberOfLinks = cpu_to_le32(1); 29148c2ecf20Sopenharmony_ci buf->DeletePending = 0; 29158c2ecf20Sopenharmony_ci } 29168c2ecf20Sopenharmony_ci 29178c2ecf20Sopenharmony_ci 29188c2ecf20Sopenharmony_ci smb2_parse_contexts(server, rsp, &oparms->fid->epoch, 29198c2ecf20Sopenharmony_ci oparms->fid->lease_key, oplock, buf, posix); 29208c2ecf20Sopenharmony_cicreat_exit: 29218c2ecf20Sopenharmony_ci SMB2_open_free(&rqst); 29228c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype, rsp); 29238c2ecf20Sopenharmony_ci return rc; 29248c2ecf20Sopenharmony_ci} 29258c2ecf20Sopenharmony_ci 29268c2ecf20Sopenharmony_ciint 29278c2ecf20Sopenharmony_ciSMB2_ioctl_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, 29288c2ecf20Sopenharmony_ci struct smb_rqst *rqst, 29298c2ecf20Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, u32 opcode, 29308c2ecf20Sopenharmony_ci char *in_data, u32 indatalen, 29318c2ecf20Sopenharmony_ci __u32 max_response_size) 29328c2ecf20Sopenharmony_ci{ 29338c2ecf20Sopenharmony_ci struct smb2_ioctl_req *req; 29348c2ecf20Sopenharmony_ci struct kvec *iov = rqst->rq_iov; 29358c2ecf20Sopenharmony_ci unsigned int total_len; 29368c2ecf20Sopenharmony_ci int rc; 29378c2ecf20Sopenharmony_ci char *in_data_buf; 29388c2ecf20Sopenharmony_ci 29398c2ecf20Sopenharmony_ci rc = smb2_ioctl_req_init(opcode, tcon, server, 29408c2ecf20Sopenharmony_ci (void **) &req, &total_len); 29418c2ecf20Sopenharmony_ci if (rc) 29428c2ecf20Sopenharmony_ci return rc; 29438c2ecf20Sopenharmony_ci 29448c2ecf20Sopenharmony_ci if (indatalen) { 29458c2ecf20Sopenharmony_ci /* 29468c2ecf20Sopenharmony_ci * indatalen is usually small at a couple of bytes max, so 29478c2ecf20Sopenharmony_ci * just allocate through generic pool 29488c2ecf20Sopenharmony_ci */ 29498c2ecf20Sopenharmony_ci in_data_buf = kmemdup(in_data, indatalen, GFP_NOFS); 29508c2ecf20Sopenharmony_ci if (!in_data_buf) { 29518c2ecf20Sopenharmony_ci cifs_small_buf_release(req); 29528c2ecf20Sopenharmony_ci return -ENOMEM; 29538c2ecf20Sopenharmony_ci } 29548c2ecf20Sopenharmony_ci } 29558c2ecf20Sopenharmony_ci 29568c2ecf20Sopenharmony_ci req->CtlCode = cpu_to_le32(opcode); 29578c2ecf20Sopenharmony_ci req->PersistentFileId = persistent_fid; 29588c2ecf20Sopenharmony_ci req->VolatileFileId = volatile_fid; 29598c2ecf20Sopenharmony_ci 29608c2ecf20Sopenharmony_ci iov[0].iov_base = (char *)req; 29618c2ecf20Sopenharmony_ci /* 29628c2ecf20Sopenharmony_ci * If no input data, the size of ioctl struct in 29638c2ecf20Sopenharmony_ci * protocol spec still includes a 1 byte data buffer, 29648c2ecf20Sopenharmony_ci * but if input data passed to ioctl, we do not 29658c2ecf20Sopenharmony_ci * want to double count this, so we do not send 29668c2ecf20Sopenharmony_ci * the dummy one byte of data in iovec[0] if sending 29678c2ecf20Sopenharmony_ci * input data (in iovec[1]). 29688c2ecf20Sopenharmony_ci */ 29698c2ecf20Sopenharmony_ci if (indatalen) { 29708c2ecf20Sopenharmony_ci req->InputCount = cpu_to_le32(indatalen); 29718c2ecf20Sopenharmony_ci /* do not set InputOffset if no input data */ 29728c2ecf20Sopenharmony_ci req->InputOffset = 29738c2ecf20Sopenharmony_ci cpu_to_le32(offsetof(struct smb2_ioctl_req, Buffer)); 29748c2ecf20Sopenharmony_ci rqst->rq_nvec = 2; 29758c2ecf20Sopenharmony_ci iov[0].iov_len = total_len - 1; 29768c2ecf20Sopenharmony_ci iov[1].iov_base = in_data_buf; 29778c2ecf20Sopenharmony_ci iov[1].iov_len = indatalen; 29788c2ecf20Sopenharmony_ci } else { 29798c2ecf20Sopenharmony_ci rqst->rq_nvec = 1; 29808c2ecf20Sopenharmony_ci iov[0].iov_len = total_len; 29818c2ecf20Sopenharmony_ci } 29828c2ecf20Sopenharmony_ci 29838c2ecf20Sopenharmony_ci req->OutputOffset = 0; 29848c2ecf20Sopenharmony_ci req->OutputCount = 0; /* MBZ */ 29858c2ecf20Sopenharmony_ci 29868c2ecf20Sopenharmony_ci /* 29878c2ecf20Sopenharmony_ci * In most cases max_response_size is set to 16K (CIFSMaxBufSize) 29888c2ecf20Sopenharmony_ci * We Could increase default MaxOutputResponse, but that could require 29898c2ecf20Sopenharmony_ci * more credits. Windows typically sets this smaller, but for some 29908c2ecf20Sopenharmony_ci * ioctls it may be useful to allow server to send more. No point 29918c2ecf20Sopenharmony_ci * limiting what the server can send as long as fits in one credit 29928c2ecf20Sopenharmony_ci * We can not handle more than CIFS_MAX_BUF_SIZE yet but may want 29938c2ecf20Sopenharmony_ci * to increase this limit up in the future. 29948c2ecf20Sopenharmony_ci * Note that for snapshot queries that servers like Azure expect that 29958c2ecf20Sopenharmony_ci * the first query be minimal size (and just used to get the number/size 29968c2ecf20Sopenharmony_ci * of previous versions) so response size must be specified as EXACTLY 29978c2ecf20Sopenharmony_ci * sizeof(struct snapshot_array) which is 16 when rounded up to multiple 29988c2ecf20Sopenharmony_ci * of eight bytes. Currently that is the only case where we set max 29998c2ecf20Sopenharmony_ci * response size smaller. 30008c2ecf20Sopenharmony_ci */ 30018c2ecf20Sopenharmony_ci req->MaxOutputResponse = cpu_to_le32(max_response_size); 30028c2ecf20Sopenharmony_ci req->sync_hdr.CreditCharge = 30038c2ecf20Sopenharmony_ci cpu_to_le16(DIV_ROUND_UP(max(indatalen, max_response_size), 30048c2ecf20Sopenharmony_ci SMB2_MAX_BUFFER_SIZE)); 30058c2ecf20Sopenharmony_ci /* always an FSCTL (for now) */ 30068c2ecf20Sopenharmony_ci req->Flags = cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL); 30078c2ecf20Sopenharmony_ci 30088c2ecf20Sopenharmony_ci /* validate negotiate request must be signed - see MS-SMB2 3.2.5.5 */ 30098c2ecf20Sopenharmony_ci if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO) 30108c2ecf20Sopenharmony_ci req->sync_hdr.Flags |= SMB2_FLAGS_SIGNED; 30118c2ecf20Sopenharmony_ci 30128c2ecf20Sopenharmony_ci return 0; 30138c2ecf20Sopenharmony_ci} 30148c2ecf20Sopenharmony_ci 30158c2ecf20Sopenharmony_civoid 30168c2ecf20Sopenharmony_ciSMB2_ioctl_free(struct smb_rqst *rqst) 30178c2ecf20Sopenharmony_ci{ 30188c2ecf20Sopenharmony_ci int i; 30198c2ecf20Sopenharmony_ci if (rqst && rqst->rq_iov) { 30208c2ecf20Sopenharmony_ci cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ 30218c2ecf20Sopenharmony_ci for (i = 1; i < rqst->rq_nvec; i++) 30228c2ecf20Sopenharmony_ci if (rqst->rq_iov[i].iov_base != smb2_padding) 30238c2ecf20Sopenharmony_ci kfree(rqst->rq_iov[i].iov_base); 30248c2ecf20Sopenharmony_ci } 30258c2ecf20Sopenharmony_ci} 30268c2ecf20Sopenharmony_ci 30278c2ecf20Sopenharmony_ci 30288c2ecf20Sopenharmony_ci/* 30298c2ecf20Sopenharmony_ci * SMB2 IOCTL is used for both IOCTLs and FSCTLs 30308c2ecf20Sopenharmony_ci */ 30318c2ecf20Sopenharmony_ciint 30328c2ecf20Sopenharmony_ciSMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, 30338c2ecf20Sopenharmony_ci u64 volatile_fid, u32 opcode, char *in_data, u32 indatalen, 30348c2ecf20Sopenharmony_ci u32 max_out_data_len, char **out_data, 30358c2ecf20Sopenharmony_ci u32 *plen /* returned data len */) 30368c2ecf20Sopenharmony_ci{ 30378c2ecf20Sopenharmony_ci struct smb_rqst rqst; 30388c2ecf20Sopenharmony_ci struct smb2_ioctl_rsp *rsp = NULL; 30398c2ecf20Sopenharmony_ci struct cifs_ses *ses; 30408c2ecf20Sopenharmony_ci struct TCP_Server_Info *server; 30418c2ecf20Sopenharmony_ci struct kvec iov[SMB2_IOCTL_IOV_SIZE]; 30428c2ecf20Sopenharmony_ci struct kvec rsp_iov = {NULL, 0}; 30438c2ecf20Sopenharmony_ci int resp_buftype = CIFS_NO_BUFFER; 30448c2ecf20Sopenharmony_ci int rc = 0; 30458c2ecf20Sopenharmony_ci int flags = 0; 30468c2ecf20Sopenharmony_ci 30478c2ecf20Sopenharmony_ci cifs_dbg(FYI, "SMB2 IOCTL\n"); 30488c2ecf20Sopenharmony_ci 30498c2ecf20Sopenharmony_ci if (out_data != NULL) 30508c2ecf20Sopenharmony_ci *out_data = NULL; 30518c2ecf20Sopenharmony_ci 30528c2ecf20Sopenharmony_ci /* zero out returned data len, in case of error */ 30538c2ecf20Sopenharmony_ci if (plen) 30548c2ecf20Sopenharmony_ci *plen = 0; 30558c2ecf20Sopenharmony_ci 30568c2ecf20Sopenharmony_ci if (!tcon) 30578c2ecf20Sopenharmony_ci return -EIO; 30588c2ecf20Sopenharmony_ci 30598c2ecf20Sopenharmony_ci ses = tcon->ses; 30608c2ecf20Sopenharmony_ci if (!ses) 30618c2ecf20Sopenharmony_ci return -EIO; 30628c2ecf20Sopenharmony_ci 30638c2ecf20Sopenharmony_ci server = cifs_pick_channel(ses); 30648c2ecf20Sopenharmony_ci if (!server) 30658c2ecf20Sopenharmony_ci return -EIO; 30668c2ecf20Sopenharmony_ci 30678c2ecf20Sopenharmony_ci if (smb3_encryption_required(tcon)) 30688c2ecf20Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 30698c2ecf20Sopenharmony_ci 30708c2ecf20Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 30718c2ecf20Sopenharmony_ci memset(&iov, 0, sizeof(iov)); 30728c2ecf20Sopenharmony_ci rqst.rq_iov = iov; 30738c2ecf20Sopenharmony_ci rqst.rq_nvec = SMB2_IOCTL_IOV_SIZE; 30748c2ecf20Sopenharmony_ci 30758c2ecf20Sopenharmony_ci rc = SMB2_ioctl_init(tcon, server, 30768c2ecf20Sopenharmony_ci &rqst, persistent_fid, volatile_fid, opcode, 30778c2ecf20Sopenharmony_ci in_data, indatalen, max_out_data_len); 30788c2ecf20Sopenharmony_ci if (rc) 30798c2ecf20Sopenharmony_ci goto ioctl_exit; 30808c2ecf20Sopenharmony_ci 30818c2ecf20Sopenharmony_ci rc = cifs_send_recv(xid, ses, server, 30828c2ecf20Sopenharmony_ci &rqst, &resp_buftype, flags, 30838c2ecf20Sopenharmony_ci &rsp_iov); 30848c2ecf20Sopenharmony_ci rsp = (struct smb2_ioctl_rsp *)rsp_iov.iov_base; 30858c2ecf20Sopenharmony_ci 30868c2ecf20Sopenharmony_ci if (rc != 0) 30878c2ecf20Sopenharmony_ci trace_smb3_fsctl_err(xid, persistent_fid, tcon->tid, 30888c2ecf20Sopenharmony_ci ses->Suid, 0, opcode, rc); 30898c2ecf20Sopenharmony_ci 30908c2ecf20Sopenharmony_ci if ((rc != 0) && (rc != -EINVAL) && (rc != -E2BIG)) { 30918c2ecf20Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); 30928c2ecf20Sopenharmony_ci goto ioctl_exit; 30938c2ecf20Sopenharmony_ci } else if (rc == -EINVAL) { 30948c2ecf20Sopenharmony_ci if ((opcode != FSCTL_SRV_COPYCHUNK_WRITE) && 30958c2ecf20Sopenharmony_ci (opcode != FSCTL_SRV_COPYCHUNK)) { 30968c2ecf20Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); 30978c2ecf20Sopenharmony_ci goto ioctl_exit; 30988c2ecf20Sopenharmony_ci } 30998c2ecf20Sopenharmony_ci } else if (rc == -E2BIG) { 31008c2ecf20Sopenharmony_ci if (opcode != FSCTL_QUERY_ALLOCATED_RANGES) { 31018c2ecf20Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); 31028c2ecf20Sopenharmony_ci goto ioctl_exit; 31038c2ecf20Sopenharmony_ci } 31048c2ecf20Sopenharmony_ci } 31058c2ecf20Sopenharmony_ci 31068c2ecf20Sopenharmony_ci /* check if caller wants to look at return data or just return rc */ 31078c2ecf20Sopenharmony_ci if ((plen == NULL) || (out_data == NULL)) 31088c2ecf20Sopenharmony_ci goto ioctl_exit; 31098c2ecf20Sopenharmony_ci 31108c2ecf20Sopenharmony_ci *plen = le32_to_cpu(rsp->OutputCount); 31118c2ecf20Sopenharmony_ci 31128c2ecf20Sopenharmony_ci /* We check for obvious errors in the output buffer length and offset */ 31138c2ecf20Sopenharmony_ci if (*plen == 0) 31148c2ecf20Sopenharmony_ci goto ioctl_exit; /* server returned no data */ 31158c2ecf20Sopenharmony_ci else if (*plen > rsp_iov.iov_len || *plen > 0xFF00) { 31168c2ecf20Sopenharmony_ci cifs_tcon_dbg(VFS, "srv returned invalid ioctl length: %d\n", *plen); 31178c2ecf20Sopenharmony_ci *plen = 0; 31188c2ecf20Sopenharmony_ci rc = -EIO; 31198c2ecf20Sopenharmony_ci goto ioctl_exit; 31208c2ecf20Sopenharmony_ci } 31218c2ecf20Sopenharmony_ci 31228c2ecf20Sopenharmony_ci if (rsp_iov.iov_len - *plen < le32_to_cpu(rsp->OutputOffset)) { 31238c2ecf20Sopenharmony_ci cifs_tcon_dbg(VFS, "Malformed ioctl resp: len %d offset %d\n", *plen, 31248c2ecf20Sopenharmony_ci le32_to_cpu(rsp->OutputOffset)); 31258c2ecf20Sopenharmony_ci *plen = 0; 31268c2ecf20Sopenharmony_ci rc = -EIO; 31278c2ecf20Sopenharmony_ci goto ioctl_exit; 31288c2ecf20Sopenharmony_ci } 31298c2ecf20Sopenharmony_ci 31308c2ecf20Sopenharmony_ci *out_data = kmemdup((char *)rsp + le32_to_cpu(rsp->OutputOffset), 31318c2ecf20Sopenharmony_ci *plen, GFP_KERNEL); 31328c2ecf20Sopenharmony_ci if (*out_data == NULL) { 31338c2ecf20Sopenharmony_ci rc = -ENOMEM; 31348c2ecf20Sopenharmony_ci goto ioctl_exit; 31358c2ecf20Sopenharmony_ci } 31368c2ecf20Sopenharmony_ci 31378c2ecf20Sopenharmony_ciioctl_exit: 31388c2ecf20Sopenharmony_ci SMB2_ioctl_free(&rqst); 31398c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype, rsp); 31408c2ecf20Sopenharmony_ci return rc; 31418c2ecf20Sopenharmony_ci} 31428c2ecf20Sopenharmony_ci 31438c2ecf20Sopenharmony_ci/* 31448c2ecf20Sopenharmony_ci * Individual callers to ioctl worker function follow 31458c2ecf20Sopenharmony_ci */ 31468c2ecf20Sopenharmony_ci 31478c2ecf20Sopenharmony_ciint 31488c2ecf20Sopenharmony_ciSMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, 31498c2ecf20Sopenharmony_ci u64 persistent_fid, u64 volatile_fid) 31508c2ecf20Sopenharmony_ci{ 31518c2ecf20Sopenharmony_ci int rc; 31528c2ecf20Sopenharmony_ci struct compress_ioctl fsctl_input; 31538c2ecf20Sopenharmony_ci char *ret_data = NULL; 31548c2ecf20Sopenharmony_ci 31558c2ecf20Sopenharmony_ci fsctl_input.CompressionState = 31568c2ecf20Sopenharmony_ci cpu_to_le16(COMPRESSION_FORMAT_DEFAULT); 31578c2ecf20Sopenharmony_ci 31588c2ecf20Sopenharmony_ci rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid, 31598c2ecf20Sopenharmony_ci FSCTL_SET_COMPRESSION, 31608c2ecf20Sopenharmony_ci (char *)&fsctl_input /* data input */, 31618c2ecf20Sopenharmony_ci 2 /* in data len */, CIFSMaxBufSize /* max out data */, 31628c2ecf20Sopenharmony_ci &ret_data /* out data */, NULL); 31638c2ecf20Sopenharmony_ci 31648c2ecf20Sopenharmony_ci cifs_dbg(FYI, "set compression rc %d\n", rc); 31658c2ecf20Sopenharmony_ci 31668c2ecf20Sopenharmony_ci return rc; 31678c2ecf20Sopenharmony_ci} 31688c2ecf20Sopenharmony_ci 31698c2ecf20Sopenharmony_ciint 31708c2ecf20Sopenharmony_ciSMB2_close_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, 31718c2ecf20Sopenharmony_ci struct smb_rqst *rqst, 31728c2ecf20Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, bool query_attrs) 31738c2ecf20Sopenharmony_ci{ 31748c2ecf20Sopenharmony_ci struct smb2_close_req *req; 31758c2ecf20Sopenharmony_ci struct kvec *iov = rqst->rq_iov; 31768c2ecf20Sopenharmony_ci unsigned int total_len; 31778c2ecf20Sopenharmony_ci int rc; 31788c2ecf20Sopenharmony_ci 31798c2ecf20Sopenharmony_ci rc = smb2_plain_req_init(SMB2_CLOSE, tcon, server, 31808c2ecf20Sopenharmony_ci (void **) &req, &total_len); 31818c2ecf20Sopenharmony_ci if (rc) 31828c2ecf20Sopenharmony_ci return rc; 31838c2ecf20Sopenharmony_ci 31848c2ecf20Sopenharmony_ci req->PersistentFileId = persistent_fid; 31858c2ecf20Sopenharmony_ci req->VolatileFileId = volatile_fid; 31868c2ecf20Sopenharmony_ci if (query_attrs) 31878c2ecf20Sopenharmony_ci req->Flags = SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB; 31888c2ecf20Sopenharmony_ci else 31898c2ecf20Sopenharmony_ci req->Flags = 0; 31908c2ecf20Sopenharmony_ci iov[0].iov_base = (char *)req; 31918c2ecf20Sopenharmony_ci iov[0].iov_len = total_len; 31928c2ecf20Sopenharmony_ci 31938c2ecf20Sopenharmony_ci return 0; 31948c2ecf20Sopenharmony_ci} 31958c2ecf20Sopenharmony_ci 31968c2ecf20Sopenharmony_civoid 31978c2ecf20Sopenharmony_ciSMB2_close_free(struct smb_rqst *rqst) 31988c2ecf20Sopenharmony_ci{ 31998c2ecf20Sopenharmony_ci if (rqst && rqst->rq_iov) 32008c2ecf20Sopenharmony_ci cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ 32018c2ecf20Sopenharmony_ci} 32028c2ecf20Sopenharmony_ci 32038c2ecf20Sopenharmony_ciint 32048c2ecf20Sopenharmony_ci__SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, 32058c2ecf20Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, 32068c2ecf20Sopenharmony_ci struct smb2_file_network_open_info *pbuf) 32078c2ecf20Sopenharmony_ci{ 32088c2ecf20Sopenharmony_ci struct smb_rqst rqst; 32098c2ecf20Sopenharmony_ci struct smb2_close_rsp *rsp = NULL; 32108c2ecf20Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 32118c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(ses); 32128c2ecf20Sopenharmony_ci struct kvec iov[1]; 32138c2ecf20Sopenharmony_ci struct kvec rsp_iov; 32148c2ecf20Sopenharmony_ci int resp_buftype = CIFS_NO_BUFFER; 32158c2ecf20Sopenharmony_ci int rc = 0; 32168c2ecf20Sopenharmony_ci int flags = 0; 32178c2ecf20Sopenharmony_ci bool query_attrs = false; 32188c2ecf20Sopenharmony_ci 32198c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Close\n"); 32208c2ecf20Sopenharmony_ci 32218c2ecf20Sopenharmony_ci if (!ses || !server) 32228c2ecf20Sopenharmony_ci return -EIO; 32238c2ecf20Sopenharmony_ci 32248c2ecf20Sopenharmony_ci if (smb3_encryption_required(tcon)) 32258c2ecf20Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 32268c2ecf20Sopenharmony_ci 32278c2ecf20Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 32288c2ecf20Sopenharmony_ci memset(&iov, 0, sizeof(iov)); 32298c2ecf20Sopenharmony_ci rqst.rq_iov = iov; 32308c2ecf20Sopenharmony_ci rqst.rq_nvec = 1; 32318c2ecf20Sopenharmony_ci 32328c2ecf20Sopenharmony_ci /* check if need to ask server to return timestamps in close response */ 32338c2ecf20Sopenharmony_ci if (pbuf) 32348c2ecf20Sopenharmony_ci query_attrs = true; 32358c2ecf20Sopenharmony_ci 32368c2ecf20Sopenharmony_ci trace_smb3_close_enter(xid, persistent_fid, tcon->tid, ses->Suid); 32378c2ecf20Sopenharmony_ci rc = SMB2_close_init(tcon, server, 32388c2ecf20Sopenharmony_ci &rqst, persistent_fid, volatile_fid, 32398c2ecf20Sopenharmony_ci query_attrs); 32408c2ecf20Sopenharmony_ci if (rc) 32418c2ecf20Sopenharmony_ci goto close_exit; 32428c2ecf20Sopenharmony_ci 32438c2ecf20Sopenharmony_ci rc = cifs_send_recv(xid, ses, server, 32448c2ecf20Sopenharmony_ci &rqst, &resp_buftype, flags, &rsp_iov); 32458c2ecf20Sopenharmony_ci rsp = (struct smb2_close_rsp *)rsp_iov.iov_base; 32468c2ecf20Sopenharmony_ci 32478c2ecf20Sopenharmony_ci if (rc != 0) { 32488c2ecf20Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_CLOSE_HE); 32498c2ecf20Sopenharmony_ci trace_smb3_close_err(xid, persistent_fid, tcon->tid, ses->Suid, 32508c2ecf20Sopenharmony_ci rc); 32518c2ecf20Sopenharmony_ci goto close_exit; 32528c2ecf20Sopenharmony_ci } else { 32538c2ecf20Sopenharmony_ci trace_smb3_close_done(xid, persistent_fid, tcon->tid, 32548c2ecf20Sopenharmony_ci ses->Suid); 32558c2ecf20Sopenharmony_ci /* 32568c2ecf20Sopenharmony_ci * Note that have to subtract 4 since struct network_open_info 32578c2ecf20Sopenharmony_ci * has a final 4 byte pad that close response does not have 32588c2ecf20Sopenharmony_ci */ 32598c2ecf20Sopenharmony_ci if (pbuf) 32608c2ecf20Sopenharmony_ci memcpy(pbuf, (char *)&rsp->CreationTime, sizeof(*pbuf) - 4); 32618c2ecf20Sopenharmony_ci } 32628c2ecf20Sopenharmony_ci 32638c2ecf20Sopenharmony_ci atomic_dec(&tcon->num_remote_opens); 32648c2ecf20Sopenharmony_ciclose_exit: 32658c2ecf20Sopenharmony_ci SMB2_close_free(&rqst); 32668c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype, rsp); 32678c2ecf20Sopenharmony_ci 32688c2ecf20Sopenharmony_ci /* retry close in a worker thread if this one is interrupted */ 32698c2ecf20Sopenharmony_ci if (is_interrupt_error(rc)) { 32708c2ecf20Sopenharmony_ci int tmp_rc; 32718c2ecf20Sopenharmony_ci 32728c2ecf20Sopenharmony_ci tmp_rc = smb2_handle_cancelled_close(tcon, persistent_fid, 32738c2ecf20Sopenharmony_ci volatile_fid); 32748c2ecf20Sopenharmony_ci if (tmp_rc) 32758c2ecf20Sopenharmony_ci cifs_dbg(VFS, "handle cancelled close fid 0x%llx returned error %d\n", 32768c2ecf20Sopenharmony_ci persistent_fid, tmp_rc); 32778c2ecf20Sopenharmony_ci } 32788c2ecf20Sopenharmony_ci return rc; 32798c2ecf20Sopenharmony_ci} 32808c2ecf20Sopenharmony_ci 32818c2ecf20Sopenharmony_ciint 32828c2ecf20Sopenharmony_ciSMB2_close(const unsigned int xid, struct cifs_tcon *tcon, 32838c2ecf20Sopenharmony_ci u64 persistent_fid, u64 volatile_fid) 32848c2ecf20Sopenharmony_ci{ 32858c2ecf20Sopenharmony_ci return __SMB2_close(xid, tcon, persistent_fid, volatile_fid, NULL); 32868c2ecf20Sopenharmony_ci} 32878c2ecf20Sopenharmony_ci 32888c2ecf20Sopenharmony_ciint 32898c2ecf20Sopenharmony_cismb2_validate_iov(unsigned int offset, unsigned int buffer_length, 32908c2ecf20Sopenharmony_ci struct kvec *iov, unsigned int min_buf_size) 32918c2ecf20Sopenharmony_ci{ 32928c2ecf20Sopenharmony_ci unsigned int smb_len = iov->iov_len; 32938c2ecf20Sopenharmony_ci char *end_of_smb = smb_len + (char *)iov->iov_base; 32948c2ecf20Sopenharmony_ci char *begin_of_buf = offset + (char *)iov->iov_base; 32958c2ecf20Sopenharmony_ci char *end_of_buf = begin_of_buf + buffer_length; 32968c2ecf20Sopenharmony_ci 32978c2ecf20Sopenharmony_ci 32988c2ecf20Sopenharmony_ci if (buffer_length < min_buf_size) { 32998c2ecf20Sopenharmony_ci cifs_dbg(VFS, "buffer length %d smaller than minimum size %d\n", 33008c2ecf20Sopenharmony_ci buffer_length, min_buf_size); 33018c2ecf20Sopenharmony_ci return -EINVAL; 33028c2ecf20Sopenharmony_ci } 33038c2ecf20Sopenharmony_ci 33048c2ecf20Sopenharmony_ci /* check if beyond RFC1001 maximum length */ 33058c2ecf20Sopenharmony_ci if ((smb_len > 0x7FFFFF) || (buffer_length > 0x7FFFFF)) { 33068c2ecf20Sopenharmony_ci cifs_dbg(VFS, "buffer length %d or smb length %d too large\n", 33078c2ecf20Sopenharmony_ci buffer_length, smb_len); 33088c2ecf20Sopenharmony_ci return -EINVAL; 33098c2ecf20Sopenharmony_ci } 33108c2ecf20Sopenharmony_ci 33118c2ecf20Sopenharmony_ci if ((begin_of_buf > end_of_smb) || (end_of_buf > end_of_smb)) { 33128c2ecf20Sopenharmony_ci cifs_dbg(VFS, "Invalid server response, bad offset to data\n"); 33138c2ecf20Sopenharmony_ci return -EINVAL; 33148c2ecf20Sopenharmony_ci } 33158c2ecf20Sopenharmony_ci 33168c2ecf20Sopenharmony_ci return 0; 33178c2ecf20Sopenharmony_ci} 33188c2ecf20Sopenharmony_ci 33198c2ecf20Sopenharmony_ci/* 33208c2ecf20Sopenharmony_ci * If SMB buffer fields are valid, copy into temporary buffer to hold result. 33218c2ecf20Sopenharmony_ci * Caller must free buffer. 33228c2ecf20Sopenharmony_ci */ 33238c2ecf20Sopenharmony_ciint 33248c2ecf20Sopenharmony_cismb2_validate_and_copy_iov(unsigned int offset, unsigned int buffer_length, 33258c2ecf20Sopenharmony_ci struct kvec *iov, unsigned int minbufsize, 33268c2ecf20Sopenharmony_ci char *data) 33278c2ecf20Sopenharmony_ci{ 33288c2ecf20Sopenharmony_ci char *begin_of_buf = offset + (char *)iov->iov_base; 33298c2ecf20Sopenharmony_ci int rc; 33308c2ecf20Sopenharmony_ci 33318c2ecf20Sopenharmony_ci if (!data) 33328c2ecf20Sopenharmony_ci return -EINVAL; 33338c2ecf20Sopenharmony_ci 33348c2ecf20Sopenharmony_ci rc = smb2_validate_iov(offset, buffer_length, iov, minbufsize); 33358c2ecf20Sopenharmony_ci if (rc) 33368c2ecf20Sopenharmony_ci return rc; 33378c2ecf20Sopenharmony_ci 33388c2ecf20Sopenharmony_ci memcpy(data, begin_of_buf, buffer_length); 33398c2ecf20Sopenharmony_ci 33408c2ecf20Sopenharmony_ci return 0; 33418c2ecf20Sopenharmony_ci} 33428c2ecf20Sopenharmony_ci 33438c2ecf20Sopenharmony_ciint 33448c2ecf20Sopenharmony_ciSMB2_query_info_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, 33458c2ecf20Sopenharmony_ci struct smb_rqst *rqst, 33468c2ecf20Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, 33478c2ecf20Sopenharmony_ci u8 info_class, u8 info_type, u32 additional_info, 33488c2ecf20Sopenharmony_ci size_t output_len, size_t input_len, void *input) 33498c2ecf20Sopenharmony_ci{ 33508c2ecf20Sopenharmony_ci struct smb2_query_info_req *req; 33518c2ecf20Sopenharmony_ci struct kvec *iov = rqst->rq_iov; 33528c2ecf20Sopenharmony_ci unsigned int total_len; 33538c2ecf20Sopenharmony_ci size_t len; 33548c2ecf20Sopenharmony_ci int rc; 33558c2ecf20Sopenharmony_ci 33568c2ecf20Sopenharmony_ci if (unlikely(check_add_overflow(input_len, sizeof(*req), &len) || 33578c2ecf20Sopenharmony_ci len > CIFSMaxBufSize)) 33588c2ecf20Sopenharmony_ci return -EINVAL; 33598c2ecf20Sopenharmony_ci 33608c2ecf20Sopenharmony_ci rc = smb2_plain_req_init(SMB2_QUERY_INFO, tcon, server, 33618c2ecf20Sopenharmony_ci (void **) &req, &total_len); 33628c2ecf20Sopenharmony_ci if (rc) 33638c2ecf20Sopenharmony_ci return rc; 33648c2ecf20Sopenharmony_ci 33658c2ecf20Sopenharmony_ci req->InfoType = info_type; 33668c2ecf20Sopenharmony_ci req->FileInfoClass = info_class; 33678c2ecf20Sopenharmony_ci req->PersistentFileId = persistent_fid; 33688c2ecf20Sopenharmony_ci req->VolatileFileId = volatile_fid; 33698c2ecf20Sopenharmony_ci req->AdditionalInformation = cpu_to_le32(additional_info); 33708c2ecf20Sopenharmony_ci 33718c2ecf20Sopenharmony_ci req->OutputBufferLength = cpu_to_le32(output_len); 33728c2ecf20Sopenharmony_ci if (input_len) { 33738c2ecf20Sopenharmony_ci req->InputBufferLength = cpu_to_le32(input_len); 33748c2ecf20Sopenharmony_ci /* total_len for smb query request never close to le16 max */ 33758c2ecf20Sopenharmony_ci req->InputBufferOffset = cpu_to_le16(total_len - 1); 33768c2ecf20Sopenharmony_ci memcpy(req->Buffer, input, input_len); 33778c2ecf20Sopenharmony_ci } 33788c2ecf20Sopenharmony_ci 33798c2ecf20Sopenharmony_ci iov[0].iov_base = (char *)req; 33808c2ecf20Sopenharmony_ci /* 1 for Buffer */ 33818c2ecf20Sopenharmony_ci iov[0].iov_len = len; 33828c2ecf20Sopenharmony_ci return 0; 33838c2ecf20Sopenharmony_ci} 33848c2ecf20Sopenharmony_ci 33858c2ecf20Sopenharmony_civoid 33868c2ecf20Sopenharmony_ciSMB2_query_info_free(struct smb_rqst *rqst) 33878c2ecf20Sopenharmony_ci{ 33888c2ecf20Sopenharmony_ci if (rqst && rqst->rq_iov) 33898c2ecf20Sopenharmony_ci cifs_buf_release(rqst->rq_iov[0].iov_base); /* request */ 33908c2ecf20Sopenharmony_ci} 33918c2ecf20Sopenharmony_ci 33928c2ecf20Sopenharmony_cistatic int 33938c2ecf20Sopenharmony_ciquery_info(const unsigned int xid, struct cifs_tcon *tcon, 33948c2ecf20Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, u8 info_class, u8 info_type, 33958c2ecf20Sopenharmony_ci u32 additional_info, size_t output_len, size_t min_len, void **data, 33968c2ecf20Sopenharmony_ci u32 *dlen) 33978c2ecf20Sopenharmony_ci{ 33988c2ecf20Sopenharmony_ci struct smb_rqst rqst; 33998c2ecf20Sopenharmony_ci struct smb2_query_info_rsp *rsp = NULL; 34008c2ecf20Sopenharmony_ci struct kvec iov[1]; 34018c2ecf20Sopenharmony_ci struct kvec rsp_iov; 34028c2ecf20Sopenharmony_ci int rc = 0; 34038c2ecf20Sopenharmony_ci int resp_buftype = CIFS_NO_BUFFER; 34048c2ecf20Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 34058c2ecf20Sopenharmony_ci struct TCP_Server_Info *server; 34068c2ecf20Sopenharmony_ci int flags = 0; 34078c2ecf20Sopenharmony_ci bool allocated = false; 34088c2ecf20Sopenharmony_ci 34098c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Query Info\n"); 34108c2ecf20Sopenharmony_ci 34118c2ecf20Sopenharmony_ci if (!ses) 34128c2ecf20Sopenharmony_ci return -EIO; 34138c2ecf20Sopenharmony_ci server = cifs_pick_channel(ses); 34148c2ecf20Sopenharmony_ci if (!server) 34158c2ecf20Sopenharmony_ci return -EIO; 34168c2ecf20Sopenharmony_ci 34178c2ecf20Sopenharmony_ci if (smb3_encryption_required(tcon)) 34188c2ecf20Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 34198c2ecf20Sopenharmony_ci 34208c2ecf20Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 34218c2ecf20Sopenharmony_ci memset(&iov, 0, sizeof(iov)); 34228c2ecf20Sopenharmony_ci rqst.rq_iov = iov; 34238c2ecf20Sopenharmony_ci rqst.rq_nvec = 1; 34248c2ecf20Sopenharmony_ci 34258c2ecf20Sopenharmony_ci rc = SMB2_query_info_init(tcon, server, 34268c2ecf20Sopenharmony_ci &rqst, persistent_fid, volatile_fid, 34278c2ecf20Sopenharmony_ci info_class, info_type, additional_info, 34288c2ecf20Sopenharmony_ci output_len, 0, NULL); 34298c2ecf20Sopenharmony_ci if (rc) 34308c2ecf20Sopenharmony_ci goto qinf_exit; 34318c2ecf20Sopenharmony_ci 34328c2ecf20Sopenharmony_ci trace_smb3_query_info_enter(xid, persistent_fid, tcon->tid, 34338c2ecf20Sopenharmony_ci ses->Suid, info_class, (__u32)info_type); 34348c2ecf20Sopenharmony_ci 34358c2ecf20Sopenharmony_ci rc = cifs_send_recv(xid, ses, server, 34368c2ecf20Sopenharmony_ci &rqst, &resp_buftype, flags, &rsp_iov); 34378c2ecf20Sopenharmony_ci rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; 34388c2ecf20Sopenharmony_ci 34398c2ecf20Sopenharmony_ci if (rc) { 34408c2ecf20Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); 34418c2ecf20Sopenharmony_ci trace_smb3_query_info_err(xid, persistent_fid, tcon->tid, 34428c2ecf20Sopenharmony_ci ses->Suid, info_class, (__u32)info_type, rc); 34438c2ecf20Sopenharmony_ci goto qinf_exit; 34448c2ecf20Sopenharmony_ci } 34458c2ecf20Sopenharmony_ci 34468c2ecf20Sopenharmony_ci trace_smb3_query_info_done(xid, persistent_fid, tcon->tid, 34478c2ecf20Sopenharmony_ci ses->Suid, info_class, (__u32)info_type); 34488c2ecf20Sopenharmony_ci 34498c2ecf20Sopenharmony_ci if (dlen) { 34508c2ecf20Sopenharmony_ci *dlen = le32_to_cpu(rsp->OutputBufferLength); 34518c2ecf20Sopenharmony_ci if (!*data) { 34528c2ecf20Sopenharmony_ci *data = kmalloc(*dlen, GFP_KERNEL); 34538c2ecf20Sopenharmony_ci if (!*data) { 34548c2ecf20Sopenharmony_ci cifs_tcon_dbg(VFS, 34558c2ecf20Sopenharmony_ci "Error %d allocating memory for acl\n", 34568c2ecf20Sopenharmony_ci rc); 34578c2ecf20Sopenharmony_ci *dlen = 0; 34588c2ecf20Sopenharmony_ci rc = -ENOMEM; 34598c2ecf20Sopenharmony_ci goto qinf_exit; 34608c2ecf20Sopenharmony_ci } 34618c2ecf20Sopenharmony_ci allocated = true; 34628c2ecf20Sopenharmony_ci } 34638c2ecf20Sopenharmony_ci } 34648c2ecf20Sopenharmony_ci 34658c2ecf20Sopenharmony_ci rc = smb2_validate_and_copy_iov(le16_to_cpu(rsp->OutputBufferOffset), 34668c2ecf20Sopenharmony_ci le32_to_cpu(rsp->OutputBufferLength), 34678c2ecf20Sopenharmony_ci &rsp_iov, min_len, *data); 34688c2ecf20Sopenharmony_ci if (rc && allocated) { 34698c2ecf20Sopenharmony_ci kfree(*data); 34708c2ecf20Sopenharmony_ci *data = NULL; 34718c2ecf20Sopenharmony_ci *dlen = 0; 34728c2ecf20Sopenharmony_ci } 34738c2ecf20Sopenharmony_ci 34748c2ecf20Sopenharmony_ciqinf_exit: 34758c2ecf20Sopenharmony_ci SMB2_query_info_free(&rqst); 34768c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype, rsp); 34778c2ecf20Sopenharmony_ci return rc; 34788c2ecf20Sopenharmony_ci} 34798c2ecf20Sopenharmony_ci 34808c2ecf20Sopenharmony_ciint SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, 34818c2ecf20Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, struct smb2_file_all_info *data) 34828c2ecf20Sopenharmony_ci{ 34838c2ecf20Sopenharmony_ci return query_info(xid, tcon, persistent_fid, volatile_fid, 34848c2ecf20Sopenharmony_ci FILE_ALL_INFORMATION, SMB2_O_INFO_FILE, 0, 34858c2ecf20Sopenharmony_ci sizeof(struct smb2_file_all_info) + PATH_MAX * 2, 34868c2ecf20Sopenharmony_ci sizeof(struct smb2_file_all_info), (void **)&data, 34878c2ecf20Sopenharmony_ci NULL); 34888c2ecf20Sopenharmony_ci} 34898c2ecf20Sopenharmony_ci 34908c2ecf20Sopenharmony_ciint 34918c2ecf20Sopenharmony_ciSMB311_posix_query_info(const unsigned int xid, struct cifs_tcon *tcon, 34928c2ecf20Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, struct smb311_posix_qinfo *data, u32 *plen) 34938c2ecf20Sopenharmony_ci{ 34948c2ecf20Sopenharmony_ci size_t output_len = sizeof(struct smb311_posix_qinfo *) + 34958c2ecf20Sopenharmony_ci (sizeof(struct cifs_sid) * 2) + (PATH_MAX * 2); 34968c2ecf20Sopenharmony_ci *plen = 0; 34978c2ecf20Sopenharmony_ci 34988c2ecf20Sopenharmony_ci return query_info(xid, tcon, persistent_fid, volatile_fid, 34998c2ecf20Sopenharmony_ci SMB_FIND_FILE_POSIX_INFO, SMB2_O_INFO_FILE, 0, 35008c2ecf20Sopenharmony_ci output_len, sizeof(struct smb311_posix_qinfo), (void **)&data, plen); 35018c2ecf20Sopenharmony_ci} 35028c2ecf20Sopenharmony_ci 35038c2ecf20Sopenharmony_ciint 35048c2ecf20Sopenharmony_ciSMB2_query_acl(const unsigned int xid, struct cifs_tcon *tcon, 35058c2ecf20Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, 35068c2ecf20Sopenharmony_ci void **data, u32 *plen) 35078c2ecf20Sopenharmony_ci{ 35088c2ecf20Sopenharmony_ci __u32 additional_info = OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO; 35098c2ecf20Sopenharmony_ci *plen = 0; 35108c2ecf20Sopenharmony_ci 35118c2ecf20Sopenharmony_ci return query_info(xid, tcon, persistent_fid, volatile_fid, 35128c2ecf20Sopenharmony_ci 0, SMB2_O_INFO_SECURITY, additional_info, 35138c2ecf20Sopenharmony_ci SMB2_MAX_BUFFER_SIZE, MIN_SEC_DESC_LEN, data, plen); 35148c2ecf20Sopenharmony_ci} 35158c2ecf20Sopenharmony_ci 35168c2ecf20Sopenharmony_ciint 35178c2ecf20Sopenharmony_ciSMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon, 35188c2ecf20Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, __le64 *uniqueid) 35198c2ecf20Sopenharmony_ci{ 35208c2ecf20Sopenharmony_ci return query_info(xid, tcon, persistent_fid, volatile_fid, 35218c2ecf20Sopenharmony_ci FILE_INTERNAL_INFORMATION, SMB2_O_INFO_FILE, 0, 35228c2ecf20Sopenharmony_ci sizeof(struct smb2_file_internal_info), 35238c2ecf20Sopenharmony_ci sizeof(struct smb2_file_internal_info), 35248c2ecf20Sopenharmony_ci (void **)&uniqueid, NULL); 35258c2ecf20Sopenharmony_ci} 35268c2ecf20Sopenharmony_ci 35278c2ecf20Sopenharmony_ci/* 35288c2ecf20Sopenharmony_ci * CHANGE_NOTIFY Request is sent to get notifications on changes to a directory 35298c2ecf20Sopenharmony_ci * See MS-SMB2 2.2.35 and 2.2.36 35308c2ecf20Sopenharmony_ci */ 35318c2ecf20Sopenharmony_ci 35328c2ecf20Sopenharmony_cistatic int 35338c2ecf20Sopenharmony_ciSMB2_notify_init(const unsigned int xid, struct smb_rqst *rqst, 35348c2ecf20Sopenharmony_ci struct cifs_tcon *tcon, struct TCP_Server_Info *server, 35358c2ecf20Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, 35368c2ecf20Sopenharmony_ci u32 completion_filter, bool watch_tree) 35378c2ecf20Sopenharmony_ci{ 35388c2ecf20Sopenharmony_ci struct smb2_change_notify_req *req; 35398c2ecf20Sopenharmony_ci struct kvec *iov = rqst->rq_iov; 35408c2ecf20Sopenharmony_ci unsigned int total_len; 35418c2ecf20Sopenharmony_ci int rc; 35428c2ecf20Sopenharmony_ci 35438c2ecf20Sopenharmony_ci rc = smb2_plain_req_init(SMB2_CHANGE_NOTIFY, tcon, server, 35448c2ecf20Sopenharmony_ci (void **) &req, &total_len); 35458c2ecf20Sopenharmony_ci if (rc) 35468c2ecf20Sopenharmony_ci return rc; 35478c2ecf20Sopenharmony_ci 35488c2ecf20Sopenharmony_ci req->PersistentFileId = persistent_fid; 35498c2ecf20Sopenharmony_ci req->VolatileFileId = volatile_fid; 35508c2ecf20Sopenharmony_ci /* See note 354 of MS-SMB2, 64K max */ 35518c2ecf20Sopenharmony_ci req->OutputBufferLength = 35528c2ecf20Sopenharmony_ci cpu_to_le32(SMB2_MAX_BUFFER_SIZE - MAX_SMB2_HDR_SIZE); 35538c2ecf20Sopenharmony_ci req->CompletionFilter = cpu_to_le32(completion_filter); 35548c2ecf20Sopenharmony_ci if (watch_tree) 35558c2ecf20Sopenharmony_ci req->Flags = cpu_to_le16(SMB2_WATCH_TREE); 35568c2ecf20Sopenharmony_ci else 35578c2ecf20Sopenharmony_ci req->Flags = 0; 35588c2ecf20Sopenharmony_ci 35598c2ecf20Sopenharmony_ci iov[0].iov_base = (char *)req; 35608c2ecf20Sopenharmony_ci iov[0].iov_len = total_len; 35618c2ecf20Sopenharmony_ci 35628c2ecf20Sopenharmony_ci return 0; 35638c2ecf20Sopenharmony_ci} 35648c2ecf20Sopenharmony_ci 35658c2ecf20Sopenharmony_ciint 35668c2ecf20Sopenharmony_ciSMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon, 35678c2ecf20Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, bool watch_tree, 35688c2ecf20Sopenharmony_ci u32 completion_filter) 35698c2ecf20Sopenharmony_ci{ 35708c2ecf20Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 35718c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(ses); 35728c2ecf20Sopenharmony_ci struct smb_rqst rqst; 35738c2ecf20Sopenharmony_ci struct kvec iov[1]; 35748c2ecf20Sopenharmony_ci struct kvec rsp_iov = {NULL, 0}; 35758c2ecf20Sopenharmony_ci int resp_buftype = CIFS_NO_BUFFER; 35768c2ecf20Sopenharmony_ci int flags = 0; 35778c2ecf20Sopenharmony_ci int rc = 0; 35788c2ecf20Sopenharmony_ci 35798c2ecf20Sopenharmony_ci cifs_dbg(FYI, "change notify\n"); 35808c2ecf20Sopenharmony_ci if (!ses || !server) 35818c2ecf20Sopenharmony_ci return -EIO; 35828c2ecf20Sopenharmony_ci 35838c2ecf20Sopenharmony_ci if (smb3_encryption_required(tcon)) 35848c2ecf20Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 35858c2ecf20Sopenharmony_ci 35868c2ecf20Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 35878c2ecf20Sopenharmony_ci memset(&iov, 0, sizeof(iov)); 35888c2ecf20Sopenharmony_ci rqst.rq_iov = iov; 35898c2ecf20Sopenharmony_ci rqst.rq_nvec = 1; 35908c2ecf20Sopenharmony_ci 35918c2ecf20Sopenharmony_ci rc = SMB2_notify_init(xid, &rqst, tcon, server, 35928c2ecf20Sopenharmony_ci persistent_fid, volatile_fid, 35938c2ecf20Sopenharmony_ci completion_filter, watch_tree); 35948c2ecf20Sopenharmony_ci if (rc) 35958c2ecf20Sopenharmony_ci goto cnotify_exit; 35968c2ecf20Sopenharmony_ci 35978c2ecf20Sopenharmony_ci trace_smb3_notify_enter(xid, persistent_fid, tcon->tid, ses->Suid, 35988c2ecf20Sopenharmony_ci (u8)watch_tree, completion_filter); 35998c2ecf20Sopenharmony_ci rc = cifs_send_recv(xid, ses, server, 36008c2ecf20Sopenharmony_ci &rqst, &resp_buftype, flags, &rsp_iov); 36018c2ecf20Sopenharmony_ci 36028c2ecf20Sopenharmony_ci if (rc != 0) { 36038c2ecf20Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_CHANGE_NOTIFY_HE); 36048c2ecf20Sopenharmony_ci trace_smb3_notify_err(xid, persistent_fid, tcon->tid, ses->Suid, 36058c2ecf20Sopenharmony_ci (u8)watch_tree, completion_filter, rc); 36068c2ecf20Sopenharmony_ci } else 36078c2ecf20Sopenharmony_ci trace_smb3_notify_done(xid, persistent_fid, tcon->tid, 36088c2ecf20Sopenharmony_ci ses->Suid, (u8)watch_tree, completion_filter); 36098c2ecf20Sopenharmony_ci 36108c2ecf20Sopenharmony_ci cnotify_exit: 36118c2ecf20Sopenharmony_ci if (rqst.rq_iov) 36128c2ecf20Sopenharmony_ci cifs_small_buf_release(rqst.rq_iov[0].iov_base); /* request */ 36138c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype, rsp_iov.iov_base); 36148c2ecf20Sopenharmony_ci return rc; 36158c2ecf20Sopenharmony_ci} 36168c2ecf20Sopenharmony_ci 36178c2ecf20Sopenharmony_ci 36188c2ecf20Sopenharmony_ci 36198c2ecf20Sopenharmony_ci/* 36208c2ecf20Sopenharmony_ci * This is a no-op for now. We're not really interested in the reply, but 36218c2ecf20Sopenharmony_ci * rather in the fact that the server sent one and that server->lstrp 36228c2ecf20Sopenharmony_ci * gets updated. 36238c2ecf20Sopenharmony_ci * 36248c2ecf20Sopenharmony_ci * FIXME: maybe we should consider checking that the reply matches request? 36258c2ecf20Sopenharmony_ci */ 36268c2ecf20Sopenharmony_cistatic void 36278c2ecf20Sopenharmony_cismb2_echo_callback(struct mid_q_entry *mid) 36288c2ecf20Sopenharmony_ci{ 36298c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = mid->callback_data; 36308c2ecf20Sopenharmony_ci struct smb2_echo_rsp *rsp = (struct smb2_echo_rsp *)mid->resp_buf; 36318c2ecf20Sopenharmony_ci struct cifs_credits credits = { .value = 0, .instance = 0 }; 36328c2ecf20Sopenharmony_ci 36338c2ecf20Sopenharmony_ci if (mid->mid_state == MID_RESPONSE_RECEIVED 36348c2ecf20Sopenharmony_ci || mid->mid_state == MID_RESPONSE_MALFORMED) { 36358c2ecf20Sopenharmony_ci credits.value = le16_to_cpu(rsp->sync_hdr.CreditRequest); 36368c2ecf20Sopenharmony_ci credits.instance = server->reconnect_instance; 36378c2ecf20Sopenharmony_ci } 36388c2ecf20Sopenharmony_ci 36398c2ecf20Sopenharmony_ci DeleteMidQEntry(mid); 36408c2ecf20Sopenharmony_ci add_credits(server, &credits, CIFS_ECHO_OP); 36418c2ecf20Sopenharmony_ci} 36428c2ecf20Sopenharmony_ci 36438c2ecf20Sopenharmony_civoid smb2_reconnect_server(struct work_struct *work) 36448c2ecf20Sopenharmony_ci{ 36458c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = container_of(work, 36468c2ecf20Sopenharmony_ci struct TCP_Server_Info, reconnect.work); 36478c2ecf20Sopenharmony_ci struct cifs_ses *ses; 36488c2ecf20Sopenharmony_ci struct cifs_tcon *tcon, *tcon2; 36498c2ecf20Sopenharmony_ci struct list_head tmp_list; 36508c2ecf20Sopenharmony_ci int tcon_exist = false; 36518c2ecf20Sopenharmony_ci int rc; 36528c2ecf20Sopenharmony_ci int resched = false; 36538c2ecf20Sopenharmony_ci 36548c2ecf20Sopenharmony_ci 36558c2ecf20Sopenharmony_ci /* Prevent simultaneous reconnects that can corrupt tcon->rlist list */ 36568c2ecf20Sopenharmony_ci mutex_lock(&server->reconnect_mutex); 36578c2ecf20Sopenharmony_ci 36588c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&tmp_list); 36598c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Need negotiate, reconnecting tcons\n"); 36608c2ecf20Sopenharmony_ci 36618c2ecf20Sopenharmony_ci spin_lock(&cifs_tcp_ses_lock); 36628c2ecf20Sopenharmony_ci list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { 36638c2ecf20Sopenharmony_ci list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { 36648c2ecf20Sopenharmony_ci if (tcon->need_reconnect || tcon->need_reopen_files) { 36658c2ecf20Sopenharmony_ci tcon->tc_count++; 36668c2ecf20Sopenharmony_ci list_add_tail(&tcon->rlist, &tmp_list); 36678c2ecf20Sopenharmony_ci tcon_exist = true; 36688c2ecf20Sopenharmony_ci } 36698c2ecf20Sopenharmony_ci } 36708c2ecf20Sopenharmony_ci /* 36718c2ecf20Sopenharmony_ci * IPC has the same lifetime as its session and uses its 36728c2ecf20Sopenharmony_ci * refcount. 36738c2ecf20Sopenharmony_ci */ 36748c2ecf20Sopenharmony_ci if (ses->tcon_ipc && ses->tcon_ipc->need_reconnect) { 36758c2ecf20Sopenharmony_ci list_add_tail(&ses->tcon_ipc->rlist, &tmp_list); 36768c2ecf20Sopenharmony_ci tcon_exist = true; 36778c2ecf20Sopenharmony_ci ses->ses_count++; 36788c2ecf20Sopenharmony_ci } 36798c2ecf20Sopenharmony_ci } 36808c2ecf20Sopenharmony_ci /* 36818c2ecf20Sopenharmony_ci * Get the reference to server struct to be sure that the last call of 36828c2ecf20Sopenharmony_ci * cifs_put_tcon() in the loop below won't release the server pointer. 36838c2ecf20Sopenharmony_ci */ 36848c2ecf20Sopenharmony_ci if (tcon_exist) 36858c2ecf20Sopenharmony_ci server->srv_count++; 36868c2ecf20Sopenharmony_ci 36878c2ecf20Sopenharmony_ci spin_unlock(&cifs_tcp_ses_lock); 36888c2ecf20Sopenharmony_ci 36898c2ecf20Sopenharmony_ci list_for_each_entry_safe(tcon, tcon2, &tmp_list, rlist) { 36908c2ecf20Sopenharmony_ci rc = smb2_reconnect(SMB2_INTERNAL_CMD, tcon, server); 36918c2ecf20Sopenharmony_ci if (!rc) 36928c2ecf20Sopenharmony_ci cifs_reopen_persistent_handles(tcon); 36938c2ecf20Sopenharmony_ci else 36948c2ecf20Sopenharmony_ci resched = true; 36958c2ecf20Sopenharmony_ci list_del_init(&tcon->rlist); 36968c2ecf20Sopenharmony_ci if (tcon->ipc) 36978c2ecf20Sopenharmony_ci cifs_put_smb_ses(tcon->ses); 36988c2ecf20Sopenharmony_ci else 36998c2ecf20Sopenharmony_ci cifs_put_tcon(tcon); 37008c2ecf20Sopenharmony_ci } 37018c2ecf20Sopenharmony_ci 37028c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Reconnecting tcons finished\n"); 37038c2ecf20Sopenharmony_ci if (resched) 37048c2ecf20Sopenharmony_ci queue_delayed_work(cifsiod_wq, &server->reconnect, 2 * HZ); 37058c2ecf20Sopenharmony_ci mutex_unlock(&server->reconnect_mutex); 37068c2ecf20Sopenharmony_ci 37078c2ecf20Sopenharmony_ci /* now we can safely release srv struct */ 37088c2ecf20Sopenharmony_ci if (tcon_exist) 37098c2ecf20Sopenharmony_ci cifs_put_tcp_session(server, 1); 37108c2ecf20Sopenharmony_ci} 37118c2ecf20Sopenharmony_ci 37128c2ecf20Sopenharmony_ciint 37138c2ecf20Sopenharmony_ciSMB2_echo(struct TCP_Server_Info *server) 37148c2ecf20Sopenharmony_ci{ 37158c2ecf20Sopenharmony_ci struct smb2_echo_req *req; 37168c2ecf20Sopenharmony_ci int rc = 0; 37178c2ecf20Sopenharmony_ci struct kvec iov[1]; 37188c2ecf20Sopenharmony_ci struct smb_rqst rqst = { .rq_iov = iov, 37198c2ecf20Sopenharmony_ci .rq_nvec = 1 }; 37208c2ecf20Sopenharmony_ci unsigned int total_len; 37218c2ecf20Sopenharmony_ci 37228c2ecf20Sopenharmony_ci cifs_dbg(FYI, "In echo request\n"); 37238c2ecf20Sopenharmony_ci 37248c2ecf20Sopenharmony_ci if (server->tcpStatus == CifsNeedNegotiate) { 37258c2ecf20Sopenharmony_ci /* No need to send echo on newly established connections */ 37268c2ecf20Sopenharmony_ci mod_delayed_work(cifsiod_wq, &server->reconnect, 0); 37278c2ecf20Sopenharmony_ci return rc; 37288c2ecf20Sopenharmony_ci } 37298c2ecf20Sopenharmony_ci 37308c2ecf20Sopenharmony_ci rc = smb2_plain_req_init(SMB2_ECHO, NULL, server, 37318c2ecf20Sopenharmony_ci (void **)&req, &total_len); 37328c2ecf20Sopenharmony_ci if (rc) 37338c2ecf20Sopenharmony_ci return rc; 37348c2ecf20Sopenharmony_ci 37358c2ecf20Sopenharmony_ci req->sync_hdr.CreditRequest = cpu_to_le16(1); 37368c2ecf20Sopenharmony_ci 37378c2ecf20Sopenharmony_ci iov[0].iov_len = total_len; 37388c2ecf20Sopenharmony_ci iov[0].iov_base = (char *)req; 37398c2ecf20Sopenharmony_ci 37408c2ecf20Sopenharmony_ci rc = cifs_call_async(server, &rqst, NULL, smb2_echo_callback, NULL, 37418c2ecf20Sopenharmony_ci server, CIFS_ECHO_OP, NULL); 37428c2ecf20Sopenharmony_ci if (rc) 37438c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Echo request failed: %d\n", rc); 37448c2ecf20Sopenharmony_ci 37458c2ecf20Sopenharmony_ci cifs_small_buf_release(req); 37468c2ecf20Sopenharmony_ci return rc; 37478c2ecf20Sopenharmony_ci} 37488c2ecf20Sopenharmony_ci 37498c2ecf20Sopenharmony_civoid 37508c2ecf20Sopenharmony_ciSMB2_flush_free(struct smb_rqst *rqst) 37518c2ecf20Sopenharmony_ci{ 37528c2ecf20Sopenharmony_ci if (rqst && rqst->rq_iov) 37538c2ecf20Sopenharmony_ci cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ 37548c2ecf20Sopenharmony_ci} 37558c2ecf20Sopenharmony_ci 37568c2ecf20Sopenharmony_ciint 37578c2ecf20Sopenharmony_ciSMB2_flush_init(const unsigned int xid, struct smb_rqst *rqst, 37588c2ecf20Sopenharmony_ci struct cifs_tcon *tcon, struct TCP_Server_Info *server, 37598c2ecf20Sopenharmony_ci u64 persistent_fid, u64 volatile_fid) 37608c2ecf20Sopenharmony_ci{ 37618c2ecf20Sopenharmony_ci struct smb2_flush_req *req; 37628c2ecf20Sopenharmony_ci struct kvec *iov = rqst->rq_iov; 37638c2ecf20Sopenharmony_ci unsigned int total_len; 37648c2ecf20Sopenharmony_ci int rc; 37658c2ecf20Sopenharmony_ci 37668c2ecf20Sopenharmony_ci rc = smb2_plain_req_init(SMB2_FLUSH, tcon, server, 37678c2ecf20Sopenharmony_ci (void **) &req, &total_len); 37688c2ecf20Sopenharmony_ci if (rc) 37698c2ecf20Sopenharmony_ci return rc; 37708c2ecf20Sopenharmony_ci 37718c2ecf20Sopenharmony_ci req->PersistentFileId = persistent_fid; 37728c2ecf20Sopenharmony_ci req->VolatileFileId = volatile_fid; 37738c2ecf20Sopenharmony_ci 37748c2ecf20Sopenharmony_ci iov[0].iov_base = (char *)req; 37758c2ecf20Sopenharmony_ci iov[0].iov_len = total_len; 37768c2ecf20Sopenharmony_ci 37778c2ecf20Sopenharmony_ci return 0; 37788c2ecf20Sopenharmony_ci} 37798c2ecf20Sopenharmony_ci 37808c2ecf20Sopenharmony_ciint 37818c2ecf20Sopenharmony_ciSMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, 37828c2ecf20Sopenharmony_ci u64 volatile_fid) 37838c2ecf20Sopenharmony_ci{ 37848c2ecf20Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 37858c2ecf20Sopenharmony_ci struct smb_rqst rqst; 37868c2ecf20Sopenharmony_ci struct kvec iov[1]; 37878c2ecf20Sopenharmony_ci struct kvec rsp_iov = {NULL, 0}; 37888c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(ses); 37898c2ecf20Sopenharmony_ci int resp_buftype = CIFS_NO_BUFFER; 37908c2ecf20Sopenharmony_ci int flags = 0; 37918c2ecf20Sopenharmony_ci int rc = 0; 37928c2ecf20Sopenharmony_ci 37938c2ecf20Sopenharmony_ci cifs_dbg(FYI, "flush\n"); 37948c2ecf20Sopenharmony_ci if (!ses || !(ses->server)) 37958c2ecf20Sopenharmony_ci return -EIO; 37968c2ecf20Sopenharmony_ci 37978c2ecf20Sopenharmony_ci if (smb3_encryption_required(tcon)) 37988c2ecf20Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 37998c2ecf20Sopenharmony_ci 38008c2ecf20Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 38018c2ecf20Sopenharmony_ci memset(&iov, 0, sizeof(iov)); 38028c2ecf20Sopenharmony_ci rqst.rq_iov = iov; 38038c2ecf20Sopenharmony_ci rqst.rq_nvec = 1; 38048c2ecf20Sopenharmony_ci 38058c2ecf20Sopenharmony_ci rc = SMB2_flush_init(xid, &rqst, tcon, server, 38068c2ecf20Sopenharmony_ci persistent_fid, volatile_fid); 38078c2ecf20Sopenharmony_ci if (rc) 38088c2ecf20Sopenharmony_ci goto flush_exit; 38098c2ecf20Sopenharmony_ci 38108c2ecf20Sopenharmony_ci trace_smb3_flush_enter(xid, persistent_fid, tcon->tid, ses->Suid); 38118c2ecf20Sopenharmony_ci rc = cifs_send_recv(xid, ses, server, 38128c2ecf20Sopenharmony_ci &rqst, &resp_buftype, flags, &rsp_iov); 38138c2ecf20Sopenharmony_ci 38148c2ecf20Sopenharmony_ci if (rc != 0) { 38158c2ecf20Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_FLUSH_HE); 38168c2ecf20Sopenharmony_ci trace_smb3_flush_err(xid, persistent_fid, tcon->tid, ses->Suid, 38178c2ecf20Sopenharmony_ci rc); 38188c2ecf20Sopenharmony_ci } else 38198c2ecf20Sopenharmony_ci trace_smb3_flush_done(xid, persistent_fid, tcon->tid, 38208c2ecf20Sopenharmony_ci ses->Suid); 38218c2ecf20Sopenharmony_ci 38228c2ecf20Sopenharmony_ci flush_exit: 38238c2ecf20Sopenharmony_ci SMB2_flush_free(&rqst); 38248c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype, rsp_iov.iov_base); 38258c2ecf20Sopenharmony_ci return rc; 38268c2ecf20Sopenharmony_ci} 38278c2ecf20Sopenharmony_ci 38288c2ecf20Sopenharmony_ci/* 38298c2ecf20Sopenharmony_ci * To form a chain of read requests, any read requests after the first should 38308c2ecf20Sopenharmony_ci * have the end_of_chain boolean set to true. 38318c2ecf20Sopenharmony_ci */ 38328c2ecf20Sopenharmony_cistatic int 38338c2ecf20Sopenharmony_cismb2_new_read_req(void **buf, unsigned int *total_len, 38348c2ecf20Sopenharmony_ci struct cifs_io_parms *io_parms, struct cifs_readdata *rdata, 38358c2ecf20Sopenharmony_ci unsigned int remaining_bytes, int request_type) 38368c2ecf20Sopenharmony_ci{ 38378c2ecf20Sopenharmony_ci int rc = -EACCES; 38388c2ecf20Sopenharmony_ci struct smb2_read_plain_req *req = NULL; 38398c2ecf20Sopenharmony_ci struct smb2_sync_hdr *shdr; 38408c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = io_parms->server; 38418c2ecf20Sopenharmony_ci 38428c2ecf20Sopenharmony_ci rc = smb2_plain_req_init(SMB2_READ, io_parms->tcon, server, 38438c2ecf20Sopenharmony_ci (void **) &req, total_len); 38448c2ecf20Sopenharmony_ci if (rc) 38458c2ecf20Sopenharmony_ci return rc; 38468c2ecf20Sopenharmony_ci 38478c2ecf20Sopenharmony_ci if (server == NULL) 38488c2ecf20Sopenharmony_ci return -ECONNABORTED; 38498c2ecf20Sopenharmony_ci 38508c2ecf20Sopenharmony_ci shdr = &req->sync_hdr; 38518c2ecf20Sopenharmony_ci shdr->ProcessId = cpu_to_le32(io_parms->pid); 38528c2ecf20Sopenharmony_ci 38538c2ecf20Sopenharmony_ci req->PersistentFileId = io_parms->persistent_fid; 38548c2ecf20Sopenharmony_ci req->VolatileFileId = io_parms->volatile_fid; 38558c2ecf20Sopenharmony_ci req->ReadChannelInfoOffset = 0; /* reserved */ 38568c2ecf20Sopenharmony_ci req->ReadChannelInfoLength = 0; /* reserved */ 38578c2ecf20Sopenharmony_ci req->Channel = 0; /* reserved */ 38588c2ecf20Sopenharmony_ci req->MinimumCount = 0; 38598c2ecf20Sopenharmony_ci req->Length = cpu_to_le32(io_parms->length); 38608c2ecf20Sopenharmony_ci req->Offset = cpu_to_le64(io_parms->offset); 38618c2ecf20Sopenharmony_ci 38628c2ecf20Sopenharmony_ci trace_smb3_read_enter(0 /* xid */, 38638c2ecf20Sopenharmony_ci io_parms->persistent_fid, 38648c2ecf20Sopenharmony_ci io_parms->tcon->tid, io_parms->tcon->ses->Suid, 38658c2ecf20Sopenharmony_ci io_parms->offset, io_parms->length); 38668c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_SMB_DIRECT 38678c2ecf20Sopenharmony_ci /* 38688c2ecf20Sopenharmony_ci * If we want to do a RDMA write, fill in and append 38698c2ecf20Sopenharmony_ci * smbd_buffer_descriptor_v1 to the end of read request 38708c2ecf20Sopenharmony_ci */ 38718c2ecf20Sopenharmony_ci if (server->rdma && rdata && !server->sign && 38728c2ecf20Sopenharmony_ci rdata->bytes >= server->smbd_conn->rdma_readwrite_threshold) { 38738c2ecf20Sopenharmony_ci 38748c2ecf20Sopenharmony_ci struct smbd_buffer_descriptor_v1 *v1; 38758c2ecf20Sopenharmony_ci bool need_invalidate = server->dialect == SMB30_PROT_ID; 38768c2ecf20Sopenharmony_ci 38778c2ecf20Sopenharmony_ci rdata->mr = smbd_register_mr( 38788c2ecf20Sopenharmony_ci server->smbd_conn, rdata->pages, 38798c2ecf20Sopenharmony_ci rdata->nr_pages, rdata->page_offset, 38808c2ecf20Sopenharmony_ci rdata->tailsz, true, need_invalidate); 38818c2ecf20Sopenharmony_ci if (!rdata->mr) 38828c2ecf20Sopenharmony_ci return -EAGAIN; 38838c2ecf20Sopenharmony_ci 38848c2ecf20Sopenharmony_ci req->Channel = SMB2_CHANNEL_RDMA_V1_INVALIDATE; 38858c2ecf20Sopenharmony_ci if (need_invalidate) 38868c2ecf20Sopenharmony_ci req->Channel = SMB2_CHANNEL_RDMA_V1; 38878c2ecf20Sopenharmony_ci req->ReadChannelInfoOffset = 38888c2ecf20Sopenharmony_ci cpu_to_le16(offsetof(struct smb2_read_plain_req, Buffer)); 38898c2ecf20Sopenharmony_ci req->ReadChannelInfoLength = 38908c2ecf20Sopenharmony_ci cpu_to_le16(sizeof(struct smbd_buffer_descriptor_v1)); 38918c2ecf20Sopenharmony_ci v1 = (struct smbd_buffer_descriptor_v1 *) &req->Buffer[0]; 38928c2ecf20Sopenharmony_ci v1->offset = cpu_to_le64(rdata->mr->mr->iova); 38938c2ecf20Sopenharmony_ci v1->token = cpu_to_le32(rdata->mr->mr->rkey); 38948c2ecf20Sopenharmony_ci v1->length = cpu_to_le32(rdata->mr->mr->length); 38958c2ecf20Sopenharmony_ci 38968c2ecf20Sopenharmony_ci *total_len += sizeof(*v1) - 1; 38978c2ecf20Sopenharmony_ci } 38988c2ecf20Sopenharmony_ci#endif 38998c2ecf20Sopenharmony_ci if (request_type & CHAINED_REQUEST) { 39008c2ecf20Sopenharmony_ci if (!(request_type & END_OF_CHAIN)) { 39018c2ecf20Sopenharmony_ci /* next 8-byte aligned request */ 39028c2ecf20Sopenharmony_ci *total_len = DIV_ROUND_UP(*total_len, 8) * 8; 39038c2ecf20Sopenharmony_ci shdr->NextCommand = cpu_to_le32(*total_len); 39048c2ecf20Sopenharmony_ci } else /* END_OF_CHAIN */ 39058c2ecf20Sopenharmony_ci shdr->NextCommand = 0; 39068c2ecf20Sopenharmony_ci if (request_type & RELATED_REQUEST) { 39078c2ecf20Sopenharmony_ci shdr->Flags |= SMB2_FLAGS_RELATED_OPERATIONS; 39088c2ecf20Sopenharmony_ci /* 39098c2ecf20Sopenharmony_ci * Related requests use info from previous read request 39108c2ecf20Sopenharmony_ci * in chain. 39118c2ecf20Sopenharmony_ci */ 39128c2ecf20Sopenharmony_ci shdr->SessionId = 0xFFFFFFFFFFFFFFFF; 39138c2ecf20Sopenharmony_ci shdr->TreeId = 0xFFFFFFFF; 39148c2ecf20Sopenharmony_ci req->PersistentFileId = 0xFFFFFFFFFFFFFFFF; 39158c2ecf20Sopenharmony_ci req->VolatileFileId = 0xFFFFFFFFFFFFFFFF; 39168c2ecf20Sopenharmony_ci } 39178c2ecf20Sopenharmony_ci } 39188c2ecf20Sopenharmony_ci if (remaining_bytes > io_parms->length) 39198c2ecf20Sopenharmony_ci req->RemainingBytes = cpu_to_le32(remaining_bytes); 39208c2ecf20Sopenharmony_ci else 39218c2ecf20Sopenharmony_ci req->RemainingBytes = 0; 39228c2ecf20Sopenharmony_ci 39238c2ecf20Sopenharmony_ci *buf = req; 39248c2ecf20Sopenharmony_ci return rc; 39258c2ecf20Sopenharmony_ci} 39268c2ecf20Sopenharmony_ci 39278c2ecf20Sopenharmony_cistatic void 39288c2ecf20Sopenharmony_cismb2_readv_callback(struct mid_q_entry *mid) 39298c2ecf20Sopenharmony_ci{ 39308c2ecf20Sopenharmony_ci struct cifs_readdata *rdata = mid->callback_data; 39318c2ecf20Sopenharmony_ci struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink); 39328c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = rdata->server; 39338c2ecf20Sopenharmony_ci struct smb2_sync_hdr *shdr = 39348c2ecf20Sopenharmony_ci (struct smb2_sync_hdr *)rdata->iov[0].iov_base; 39358c2ecf20Sopenharmony_ci struct cifs_credits credits = { .value = 0, .instance = 0 }; 39368c2ecf20Sopenharmony_ci struct smb_rqst rqst = { .rq_iov = &rdata->iov[1], 39378c2ecf20Sopenharmony_ci .rq_nvec = 1, }; 39388c2ecf20Sopenharmony_ci 39398c2ecf20Sopenharmony_ci if (rdata->got_bytes) { 39408c2ecf20Sopenharmony_ci rqst.rq_pages = rdata->pages; 39418c2ecf20Sopenharmony_ci rqst.rq_offset = rdata->page_offset; 39428c2ecf20Sopenharmony_ci rqst.rq_npages = rdata->nr_pages; 39438c2ecf20Sopenharmony_ci rqst.rq_pagesz = rdata->pagesz; 39448c2ecf20Sopenharmony_ci rqst.rq_tailsz = rdata->tailsz; 39458c2ecf20Sopenharmony_ci } 39468c2ecf20Sopenharmony_ci 39478c2ecf20Sopenharmony_ci WARN_ONCE(rdata->server != mid->server, 39488c2ecf20Sopenharmony_ci "rdata server %p != mid server %p", 39498c2ecf20Sopenharmony_ci rdata->server, mid->server); 39508c2ecf20Sopenharmony_ci 39518c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: mid=%llu state=%d result=%d bytes=%u\n", 39528c2ecf20Sopenharmony_ci __func__, mid->mid, mid->mid_state, rdata->result, 39538c2ecf20Sopenharmony_ci rdata->bytes); 39548c2ecf20Sopenharmony_ci 39558c2ecf20Sopenharmony_ci switch (mid->mid_state) { 39568c2ecf20Sopenharmony_ci case MID_RESPONSE_RECEIVED: 39578c2ecf20Sopenharmony_ci credits.value = le16_to_cpu(shdr->CreditRequest); 39588c2ecf20Sopenharmony_ci credits.instance = server->reconnect_instance; 39598c2ecf20Sopenharmony_ci /* result already set, check signature */ 39608c2ecf20Sopenharmony_ci if (server->sign && !mid->decrypted) { 39618c2ecf20Sopenharmony_ci int rc; 39628c2ecf20Sopenharmony_ci 39638c2ecf20Sopenharmony_ci rc = smb2_verify_signature(&rqst, server); 39648c2ecf20Sopenharmony_ci if (rc) 39658c2ecf20Sopenharmony_ci cifs_tcon_dbg(VFS, "SMB signature verification returned error = %d\n", 39668c2ecf20Sopenharmony_ci rc); 39678c2ecf20Sopenharmony_ci } 39688c2ecf20Sopenharmony_ci /* FIXME: should this be counted toward the initiating task? */ 39698c2ecf20Sopenharmony_ci task_io_account_read(rdata->got_bytes); 39708c2ecf20Sopenharmony_ci cifs_stats_bytes_read(tcon, rdata->got_bytes); 39718c2ecf20Sopenharmony_ci break; 39728c2ecf20Sopenharmony_ci case MID_REQUEST_SUBMITTED: 39738c2ecf20Sopenharmony_ci case MID_RETRY_NEEDED: 39748c2ecf20Sopenharmony_ci rdata->result = -EAGAIN; 39758c2ecf20Sopenharmony_ci if (server->sign && rdata->got_bytes) 39768c2ecf20Sopenharmony_ci /* reset bytes number since we can not check a sign */ 39778c2ecf20Sopenharmony_ci rdata->got_bytes = 0; 39788c2ecf20Sopenharmony_ci /* FIXME: should this be counted toward the initiating task? */ 39798c2ecf20Sopenharmony_ci task_io_account_read(rdata->got_bytes); 39808c2ecf20Sopenharmony_ci cifs_stats_bytes_read(tcon, rdata->got_bytes); 39818c2ecf20Sopenharmony_ci break; 39828c2ecf20Sopenharmony_ci case MID_RESPONSE_MALFORMED: 39838c2ecf20Sopenharmony_ci credits.value = le16_to_cpu(shdr->CreditRequest); 39848c2ecf20Sopenharmony_ci credits.instance = server->reconnect_instance; 39858c2ecf20Sopenharmony_ci fallthrough; 39868c2ecf20Sopenharmony_ci default: 39878c2ecf20Sopenharmony_ci rdata->result = -EIO; 39888c2ecf20Sopenharmony_ci } 39898c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_SMB_DIRECT 39908c2ecf20Sopenharmony_ci /* 39918c2ecf20Sopenharmony_ci * If this rdata has a memmory registered, the MR can be freed 39928c2ecf20Sopenharmony_ci * MR needs to be freed as soon as I/O finishes to prevent deadlock 39938c2ecf20Sopenharmony_ci * because they have limited number and are used for future I/Os 39948c2ecf20Sopenharmony_ci */ 39958c2ecf20Sopenharmony_ci if (rdata->mr) { 39968c2ecf20Sopenharmony_ci smbd_deregister_mr(rdata->mr); 39978c2ecf20Sopenharmony_ci rdata->mr = NULL; 39988c2ecf20Sopenharmony_ci } 39998c2ecf20Sopenharmony_ci#endif 40008c2ecf20Sopenharmony_ci if (rdata->result && rdata->result != -ENODATA) { 40018c2ecf20Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_READ_HE); 40028c2ecf20Sopenharmony_ci trace_smb3_read_err(0 /* xid */, 40038c2ecf20Sopenharmony_ci rdata->cfile->fid.persistent_fid, 40048c2ecf20Sopenharmony_ci tcon->tid, tcon->ses->Suid, rdata->offset, 40058c2ecf20Sopenharmony_ci rdata->bytes, rdata->result); 40068c2ecf20Sopenharmony_ci } else 40078c2ecf20Sopenharmony_ci trace_smb3_read_done(0 /* xid */, 40088c2ecf20Sopenharmony_ci rdata->cfile->fid.persistent_fid, 40098c2ecf20Sopenharmony_ci tcon->tid, tcon->ses->Suid, 40108c2ecf20Sopenharmony_ci rdata->offset, rdata->got_bytes); 40118c2ecf20Sopenharmony_ci 40128c2ecf20Sopenharmony_ci queue_work(cifsiod_wq, &rdata->work); 40138c2ecf20Sopenharmony_ci DeleteMidQEntry(mid); 40148c2ecf20Sopenharmony_ci add_credits(server, &credits, 0); 40158c2ecf20Sopenharmony_ci} 40168c2ecf20Sopenharmony_ci 40178c2ecf20Sopenharmony_ci/* smb2_async_readv - send an async read, and set up mid to handle result */ 40188c2ecf20Sopenharmony_ciint 40198c2ecf20Sopenharmony_cismb2_async_readv(struct cifs_readdata *rdata) 40208c2ecf20Sopenharmony_ci{ 40218c2ecf20Sopenharmony_ci int rc, flags = 0; 40228c2ecf20Sopenharmony_ci char *buf; 40238c2ecf20Sopenharmony_ci struct smb2_sync_hdr *shdr; 40248c2ecf20Sopenharmony_ci struct cifs_io_parms io_parms; 40258c2ecf20Sopenharmony_ci struct smb_rqst rqst = { .rq_iov = rdata->iov, 40268c2ecf20Sopenharmony_ci .rq_nvec = 1 }; 40278c2ecf20Sopenharmony_ci struct TCP_Server_Info *server; 40288c2ecf20Sopenharmony_ci struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink); 40298c2ecf20Sopenharmony_ci unsigned int total_len; 40308c2ecf20Sopenharmony_ci 40318c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: offset=%llu bytes=%u\n", 40328c2ecf20Sopenharmony_ci __func__, rdata->offset, rdata->bytes); 40338c2ecf20Sopenharmony_ci 40348c2ecf20Sopenharmony_ci if (!rdata->server) 40358c2ecf20Sopenharmony_ci rdata->server = cifs_pick_channel(tcon->ses); 40368c2ecf20Sopenharmony_ci 40378c2ecf20Sopenharmony_ci io_parms.tcon = tlink_tcon(rdata->cfile->tlink); 40388c2ecf20Sopenharmony_ci io_parms.server = server = rdata->server; 40398c2ecf20Sopenharmony_ci io_parms.offset = rdata->offset; 40408c2ecf20Sopenharmony_ci io_parms.length = rdata->bytes; 40418c2ecf20Sopenharmony_ci io_parms.persistent_fid = rdata->cfile->fid.persistent_fid; 40428c2ecf20Sopenharmony_ci io_parms.volatile_fid = rdata->cfile->fid.volatile_fid; 40438c2ecf20Sopenharmony_ci io_parms.pid = rdata->pid; 40448c2ecf20Sopenharmony_ci 40458c2ecf20Sopenharmony_ci rc = smb2_new_read_req( 40468c2ecf20Sopenharmony_ci (void **) &buf, &total_len, &io_parms, rdata, 0, 0); 40478c2ecf20Sopenharmony_ci if (rc) 40488c2ecf20Sopenharmony_ci return rc; 40498c2ecf20Sopenharmony_ci 40508c2ecf20Sopenharmony_ci if (smb3_encryption_required(io_parms.tcon)) 40518c2ecf20Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 40528c2ecf20Sopenharmony_ci 40538c2ecf20Sopenharmony_ci rdata->iov[0].iov_base = buf; 40548c2ecf20Sopenharmony_ci rdata->iov[0].iov_len = total_len; 40558c2ecf20Sopenharmony_ci 40568c2ecf20Sopenharmony_ci shdr = (struct smb2_sync_hdr *)buf; 40578c2ecf20Sopenharmony_ci 40588c2ecf20Sopenharmony_ci if (rdata->credits.value > 0) { 40598c2ecf20Sopenharmony_ci shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->bytes, 40608c2ecf20Sopenharmony_ci SMB2_MAX_BUFFER_SIZE)); 40618c2ecf20Sopenharmony_ci shdr->CreditRequest = cpu_to_le16(le16_to_cpu(shdr->CreditCharge) + 8); 40628c2ecf20Sopenharmony_ci 40638c2ecf20Sopenharmony_ci rc = adjust_credits(server, &rdata->credits, rdata->bytes); 40648c2ecf20Sopenharmony_ci if (rc) 40658c2ecf20Sopenharmony_ci goto async_readv_out; 40668c2ecf20Sopenharmony_ci 40678c2ecf20Sopenharmony_ci flags |= CIFS_HAS_CREDITS; 40688c2ecf20Sopenharmony_ci } 40698c2ecf20Sopenharmony_ci 40708c2ecf20Sopenharmony_ci kref_get(&rdata->refcount); 40718c2ecf20Sopenharmony_ci rc = cifs_call_async(server, &rqst, 40728c2ecf20Sopenharmony_ci cifs_readv_receive, smb2_readv_callback, 40738c2ecf20Sopenharmony_ci smb3_handle_read_data, rdata, flags, 40748c2ecf20Sopenharmony_ci &rdata->credits); 40758c2ecf20Sopenharmony_ci if (rc) { 40768c2ecf20Sopenharmony_ci kref_put(&rdata->refcount, cifs_readdata_release); 40778c2ecf20Sopenharmony_ci cifs_stats_fail_inc(io_parms.tcon, SMB2_READ_HE); 40788c2ecf20Sopenharmony_ci trace_smb3_read_err(0 /* xid */, io_parms.persistent_fid, 40798c2ecf20Sopenharmony_ci io_parms.tcon->tid, 40808c2ecf20Sopenharmony_ci io_parms.tcon->ses->Suid, 40818c2ecf20Sopenharmony_ci io_parms.offset, io_parms.length, rc); 40828c2ecf20Sopenharmony_ci } 40838c2ecf20Sopenharmony_ci 40848c2ecf20Sopenharmony_ciasync_readv_out: 40858c2ecf20Sopenharmony_ci cifs_small_buf_release(buf); 40868c2ecf20Sopenharmony_ci return rc; 40878c2ecf20Sopenharmony_ci} 40888c2ecf20Sopenharmony_ci 40898c2ecf20Sopenharmony_ciint 40908c2ecf20Sopenharmony_ciSMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms, 40918c2ecf20Sopenharmony_ci unsigned int *nbytes, char **buf, int *buf_type) 40928c2ecf20Sopenharmony_ci{ 40938c2ecf20Sopenharmony_ci struct smb_rqst rqst; 40948c2ecf20Sopenharmony_ci int resp_buftype, rc; 40958c2ecf20Sopenharmony_ci struct smb2_read_plain_req *req = NULL; 40968c2ecf20Sopenharmony_ci struct smb2_read_rsp *rsp = NULL; 40978c2ecf20Sopenharmony_ci struct kvec iov[1]; 40988c2ecf20Sopenharmony_ci struct kvec rsp_iov; 40998c2ecf20Sopenharmony_ci unsigned int total_len; 41008c2ecf20Sopenharmony_ci int flags = CIFS_LOG_ERROR; 41018c2ecf20Sopenharmony_ci struct cifs_ses *ses = io_parms->tcon->ses; 41028c2ecf20Sopenharmony_ci 41038c2ecf20Sopenharmony_ci if (!io_parms->server) 41048c2ecf20Sopenharmony_ci io_parms->server = cifs_pick_channel(io_parms->tcon->ses); 41058c2ecf20Sopenharmony_ci 41068c2ecf20Sopenharmony_ci *nbytes = 0; 41078c2ecf20Sopenharmony_ci rc = smb2_new_read_req((void **)&req, &total_len, io_parms, NULL, 0, 0); 41088c2ecf20Sopenharmony_ci if (rc) 41098c2ecf20Sopenharmony_ci return rc; 41108c2ecf20Sopenharmony_ci 41118c2ecf20Sopenharmony_ci if (smb3_encryption_required(io_parms->tcon)) 41128c2ecf20Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 41138c2ecf20Sopenharmony_ci 41148c2ecf20Sopenharmony_ci iov[0].iov_base = (char *)req; 41158c2ecf20Sopenharmony_ci iov[0].iov_len = total_len; 41168c2ecf20Sopenharmony_ci 41178c2ecf20Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 41188c2ecf20Sopenharmony_ci rqst.rq_iov = iov; 41198c2ecf20Sopenharmony_ci rqst.rq_nvec = 1; 41208c2ecf20Sopenharmony_ci 41218c2ecf20Sopenharmony_ci rc = cifs_send_recv(xid, ses, io_parms->server, 41228c2ecf20Sopenharmony_ci &rqst, &resp_buftype, flags, &rsp_iov); 41238c2ecf20Sopenharmony_ci rsp = (struct smb2_read_rsp *)rsp_iov.iov_base; 41248c2ecf20Sopenharmony_ci 41258c2ecf20Sopenharmony_ci if (rc) { 41268c2ecf20Sopenharmony_ci if (rc != -ENODATA) { 41278c2ecf20Sopenharmony_ci cifs_stats_fail_inc(io_parms->tcon, SMB2_READ_HE); 41288c2ecf20Sopenharmony_ci cifs_dbg(VFS, "Send error in read = %d\n", rc); 41298c2ecf20Sopenharmony_ci trace_smb3_read_err(xid, req->PersistentFileId, 41308c2ecf20Sopenharmony_ci io_parms->tcon->tid, ses->Suid, 41318c2ecf20Sopenharmony_ci io_parms->offset, io_parms->length, 41328c2ecf20Sopenharmony_ci rc); 41338c2ecf20Sopenharmony_ci } else 41348c2ecf20Sopenharmony_ci trace_smb3_read_done(xid, req->PersistentFileId, 41358c2ecf20Sopenharmony_ci io_parms->tcon->tid, ses->Suid, 41368c2ecf20Sopenharmony_ci io_parms->offset, 0); 41378c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype, rsp_iov.iov_base); 41388c2ecf20Sopenharmony_ci cifs_small_buf_release(req); 41398c2ecf20Sopenharmony_ci return rc == -ENODATA ? 0 : rc; 41408c2ecf20Sopenharmony_ci } else 41418c2ecf20Sopenharmony_ci trace_smb3_read_done(xid, req->PersistentFileId, 41428c2ecf20Sopenharmony_ci io_parms->tcon->tid, ses->Suid, 41438c2ecf20Sopenharmony_ci io_parms->offset, io_parms->length); 41448c2ecf20Sopenharmony_ci 41458c2ecf20Sopenharmony_ci cifs_small_buf_release(req); 41468c2ecf20Sopenharmony_ci 41478c2ecf20Sopenharmony_ci *nbytes = le32_to_cpu(rsp->DataLength); 41488c2ecf20Sopenharmony_ci if ((*nbytes > CIFS_MAX_MSGSIZE) || 41498c2ecf20Sopenharmony_ci (*nbytes > io_parms->length)) { 41508c2ecf20Sopenharmony_ci cifs_dbg(FYI, "bad length %d for count %d\n", 41518c2ecf20Sopenharmony_ci *nbytes, io_parms->length); 41528c2ecf20Sopenharmony_ci rc = -EIO; 41538c2ecf20Sopenharmony_ci *nbytes = 0; 41548c2ecf20Sopenharmony_ci } 41558c2ecf20Sopenharmony_ci 41568c2ecf20Sopenharmony_ci if (*buf) { 41578c2ecf20Sopenharmony_ci memcpy(*buf, (char *)rsp + rsp->DataOffset, *nbytes); 41588c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype, rsp_iov.iov_base); 41598c2ecf20Sopenharmony_ci } else if (resp_buftype != CIFS_NO_BUFFER) { 41608c2ecf20Sopenharmony_ci *buf = rsp_iov.iov_base; 41618c2ecf20Sopenharmony_ci if (resp_buftype == CIFS_SMALL_BUFFER) 41628c2ecf20Sopenharmony_ci *buf_type = CIFS_SMALL_BUFFER; 41638c2ecf20Sopenharmony_ci else if (resp_buftype == CIFS_LARGE_BUFFER) 41648c2ecf20Sopenharmony_ci *buf_type = CIFS_LARGE_BUFFER; 41658c2ecf20Sopenharmony_ci } 41668c2ecf20Sopenharmony_ci return rc; 41678c2ecf20Sopenharmony_ci} 41688c2ecf20Sopenharmony_ci 41698c2ecf20Sopenharmony_ci/* 41708c2ecf20Sopenharmony_ci * Check the mid_state and signature on received buffer (if any), and queue the 41718c2ecf20Sopenharmony_ci * workqueue completion task. 41728c2ecf20Sopenharmony_ci */ 41738c2ecf20Sopenharmony_cistatic void 41748c2ecf20Sopenharmony_cismb2_writev_callback(struct mid_q_entry *mid) 41758c2ecf20Sopenharmony_ci{ 41768c2ecf20Sopenharmony_ci struct cifs_writedata *wdata = mid->callback_data; 41778c2ecf20Sopenharmony_ci struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); 41788c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = wdata->server; 41798c2ecf20Sopenharmony_ci unsigned int written; 41808c2ecf20Sopenharmony_ci struct smb2_write_rsp *rsp = (struct smb2_write_rsp *)mid->resp_buf; 41818c2ecf20Sopenharmony_ci struct cifs_credits credits = { .value = 0, .instance = 0 }; 41828c2ecf20Sopenharmony_ci 41838c2ecf20Sopenharmony_ci WARN_ONCE(wdata->server != mid->server, 41848c2ecf20Sopenharmony_ci "wdata server %p != mid server %p", 41858c2ecf20Sopenharmony_ci wdata->server, mid->server); 41868c2ecf20Sopenharmony_ci 41878c2ecf20Sopenharmony_ci switch (mid->mid_state) { 41888c2ecf20Sopenharmony_ci case MID_RESPONSE_RECEIVED: 41898c2ecf20Sopenharmony_ci credits.value = le16_to_cpu(rsp->sync_hdr.CreditRequest); 41908c2ecf20Sopenharmony_ci credits.instance = server->reconnect_instance; 41918c2ecf20Sopenharmony_ci wdata->result = smb2_check_receive(mid, server, 0); 41928c2ecf20Sopenharmony_ci if (wdata->result != 0) 41938c2ecf20Sopenharmony_ci break; 41948c2ecf20Sopenharmony_ci 41958c2ecf20Sopenharmony_ci written = le32_to_cpu(rsp->DataLength); 41968c2ecf20Sopenharmony_ci /* 41978c2ecf20Sopenharmony_ci * Mask off high 16 bits when bytes written as returned 41988c2ecf20Sopenharmony_ci * by the server is greater than bytes requested by the 41998c2ecf20Sopenharmony_ci * client. OS/2 servers are known to set incorrect 42008c2ecf20Sopenharmony_ci * CountHigh values. 42018c2ecf20Sopenharmony_ci */ 42028c2ecf20Sopenharmony_ci if (written > wdata->bytes) 42038c2ecf20Sopenharmony_ci written &= 0xFFFF; 42048c2ecf20Sopenharmony_ci 42058c2ecf20Sopenharmony_ci if (written < wdata->bytes) 42068c2ecf20Sopenharmony_ci wdata->result = -ENOSPC; 42078c2ecf20Sopenharmony_ci else 42088c2ecf20Sopenharmony_ci wdata->bytes = written; 42098c2ecf20Sopenharmony_ci break; 42108c2ecf20Sopenharmony_ci case MID_REQUEST_SUBMITTED: 42118c2ecf20Sopenharmony_ci case MID_RETRY_NEEDED: 42128c2ecf20Sopenharmony_ci wdata->result = -EAGAIN; 42138c2ecf20Sopenharmony_ci break; 42148c2ecf20Sopenharmony_ci case MID_RESPONSE_MALFORMED: 42158c2ecf20Sopenharmony_ci credits.value = le16_to_cpu(rsp->sync_hdr.CreditRequest); 42168c2ecf20Sopenharmony_ci credits.instance = server->reconnect_instance; 42178c2ecf20Sopenharmony_ci fallthrough; 42188c2ecf20Sopenharmony_ci default: 42198c2ecf20Sopenharmony_ci wdata->result = -EIO; 42208c2ecf20Sopenharmony_ci break; 42218c2ecf20Sopenharmony_ci } 42228c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_SMB_DIRECT 42238c2ecf20Sopenharmony_ci /* 42248c2ecf20Sopenharmony_ci * If this wdata has a memory registered, the MR can be freed 42258c2ecf20Sopenharmony_ci * The number of MRs available is limited, it's important to recover 42268c2ecf20Sopenharmony_ci * used MR as soon as I/O is finished. Hold MR longer in the later 42278c2ecf20Sopenharmony_ci * I/O process can possibly result in I/O deadlock due to lack of MR 42288c2ecf20Sopenharmony_ci * to send request on I/O retry 42298c2ecf20Sopenharmony_ci */ 42308c2ecf20Sopenharmony_ci if (wdata->mr) { 42318c2ecf20Sopenharmony_ci smbd_deregister_mr(wdata->mr); 42328c2ecf20Sopenharmony_ci wdata->mr = NULL; 42338c2ecf20Sopenharmony_ci } 42348c2ecf20Sopenharmony_ci#endif 42358c2ecf20Sopenharmony_ci if (wdata->result) { 42368c2ecf20Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_WRITE_HE); 42378c2ecf20Sopenharmony_ci trace_smb3_write_err(0 /* no xid */, 42388c2ecf20Sopenharmony_ci wdata->cfile->fid.persistent_fid, 42398c2ecf20Sopenharmony_ci tcon->tid, tcon->ses->Suid, wdata->offset, 42408c2ecf20Sopenharmony_ci wdata->bytes, wdata->result); 42418c2ecf20Sopenharmony_ci if (wdata->result == -ENOSPC) 42428c2ecf20Sopenharmony_ci pr_warn_once("Out of space writing to %s\n", 42438c2ecf20Sopenharmony_ci tcon->treeName); 42448c2ecf20Sopenharmony_ci } else 42458c2ecf20Sopenharmony_ci trace_smb3_write_done(0 /* no xid */, 42468c2ecf20Sopenharmony_ci wdata->cfile->fid.persistent_fid, 42478c2ecf20Sopenharmony_ci tcon->tid, tcon->ses->Suid, 42488c2ecf20Sopenharmony_ci wdata->offset, wdata->bytes); 42498c2ecf20Sopenharmony_ci 42508c2ecf20Sopenharmony_ci queue_work(cifsiod_wq, &wdata->work); 42518c2ecf20Sopenharmony_ci DeleteMidQEntry(mid); 42528c2ecf20Sopenharmony_ci add_credits(server, &credits, 0); 42538c2ecf20Sopenharmony_ci} 42548c2ecf20Sopenharmony_ci 42558c2ecf20Sopenharmony_ci/* smb2_async_writev - send an async write, and set up mid to handle result */ 42568c2ecf20Sopenharmony_ciint 42578c2ecf20Sopenharmony_cismb2_async_writev(struct cifs_writedata *wdata, 42588c2ecf20Sopenharmony_ci void (*release)(struct kref *kref)) 42598c2ecf20Sopenharmony_ci{ 42608c2ecf20Sopenharmony_ci int rc = -EACCES, flags = 0; 42618c2ecf20Sopenharmony_ci struct smb2_write_req *req = NULL; 42628c2ecf20Sopenharmony_ci struct smb2_sync_hdr *shdr; 42638c2ecf20Sopenharmony_ci struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); 42648c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = wdata->server; 42658c2ecf20Sopenharmony_ci struct kvec iov[1]; 42668c2ecf20Sopenharmony_ci struct smb_rqst rqst = { }; 42678c2ecf20Sopenharmony_ci unsigned int total_len; 42688c2ecf20Sopenharmony_ci 42698c2ecf20Sopenharmony_ci if (!wdata->server) 42708c2ecf20Sopenharmony_ci server = wdata->server = cifs_pick_channel(tcon->ses); 42718c2ecf20Sopenharmony_ci 42728c2ecf20Sopenharmony_ci rc = smb2_plain_req_init(SMB2_WRITE, tcon, server, 42738c2ecf20Sopenharmony_ci (void **) &req, &total_len); 42748c2ecf20Sopenharmony_ci if (rc) 42758c2ecf20Sopenharmony_ci return rc; 42768c2ecf20Sopenharmony_ci 42778c2ecf20Sopenharmony_ci if (smb3_encryption_required(tcon)) 42788c2ecf20Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 42798c2ecf20Sopenharmony_ci 42808c2ecf20Sopenharmony_ci shdr = (struct smb2_sync_hdr *)req; 42818c2ecf20Sopenharmony_ci shdr->ProcessId = cpu_to_le32(wdata->cfile->pid); 42828c2ecf20Sopenharmony_ci 42838c2ecf20Sopenharmony_ci req->PersistentFileId = wdata->cfile->fid.persistent_fid; 42848c2ecf20Sopenharmony_ci req->VolatileFileId = wdata->cfile->fid.volatile_fid; 42858c2ecf20Sopenharmony_ci req->WriteChannelInfoOffset = 0; 42868c2ecf20Sopenharmony_ci req->WriteChannelInfoLength = 0; 42878c2ecf20Sopenharmony_ci req->Channel = 0; 42888c2ecf20Sopenharmony_ci req->Offset = cpu_to_le64(wdata->offset); 42898c2ecf20Sopenharmony_ci req->DataOffset = cpu_to_le16( 42908c2ecf20Sopenharmony_ci offsetof(struct smb2_write_req, Buffer)); 42918c2ecf20Sopenharmony_ci req->RemainingBytes = 0; 42928c2ecf20Sopenharmony_ci 42938c2ecf20Sopenharmony_ci trace_smb3_write_enter(0 /* xid */, wdata->cfile->fid.persistent_fid, 42948c2ecf20Sopenharmony_ci tcon->tid, tcon->ses->Suid, wdata->offset, wdata->bytes); 42958c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_SMB_DIRECT 42968c2ecf20Sopenharmony_ci /* 42978c2ecf20Sopenharmony_ci * If we want to do a server RDMA read, fill in and append 42988c2ecf20Sopenharmony_ci * smbd_buffer_descriptor_v1 to the end of write request 42998c2ecf20Sopenharmony_ci */ 43008c2ecf20Sopenharmony_ci if (server->rdma && !server->sign && wdata->bytes >= 43018c2ecf20Sopenharmony_ci server->smbd_conn->rdma_readwrite_threshold) { 43028c2ecf20Sopenharmony_ci 43038c2ecf20Sopenharmony_ci struct smbd_buffer_descriptor_v1 *v1; 43048c2ecf20Sopenharmony_ci bool need_invalidate = server->dialect == SMB30_PROT_ID; 43058c2ecf20Sopenharmony_ci 43068c2ecf20Sopenharmony_ci wdata->mr = smbd_register_mr( 43078c2ecf20Sopenharmony_ci server->smbd_conn, wdata->pages, 43088c2ecf20Sopenharmony_ci wdata->nr_pages, wdata->page_offset, 43098c2ecf20Sopenharmony_ci wdata->tailsz, false, need_invalidate); 43108c2ecf20Sopenharmony_ci if (!wdata->mr) { 43118c2ecf20Sopenharmony_ci rc = -EAGAIN; 43128c2ecf20Sopenharmony_ci goto async_writev_out; 43138c2ecf20Sopenharmony_ci } 43148c2ecf20Sopenharmony_ci req->Length = 0; 43158c2ecf20Sopenharmony_ci req->DataOffset = 0; 43168c2ecf20Sopenharmony_ci if (wdata->nr_pages > 1) 43178c2ecf20Sopenharmony_ci req->RemainingBytes = 43188c2ecf20Sopenharmony_ci cpu_to_le32( 43198c2ecf20Sopenharmony_ci (wdata->nr_pages - 1) * wdata->pagesz - 43208c2ecf20Sopenharmony_ci wdata->page_offset + wdata->tailsz 43218c2ecf20Sopenharmony_ci ); 43228c2ecf20Sopenharmony_ci else 43238c2ecf20Sopenharmony_ci req->RemainingBytes = cpu_to_le32(wdata->tailsz); 43248c2ecf20Sopenharmony_ci req->Channel = SMB2_CHANNEL_RDMA_V1_INVALIDATE; 43258c2ecf20Sopenharmony_ci if (need_invalidate) 43268c2ecf20Sopenharmony_ci req->Channel = SMB2_CHANNEL_RDMA_V1; 43278c2ecf20Sopenharmony_ci req->WriteChannelInfoOffset = 43288c2ecf20Sopenharmony_ci cpu_to_le16(offsetof(struct smb2_write_req, Buffer)); 43298c2ecf20Sopenharmony_ci req->WriteChannelInfoLength = 43308c2ecf20Sopenharmony_ci cpu_to_le16(sizeof(struct smbd_buffer_descriptor_v1)); 43318c2ecf20Sopenharmony_ci v1 = (struct smbd_buffer_descriptor_v1 *) &req->Buffer[0]; 43328c2ecf20Sopenharmony_ci v1->offset = cpu_to_le64(wdata->mr->mr->iova); 43338c2ecf20Sopenharmony_ci v1->token = cpu_to_le32(wdata->mr->mr->rkey); 43348c2ecf20Sopenharmony_ci v1->length = cpu_to_le32(wdata->mr->mr->length); 43358c2ecf20Sopenharmony_ci } 43368c2ecf20Sopenharmony_ci#endif 43378c2ecf20Sopenharmony_ci iov[0].iov_len = total_len - 1; 43388c2ecf20Sopenharmony_ci iov[0].iov_base = (char *)req; 43398c2ecf20Sopenharmony_ci 43408c2ecf20Sopenharmony_ci rqst.rq_iov = iov; 43418c2ecf20Sopenharmony_ci rqst.rq_nvec = 1; 43428c2ecf20Sopenharmony_ci rqst.rq_pages = wdata->pages; 43438c2ecf20Sopenharmony_ci rqst.rq_offset = wdata->page_offset; 43448c2ecf20Sopenharmony_ci rqst.rq_npages = wdata->nr_pages; 43458c2ecf20Sopenharmony_ci rqst.rq_pagesz = wdata->pagesz; 43468c2ecf20Sopenharmony_ci rqst.rq_tailsz = wdata->tailsz; 43478c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_SMB_DIRECT 43488c2ecf20Sopenharmony_ci if (wdata->mr) { 43498c2ecf20Sopenharmony_ci iov[0].iov_len += sizeof(struct smbd_buffer_descriptor_v1); 43508c2ecf20Sopenharmony_ci rqst.rq_npages = 0; 43518c2ecf20Sopenharmony_ci } 43528c2ecf20Sopenharmony_ci#endif 43538c2ecf20Sopenharmony_ci cifs_dbg(FYI, "async write at %llu %u bytes\n", 43548c2ecf20Sopenharmony_ci wdata->offset, wdata->bytes); 43558c2ecf20Sopenharmony_ci 43568c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_SMB_DIRECT 43578c2ecf20Sopenharmony_ci /* For RDMA read, I/O size is in RemainingBytes not in Length */ 43588c2ecf20Sopenharmony_ci if (!wdata->mr) 43598c2ecf20Sopenharmony_ci req->Length = cpu_to_le32(wdata->bytes); 43608c2ecf20Sopenharmony_ci#else 43618c2ecf20Sopenharmony_ci req->Length = cpu_to_le32(wdata->bytes); 43628c2ecf20Sopenharmony_ci#endif 43638c2ecf20Sopenharmony_ci 43648c2ecf20Sopenharmony_ci if (wdata->credits.value > 0) { 43658c2ecf20Sopenharmony_ci shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(wdata->bytes, 43668c2ecf20Sopenharmony_ci SMB2_MAX_BUFFER_SIZE)); 43678c2ecf20Sopenharmony_ci shdr->CreditRequest = cpu_to_le16(le16_to_cpu(shdr->CreditCharge) + 8); 43688c2ecf20Sopenharmony_ci 43698c2ecf20Sopenharmony_ci rc = adjust_credits(server, &wdata->credits, wdata->bytes); 43708c2ecf20Sopenharmony_ci if (rc) 43718c2ecf20Sopenharmony_ci goto async_writev_out; 43728c2ecf20Sopenharmony_ci 43738c2ecf20Sopenharmony_ci flags |= CIFS_HAS_CREDITS; 43748c2ecf20Sopenharmony_ci } 43758c2ecf20Sopenharmony_ci 43768c2ecf20Sopenharmony_ci kref_get(&wdata->refcount); 43778c2ecf20Sopenharmony_ci rc = cifs_call_async(server, &rqst, NULL, smb2_writev_callback, NULL, 43788c2ecf20Sopenharmony_ci wdata, flags, &wdata->credits); 43798c2ecf20Sopenharmony_ci 43808c2ecf20Sopenharmony_ci if (rc) { 43818c2ecf20Sopenharmony_ci trace_smb3_write_err(0 /* no xid */, req->PersistentFileId, 43828c2ecf20Sopenharmony_ci tcon->tid, tcon->ses->Suid, wdata->offset, 43838c2ecf20Sopenharmony_ci wdata->bytes, rc); 43848c2ecf20Sopenharmony_ci kref_put(&wdata->refcount, release); 43858c2ecf20Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_WRITE_HE); 43868c2ecf20Sopenharmony_ci } 43878c2ecf20Sopenharmony_ci 43888c2ecf20Sopenharmony_ciasync_writev_out: 43898c2ecf20Sopenharmony_ci cifs_small_buf_release(req); 43908c2ecf20Sopenharmony_ci return rc; 43918c2ecf20Sopenharmony_ci} 43928c2ecf20Sopenharmony_ci 43938c2ecf20Sopenharmony_ci/* 43948c2ecf20Sopenharmony_ci * SMB2_write function gets iov pointer to kvec array with n_vec as a length. 43958c2ecf20Sopenharmony_ci * The length field from io_parms must be at least 1 and indicates a number of 43968c2ecf20Sopenharmony_ci * elements with data to write that begins with position 1 in iov array. All 43978c2ecf20Sopenharmony_ci * data length is specified by count. 43988c2ecf20Sopenharmony_ci */ 43998c2ecf20Sopenharmony_ciint 44008c2ecf20Sopenharmony_ciSMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms, 44018c2ecf20Sopenharmony_ci unsigned int *nbytes, struct kvec *iov, int n_vec) 44028c2ecf20Sopenharmony_ci{ 44038c2ecf20Sopenharmony_ci struct smb_rqst rqst; 44048c2ecf20Sopenharmony_ci int rc = 0; 44058c2ecf20Sopenharmony_ci struct smb2_write_req *req = NULL; 44068c2ecf20Sopenharmony_ci struct smb2_write_rsp *rsp = NULL; 44078c2ecf20Sopenharmony_ci int resp_buftype; 44088c2ecf20Sopenharmony_ci struct kvec rsp_iov; 44098c2ecf20Sopenharmony_ci int flags = 0; 44108c2ecf20Sopenharmony_ci unsigned int total_len; 44118c2ecf20Sopenharmony_ci struct TCP_Server_Info *server; 44128c2ecf20Sopenharmony_ci 44138c2ecf20Sopenharmony_ci *nbytes = 0; 44148c2ecf20Sopenharmony_ci 44158c2ecf20Sopenharmony_ci if (n_vec < 1) 44168c2ecf20Sopenharmony_ci return rc; 44178c2ecf20Sopenharmony_ci 44188c2ecf20Sopenharmony_ci if (!io_parms->server) 44198c2ecf20Sopenharmony_ci io_parms->server = cifs_pick_channel(io_parms->tcon->ses); 44208c2ecf20Sopenharmony_ci server = io_parms->server; 44218c2ecf20Sopenharmony_ci if (server == NULL) 44228c2ecf20Sopenharmony_ci return -ECONNABORTED; 44238c2ecf20Sopenharmony_ci 44248c2ecf20Sopenharmony_ci rc = smb2_plain_req_init(SMB2_WRITE, io_parms->tcon, server, 44258c2ecf20Sopenharmony_ci (void **) &req, &total_len); 44268c2ecf20Sopenharmony_ci if (rc) 44278c2ecf20Sopenharmony_ci return rc; 44288c2ecf20Sopenharmony_ci 44298c2ecf20Sopenharmony_ci if (smb3_encryption_required(io_parms->tcon)) 44308c2ecf20Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 44318c2ecf20Sopenharmony_ci 44328c2ecf20Sopenharmony_ci req->sync_hdr.ProcessId = cpu_to_le32(io_parms->pid); 44338c2ecf20Sopenharmony_ci 44348c2ecf20Sopenharmony_ci req->PersistentFileId = io_parms->persistent_fid; 44358c2ecf20Sopenharmony_ci req->VolatileFileId = io_parms->volatile_fid; 44368c2ecf20Sopenharmony_ci req->WriteChannelInfoOffset = 0; 44378c2ecf20Sopenharmony_ci req->WriteChannelInfoLength = 0; 44388c2ecf20Sopenharmony_ci req->Channel = 0; 44398c2ecf20Sopenharmony_ci req->Length = cpu_to_le32(io_parms->length); 44408c2ecf20Sopenharmony_ci req->Offset = cpu_to_le64(io_parms->offset); 44418c2ecf20Sopenharmony_ci req->DataOffset = cpu_to_le16( 44428c2ecf20Sopenharmony_ci offsetof(struct smb2_write_req, Buffer)); 44438c2ecf20Sopenharmony_ci req->RemainingBytes = 0; 44448c2ecf20Sopenharmony_ci 44458c2ecf20Sopenharmony_ci trace_smb3_write_enter(xid, io_parms->persistent_fid, 44468c2ecf20Sopenharmony_ci io_parms->tcon->tid, io_parms->tcon->ses->Suid, 44478c2ecf20Sopenharmony_ci io_parms->offset, io_parms->length); 44488c2ecf20Sopenharmony_ci 44498c2ecf20Sopenharmony_ci iov[0].iov_base = (char *)req; 44508c2ecf20Sopenharmony_ci /* 1 for Buffer */ 44518c2ecf20Sopenharmony_ci iov[0].iov_len = total_len - 1; 44528c2ecf20Sopenharmony_ci 44538c2ecf20Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 44548c2ecf20Sopenharmony_ci rqst.rq_iov = iov; 44558c2ecf20Sopenharmony_ci rqst.rq_nvec = n_vec + 1; 44568c2ecf20Sopenharmony_ci 44578c2ecf20Sopenharmony_ci rc = cifs_send_recv(xid, io_parms->tcon->ses, server, 44588c2ecf20Sopenharmony_ci &rqst, 44598c2ecf20Sopenharmony_ci &resp_buftype, flags, &rsp_iov); 44608c2ecf20Sopenharmony_ci rsp = (struct smb2_write_rsp *)rsp_iov.iov_base; 44618c2ecf20Sopenharmony_ci 44628c2ecf20Sopenharmony_ci if (rc) { 44638c2ecf20Sopenharmony_ci trace_smb3_write_err(xid, req->PersistentFileId, 44648c2ecf20Sopenharmony_ci io_parms->tcon->tid, 44658c2ecf20Sopenharmony_ci io_parms->tcon->ses->Suid, 44668c2ecf20Sopenharmony_ci io_parms->offset, io_parms->length, rc); 44678c2ecf20Sopenharmony_ci cifs_stats_fail_inc(io_parms->tcon, SMB2_WRITE_HE); 44688c2ecf20Sopenharmony_ci cifs_dbg(VFS, "Send error in write = %d\n", rc); 44698c2ecf20Sopenharmony_ci } else { 44708c2ecf20Sopenharmony_ci *nbytes = le32_to_cpu(rsp->DataLength); 44718c2ecf20Sopenharmony_ci trace_smb3_write_done(xid, req->PersistentFileId, 44728c2ecf20Sopenharmony_ci io_parms->tcon->tid, 44738c2ecf20Sopenharmony_ci io_parms->tcon->ses->Suid, 44748c2ecf20Sopenharmony_ci io_parms->offset, *nbytes); 44758c2ecf20Sopenharmony_ci } 44768c2ecf20Sopenharmony_ci 44778c2ecf20Sopenharmony_ci cifs_small_buf_release(req); 44788c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype, rsp); 44798c2ecf20Sopenharmony_ci return rc; 44808c2ecf20Sopenharmony_ci} 44818c2ecf20Sopenharmony_ci 44828c2ecf20Sopenharmony_ciint posix_info_sid_size(const void *beg, const void *end) 44838c2ecf20Sopenharmony_ci{ 44848c2ecf20Sopenharmony_ci size_t subauth; 44858c2ecf20Sopenharmony_ci int total; 44868c2ecf20Sopenharmony_ci 44878c2ecf20Sopenharmony_ci if (beg + 1 > end) 44888c2ecf20Sopenharmony_ci return -1; 44898c2ecf20Sopenharmony_ci 44908c2ecf20Sopenharmony_ci subauth = *(u8 *)(beg+1); 44918c2ecf20Sopenharmony_ci if (subauth < 1 || subauth > 15) 44928c2ecf20Sopenharmony_ci return -1; 44938c2ecf20Sopenharmony_ci 44948c2ecf20Sopenharmony_ci total = 1 + 1 + 6 + 4*subauth; 44958c2ecf20Sopenharmony_ci if (beg + total > end) 44968c2ecf20Sopenharmony_ci return -1; 44978c2ecf20Sopenharmony_ci 44988c2ecf20Sopenharmony_ci return total; 44998c2ecf20Sopenharmony_ci} 45008c2ecf20Sopenharmony_ci 45018c2ecf20Sopenharmony_ciint posix_info_parse(const void *beg, const void *end, 45028c2ecf20Sopenharmony_ci struct smb2_posix_info_parsed *out) 45038c2ecf20Sopenharmony_ci 45048c2ecf20Sopenharmony_ci{ 45058c2ecf20Sopenharmony_ci int total_len = 0; 45068c2ecf20Sopenharmony_ci int sid_len; 45078c2ecf20Sopenharmony_ci int name_len; 45088c2ecf20Sopenharmony_ci const void *owner_sid; 45098c2ecf20Sopenharmony_ci const void *group_sid; 45108c2ecf20Sopenharmony_ci const void *name; 45118c2ecf20Sopenharmony_ci 45128c2ecf20Sopenharmony_ci /* if no end bound given, assume payload to be correct */ 45138c2ecf20Sopenharmony_ci if (!end) { 45148c2ecf20Sopenharmony_ci const struct smb2_posix_info *p = beg; 45158c2ecf20Sopenharmony_ci 45168c2ecf20Sopenharmony_ci end = beg + le32_to_cpu(p->NextEntryOffset); 45178c2ecf20Sopenharmony_ci /* last element will have a 0 offset, pick a sensible bound */ 45188c2ecf20Sopenharmony_ci if (end == beg) 45198c2ecf20Sopenharmony_ci end += 0xFFFF; 45208c2ecf20Sopenharmony_ci } 45218c2ecf20Sopenharmony_ci 45228c2ecf20Sopenharmony_ci /* check base buf */ 45238c2ecf20Sopenharmony_ci if (beg + sizeof(struct smb2_posix_info) > end) 45248c2ecf20Sopenharmony_ci return -1; 45258c2ecf20Sopenharmony_ci total_len = sizeof(struct smb2_posix_info); 45268c2ecf20Sopenharmony_ci 45278c2ecf20Sopenharmony_ci /* check owner sid */ 45288c2ecf20Sopenharmony_ci owner_sid = beg + total_len; 45298c2ecf20Sopenharmony_ci sid_len = posix_info_sid_size(owner_sid, end); 45308c2ecf20Sopenharmony_ci if (sid_len < 0) 45318c2ecf20Sopenharmony_ci return -1; 45328c2ecf20Sopenharmony_ci total_len += sid_len; 45338c2ecf20Sopenharmony_ci 45348c2ecf20Sopenharmony_ci /* check group sid */ 45358c2ecf20Sopenharmony_ci group_sid = beg + total_len; 45368c2ecf20Sopenharmony_ci sid_len = posix_info_sid_size(group_sid, end); 45378c2ecf20Sopenharmony_ci if (sid_len < 0) 45388c2ecf20Sopenharmony_ci return -1; 45398c2ecf20Sopenharmony_ci total_len += sid_len; 45408c2ecf20Sopenharmony_ci 45418c2ecf20Sopenharmony_ci /* check name len */ 45428c2ecf20Sopenharmony_ci if (beg + total_len + 4 > end) 45438c2ecf20Sopenharmony_ci return -1; 45448c2ecf20Sopenharmony_ci name_len = le32_to_cpu(*(__le32 *)(beg + total_len)); 45458c2ecf20Sopenharmony_ci if (name_len < 1 || name_len > 0xFFFF) 45468c2ecf20Sopenharmony_ci return -1; 45478c2ecf20Sopenharmony_ci total_len += 4; 45488c2ecf20Sopenharmony_ci 45498c2ecf20Sopenharmony_ci /* check name */ 45508c2ecf20Sopenharmony_ci name = beg + total_len; 45518c2ecf20Sopenharmony_ci if (name + name_len > end) 45528c2ecf20Sopenharmony_ci return -1; 45538c2ecf20Sopenharmony_ci total_len += name_len; 45548c2ecf20Sopenharmony_ci 45558c2ecf20Sopenharmony_ci if (out) { 45568c2ecf20Sopenharmony_ci out->base = beg; 45578c2ecf20Sopenharmony_ci out->size = total_len; 45588c2ecf20Sopenharmony_ci out->name_len = name_len; 45598c2ecf20Sopenharmony_ci out->name = name; 45608c2ecf20Sopenharmony_ci memcpy(&out->owner, owner_sid, 45618c2ecf20Sopenharmony_ci posix_info_sid_size(owner_sid, end)); 45628c2ecf20Sopenharmony_ci memcpy(&out->group, group_sid, 45638c2ecf20Sopenharmony_ci posix_info_sid_size(group_sid, end)); 45648c2ecf20Sopenharmony_ci } 45658c2ecf20Sopenharmony_ci return total_len; 45668c2ecf20Sopenharmony_ci} 45678c2ecf20Sopenharmony_ci 45688c2ecf20Sopenharmony_cistatic int posix_info_extra_size(const void *beg, const void *end) 45698c2ecf20Sopenharmony_ci{ 45708c2ecf20Sopenharmony_ci int len = posix_info_parse(beg, end, NULL); 45718c2ecf20Sopenharmony_ci 45728c2ecf20Sopenharmony_ci if (len < 0) 45738c2ecf20Sopenharmony_ci return -1; 45748c2ecf20Sopenharmony_ci return len - sizeof(struct smb2_posix_info); 45758c2ecf20Sopenharmony_ci} 45768c2ecf20Sopenharmony_ci 45778c2ecf20Sopenharmony_cistatic unsigned int 45788c2ecf20Sopenharmony_cinum_entries(int infotype, char *bufstart, char *end_of_buf, char **lastentry, 45798c2ecf20Sopenharmony_ci size_t size) 45808c2ecf20Sopenharmony_ci{ 45818c2ecf20Sopenharmony_ci int len; 45828c2ecf20Sopenharmony_ci unsigned int entrycount = 0; 45838c2ecf20Sopenharmony_ci unsigned int next_offset = 0; 45848c2ecf20Sopenharmony_ci char *entryptr; 45858c2ecf20Sopenharmony_ci FILE_DIRECTORY_INFO *dir_info; 45868c2ecf20Sopenharmony_ci 45878c2ecf20Sopenharmony_ci if (bufstart == NULL) 45888c2ecf20Sopenharmony_ci return 0; 45898c2ecf20Sopenharmony_ci 45908c2ecf20Sopenharmony_ci entryptr = bufstart; 45918c2ecf20Sopenharmony_ci 45928c2ecf20Sopenharmony_ci while (1) { 45938c2ecf20Sopenharmony_ci if (entryptr + next_offset < entryptr || 45948c2ecf20Sopenharmony_ci entryptr + next_offset > end_of_buf || 45958c2ecf20Sopenharmony_ci entryptr + next_offset + size > end_of_buf) { 45968c2ecf20Sopenharmony_ci cifs_dbg(VFS, "malformed search entry would overflow\n"); 45978c2ecf20Sopenharmony_ci break; 45988c2ecf20Sopenharmony_ci } 45998c2ecf20Sopenharmony_ci 46008c2ecf20Sopenharmony_ci entryptr = entryptr + next_offset; 46018c2ecf20Sopenharmony_ci dir_info = (FILE_DIRECTORY_INFO *)entryptr; 46028c2ecf20Sopenharmony_ci 46038c2ecf20Sopenharmony_ci if (infotype == SMB_FIND_FILE_POSIX_INFO) 46048c2ecf20Sopenharmony_ci len = posix_info_extra_size(entryptr, end_of_buf); 46058c2ecf20Sopenharmony_ci else 46068c2ecf20Sopenharmony_ci len = le32_to_cpu(dir_info->FileNameLength); 46078c2ecf20Sopenharmony_ci 46088c2ecf20Sopenharmony_ci if (len < 0 || 46098c2ecf20Sopenharmony_ci entryptr + len < entryptr || 46108c2ecf20Sopenharmony_ci entryptr + len > end_of_buf || 46118c2ecf20Sopenharmony_ci entryptr + len + size > end_of_buf) { 46128c2ecf20Sopenharmony_ci cifs_dbg(VFS, "directory entry name would overflow frame end of buf %p\n", 46138c2ecf20Sopenharmony_ci end_of_buf); 46148c2ecf20Sopenharmony_ci break; 46158c2ecf20Sopenharmony_ci } 46168c2ecf20Sopenharmony_ci 46178c2ecf20Sopenharmony_ci *lastentry = entryptr; 46188c2ecf20Sopenharmony_ci entrycount++; 46198c2ecf20Sopenharmony_ci 46208c2ecf20Sopenharmony_ci next_offset = le32_to_cpu(dir_info->NextEntryOffset); 46218c2ecf20Sopenharmony_ci if (!next_offset) 46228c2ecf20Sopenharmony_ci break; 46238c2ecf20Sopenharmony_ci } 46248c2ecf20Sopenharmony_ci 46258c2ecf20Sopenharmony_ci return entrycount; 46268c2ecf20Sopenharmony_ci} 46278c2ecf20Sopenharmony_ci 46288c2ecf20Sopenharmony_ci/* 46298c2ecf20Sopenharmony_ci * Readdir/FindFirst 46308c2ecf20Sopenharmony_ci */ 46318c2ecf20Sopenharmony_ciint SMB2_query_directory_init(const unsigned int xid, 46328c2ecf20Sopenharmony_ci struct cifs_tcon *tcon, 46338c2ecf20Sopenharmony_ci struct TCP_Server_Info *server, 46348c2ecf20Sopenharmony_ci struct smb_rqst *rqst, 46358c2ecf20Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, 46368c2ecf20Sopenharmony_ci int index, int info_level) 46378c2ecf20Sopenharmony_ci{ 46388c2ecf20Sopenharmony_ci struct smb2_query_directory_req *req; 46398c2ecf20Sopenharmony_ci unsigned char *bufptr; 46408c2ecf20Sopenharmony_ci __le16 asteriks = cpu_to_le16('*'); 46418c2ecf20Sopenharmony_ci unsigned int output_size = CIFSMaxBufSize - 46428c2ecf20Sopenharmony_ci MAX_SMB2_CREATE_RESPONSE_SIZE - 46438c2ecf20Sopenharmony_ci MAX_SMB2_CLOSE_RESPONSE_SIZE; 46448c2ecf20Sopenharmony_ci unsigned int total_len; 46458c2ecf20Sopenharmony_ci struct kvec *iov = rqst->rq_iov; 46468c2ecf20Sopenharmony_ci int len, rc; 46478c2ecf20Sopenharmony_ci 46488c2ecf20Sopenharmony_ci rc = smb2_plain_req_init(SMB2_QUERY_DIRECTORY, tcon, server, 46498c2ecf20Sopenharmony_ci (void **) &req, &total_len); 46508c2ecf20Sopenharmony_ci if (rc) 46518c2ecf20Sopenharmony_ci return rc; 46528c2ecf20Sopenharmony_ci 46538c2ecf20Sopenharmony_ci switch (info_level) { 46548c2ecf20Sopenharmony_ci case SMB_FIND_FILE_DIRECTORY_INFO: 46558c2ecf20Sopenharmony_ci req->FileInformationClass = FILE_DIRECTORY_INFORMATION; 46568c2ecf20Sopenharmony_ci break; 46578c2ecf20Sopenharmony_ci case SMB_FIND_FILE_ID_FULL_DIR_INFO: 46588c2ecf20Sopenharmony_ci req->FileInformationClass = FILEID_FULL_DIRECTORY_INFORMATION; 46598c2ecf20Sopenharmony_ci break; 46608c2ecf20Sopenharmony_ci case SMB_FIND_FILE_POSIX_INFO: 46618c2ecf20Sopenharmony_ci req->FileInformationClass = SMB_FIND_FILE_POSIX_INFO; 46628c2ecf20Sopenharmony_ci break; 46638c2ecf20Sopenharmony_ci default: 46648c2ecf20Sopenharmony_ci cifs_tcon_dbg(VFS, "info level %u isn't supported\n", 46658c2ecf20Sopenharmony_ci info_level); 46668c2ecf20Sopenharmony_ci return -EINVAL; 46678c2ecf20Sopenharmony_ci } 46688c2ecf20Sopenharmony_ci 46698c2ecf20Sopenharmony_ci req->FileIndex = cpu_to_le32(index); 46708c2ecf20Sopenharmony_ci req->PersistentFileId = persistent_fid; 46718c2ecf20Sopenharmony_ci req->VolatileFileId = volatile_fid; 46728c2ecf20Sopenharmony_ci 46738c2ecf20Sopenharmony_ci len = 0x2; 46748c2ecf20Sopenharmony_ci bufptr = req->Buffer; 46758c2ecf20Sopenharmony_ci memcpy(bufptr, &asteriks, len); 46768c2ecf20Sopenharmony_ci 46778c2ecf20Sopenharmony_ci req->FileNameOffset = 46788c2ecf20Sopenharmony_ci cpu_to_le16(sizeof(struct smb2_query_directory_req)); 46798c2ecf20Sopenharmony_ci req->FileNameLength = cpu_to_le16(len); 46808c2ecf20Sopenharmony_ci /* 46818c2ecf20Sopenharmony_ci * BB could be 30 bytes or so longer if we used SMB2 specific 46828c2ecf20Sopenharmony_ci * buffer lengths, but this is safe and close enough. 46838c2ecf20Sopenharmony_ci */ 46848c2ecf20Sopenharmony_ci output_size = min_t(unsigned int, output_size, server->maxBuf); 46858c2ecf20Sopenharmony_ci output_size = min_t(unsigned int, output_size, 2 << 15); 46868c2ecf20Sopenharmony_ci req->OutputBufferLength = cpu_to_le32(output_size); 46878c2ecf20Sopenharmony_ci 46888c2ecf20Sopenharmony_ci iov[0].iov_base = (char *)req; 46898c2ecf20Sopenharmony_ci /* 1 for Buffer */ 46908c2ecf20Sopenharmony_ci iov[0].iov_len = total_len - 1; 46918c2ecf20Sopenharmony_ci 46928c2ecf20Sopenharmony_ci iov[1].iov_base = (char *)(req->Buffer); 46938c2ecf20Sopenharmony_ci iov[1].iov_len = len; 46948c2ecf20Sopenharmony_ci 46958c2ecf20Sopenharmony_ci trace_smb3_query_dir_enter(xid, persistent_fid, tcon->tid, 46968c2ecf20Sopenharmony_ci tcon->ses->Suid, index, output_size); 46978c2ecf20Sopenharmony_ci 46988c2ecf20Sopenharmony_ci return 0; 46998c2ecf20Sopenharmony_ci} 47008c2ecf20Sopenharmony_ci 47018c2ecf20Sopenharmony_civoid SMB2_query_directory_free(struct smb_rqst *rqst) 47028c2ecf20Sopenharmony_ci{ 47038c2ecf20Sopenharmony_ci if (rqst && rqst->rq_iov) { 47048c2ecf20Sopenharmony_ci cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ 47058c2ecf20Sopenharmony_ci } 47068c2ecf20Sopenharmony_ci} 47078c2ecf20Sopenharmony_ci 47088c2ecf20Sopenharmony_ciint 47098c2ecf20Sopenharmony_cismb2_parse_query_directory(struct cifs_tcon *tcon, 47108c2ecf20Sopenharmony_ci struct kvec *rsp_iov, 47118c2ecf20Sopenharmony_ci int resp_buftype, 47128c2ecf20Sopenharmony_ci struct cifs_search_info *srch_inf) 47138c2ecf20Sopenharmony_ci{ 47148c2ecf20Sopenharmony_ci struct smb2_query_directory_rsp *rsp; 47158c2ecf20Sopenharmony_ci size_t info_buf_size; 47168c2ecf20Sopenharmony_ci char *end_of_smb; 47178c2ecf20Sopenharmony_ci int rc; 47188c2ecf20Sopenharmony_ci 47198c2ecf20Sopenharmony_ci rsp = (struct smb2_query_directory_rsp *)rsp_iov->iov_base; 47208c2ecf20Sopenharmony_ci 47218c2ecf20Sopenharmony_ci switch (srch_inf->info_level) { 47228c2ecf20Sopenharmony_ci case SMB_FIND_FILE_DIRECTORY_INFO: 47238c2ecf20Sopenharmony_ci info_buf_size = sizeof(FILE_DIRECTORY_INFO) - 1; 47248c2ecf20Sopenharmony_ci break; 47258c2ecf20Sopenharmony_ci case SMB_FIND_FILE_ID_FULL_DIR_INFO: 47268c2ecf20Sopenharmony_ci info_buf_size = sizeof(SEARCH_ID_FULL_DIR_INFO) - 1; 47278c2ecf20Sopenharmony_ci break; 47288c2ecf20Sopenharmony_ci case SMB_FIND_FILE_POSIX_INFO: 47298c2ecf20Sopenharmony_ci /* note that posix payload are variable size */ 47308c2ecf20Sopenharmony_ci info_buf_size = sizeof(struct smb2_posix_info); 47318c2ecf20Sopenharmony_ci break; 47328c2ecf20Sopenharmony_ci default: 47338c2ecf20Sopenharmony_ci cifs_tcon_dbg(VFS, "info level %u isn't supported\n", 47348c2ecf20Sopenharmony_ci srch_inf->info_level); 47358c2ecf20Sopenharmony_ci return -EINVAL; 47368c2ecf20Sopenharmony_ci } 47378c2ecf20Sopenharmony_ci 47388c2ecf20Sopenharmony_ci rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), 47398c2ecf20Sopenharmony_ci le32_to_cpu(rsp->OutputBufferLength), rsp_iov, 47408c2ecf20Sopenharmony_ci info_buf_size); 47418c2ecf20Sopenharmony_ci if (rc) { 47428c2ecf20Sopenharmony_ci cifs_tcon_dbg(VFS, "bad info payload"); 47438c2ecf20Sopenharmony_ci return rc; 47448c2ecf20Sopenharmony_ci } 47458c2ecf20Sopenharmony_ci 47468c2ecf20Sopenharmony_ci srch_inf->unicode = true; 47478c2ecf20Sopenharmony_ci 47488c2ecf20Sopenharmony_ci if (srch_inf->ntwrk_buf_start) { 47498c2ecf20Sopenharmony_ci if (srch_inf->smallBuf) 47508c2ecf20Sopenharmony_ci cifs_small_buf_release(srch_inf->ntwrk_buf_start); 47518c2ecf20Sopenharmony_ci else 47528c2ecf20Sopenharmony_ci cifs_buf_release(srch_inf->ntwrk_buf_start); 47538c2ecf20Sopenharmony_ci } 47548c2ecf20Sopenharmony_ci srch_inf->ntwrk_buf_start = (char *)rsp; 47558c2ecf20Sopenharmony_ci srch_inf->srch_entries_start = srch_inf->last_entry = 47568c2ecf20Sopenharmony_ci (char *)rsp + le16_to_cpu(rsp->OutputBufferOffset); 47578c2ecf20Sopenharmony_ci end_of_smb = rsp_iov->iov_len + (char *)rsp; 47588c2ecf20Sopenharmony_ci 47598c2ecf20Sopenharmony_ci srch_inf->entries_in_buffer = num_entries( 47608c2ecf20Sopenharmony_ci srch_inf->info_level, 47618c2ecf20Sopenharmony_ci srch_inf->srch_entries_start, 47628c2ecf20Sopenharmony_ci end_of_smb, 47638c2ecf20Sopenharmony_ci &srch_inf->last_entry, 47648c2ecf20Sopenharmony_ci info_buf_size); 47658c2ecf20Sopenharmony_ci 47668c2ecf20Sopenharmony_ci srch_inf->index_of_last_entry += srch_inf->entries_in_buffer; 47678c2ecf20Sopenharmony_ci cifs_dbg(FYI, "num entries %d last_index %lld srch start %p srch end %p\n", 47688c2ecf20Sopenharmony_ci srch_inf->entries_in_buffer, srch_inf->index_of_last_entry, 47698c2ecf20Sopenharmony_ci srch_inf->srch_entries_start, srch_inf->last_entry); 47708c2ecf20Sopenharmony_ci if (resp_buftype == CIFS_LARGE_BUFFER) 47718c2ecf20Sopenharmony_ci srch_inf->smallBuf = false; 47728c2ecf20Sopenharmony_ci else if (resp_buftype == CIFS_SMALL_BUFFER) 47738c2ecf20Sopenharmony_ci srch_inf->smallBuf = true; 47748c2ecf20Sopenharmony_ci else 47758c2ecf20Sopenharmony_ci cifs_tcon_dbg(VFS, "Invalid search buffer type\n"); 47768c2ecf20Sopenharmony_ci 47778c2ecf20Sopenharmony_ci return 0; 47788c2ecf20Sopenharmony_ci} 47798c2ecf20Sopenharmony_ci 47808c2ecf20Sopenharmony_ciint 47818c2ecf20Sopenharmony_ciSMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, 47828c2ecf20Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, int index, 47838c2ecf20Sopenharmony_ci struct cifs_search_info *srch_inf) 47848c2ecf20Sopenharmony_ci{ 47858c2ecf20Sopenharmony_ci struct smb_rqst rqst; 47868c2ecf20Sopenharmony_ci struct kvec iov[SMB2_QUERY_DIRECTORY_IOV_SIZE]; 47878c2ecf20Sopenharmony_ci struct smb2_query_directory_rsp *rsp = NULL; 47888c2ecf20Sopenharmony_ci int resp_buftype = CIFS_NO_BUFFER; 47898c2ecf20Sopenharmony_ci struct kvec rsp_iov; 47908c2ecf20Sopenharmony_ci int rc = 0; 47918c2ecf20Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 47928c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(ses); 47938c2ecf20Sopenharmony_ci int flags = 0; 47948c2ecf20Sopenharmony_ci 47958c2ecf20Sopenharmony_ci if (!ses || !(ses->server)) 47968c2ecf20Sopenharmony_ci return -EIO; 47978c2ecf20Sopenharmony_ci 47988c2ecf20Sopenharmony_ci if (smb3_encryption_required(tcon)) 47998c2ecf20Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 48008c2ecf20Sopenharmony_ci 48018c2ecf20Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 48028c2ecf20Sopenharmony_ci memset(&iov, 0, sizeof(iov)); 48038c2ecf20Sopenharmony_ci rqst.rq_iov = iov; 48048c2ecf20Sopenharmony_ci rqst.rq_nvec = SMB2_QUERY_DIRECTORY_IOV_SIZE; 48058c2ecf20Sopenharmony_ci 48068c2ecf20Sopenharmony_ci rc = SMB2_query_directory_init(xid, tcon, server, 48078c2ecf20Sopenharmony_ci &rqst, persistent_fid, 48088c2ecf20Sopenharmony_ci volatile_fid, index, 48098c2ecf20Sopenharmony_ci srch_inf->info_level); 48108c2ecf20Sopenharmony_ci if (rc) 48118c2ecf20Sopenharmony_ci goto qdir_exit; 48128c2ecf20Sopenharmony_ci 48138c2ecf20Sopenharmony_ci rc = cifs_send_recv(xid, ses, server, 48148c2ecf20Sopenharmony_ci &rqst, &resp_buftype, flags, &rsp_iov); 48158c2ecf20Sopenharmony_ci rsp = (struct smb2_query_directory_rsp *)rsp_iov.iov_base; 48168c2ecf20Sopenharmony_ci 48178c2ecf20Sopenharmony_ci if (rc) { 48188c2ecf20Sopenharmony_ci if (rc == -ENODATA && 48198c2ecf20Sopenharmony_ci rsp->sync_hdr.Status == STATUS_NO_MORE_FILES) { 48208c2ecf20Sopenharmony_ci trace_smb3_query_dir_done(xid, persistent_fid, 48218c2ecf20Sopenharmony_ci tcon->tid, tcon->ses->Suid, index, 0); 48228c2ecf20Sopenharmony_ci srch_inf->endOfSearch = true; 48238c2ecf20Sopenharmony_ci rc = 0; 48248c2ecf20Sopenharmony_ci } else { 48258c2ecf20Sopenharmony_ci trace_smb3_query_dir_err(xid, persistent_fid, tcon->tid, 48268c2ecf20Sopenharmony_ci tcon->ses->Suid, index, 0, rc); 48278c2ecf20Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_QUERY_DIRECTORY_HE); 48288c2ecf20Sopenharmony_ci } 48298c2ecf20Sopenharmony_ci goto qdir_exit; 48308c2ecf20Sopenharmony_ci } 48318c2ecf20Sopenharmony_ci 48328c2ecf20Sopenharmony_ci rc = smb2_parse_query_directory(tcon, &rsp_iov, resp_buftype, 48338c2ecf20Sopenharmony_ci srch_inf); 48348c2ecf20Sopenharmony_ci if (rc) { 48358c2ecf20Sopenharmony_ci trace_smb3_query_dir_err(xid, persistent_fid, tcon->tid, 48368c2ecf20Sopenharmony_ci tcon->ses->Suid, index, 0, rc); 48378c2ecf20Sopenharmony_ci goto qdir_exit; 48388c2ecf20Sopenharmony_ci } 48398c2ecf20Sopenharmony_ci resp_buftype = CIFS_NO_BUFFER; 48408c2ecf20Sopenharmony_ci 48418c2ecf20Sopenharmony_ci trace_smb3_query_dir_done(xid, persistent_fid, tcon->tid, 48428c2ecf20Sopenharmony_ci tcon->ses->Suid, index, srch_inf->entries_in_buffer); 48438c2ecf20Sopenharmony_ci 48448c2ecf20Sopenharmony_ciqdir_exit: 48458c2ecf20Sopenharmony_ci SMB2_query_directory_free(&rqst); 48468c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype, rsp); 48478c2ecf20Sopenharmony_ci return rc; 48488c2ecf20Sopenharmony_ci} 48498c2ecf20Sopenharmony_ci 48508c2ecf20Sopenharmony_ciint 48518c2ecf20Sopenharmony_ciSMB2_set_info_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, 48528c2ecf20Sopenharmony_ci struct smb_rqst *rqst, 48538c2ecf20Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, u32 pid, 48548c2ecf20Sopenharmony_ci u8 info_class, u8 info_type, u32 additional_info, 48558c2ecf20Sopenharmony_ci void **data, unsigned int *size) 48568c2ecf20Sopenharmony_ci{ 48578c2ecf20Sopenharmony_ci struct smb2_set_info_req *req; 48588c2ecf20Sopenharmony_ci struct kvec *iov = rqst->rq_iov; 48598c2ecf20Sopenharmony_ci unsigned int i, total_len; 48608c2ecf20Sopenharmony_ci int rc; 48618c2ecf20Sopenharmony_ci 48628c2ecf20Sopenharmony_ci rc = smb2_plain_req_init(SMB2_SET_INFO, tcon, server, 48638c2ecf20Sopenharmony_ci (void **) &req, &total_len); 48648c2ecf20Sopenharmony_ci if (rc) 48658c2ecf20Sopenharmony_ci return rc; 48668c2ecf20Sopenharmony_ci 48678c2ecf20Sopenharmony_ci req->sync_hdr.ProcessId = cpu_to_le32(pid); 48688c2ecf20Sopenharmony_ci req->InfoType = info_type; 48698c2ecf20Sopenharmony_ci req->FileInfoClass = info_class; 48708c2ecf20Sopenharmony_ci req->PersistentFileId = persistent_fid; 48718c2ecf20Sopenharmony_ci req->VolatileFileId = volatile_fid; 48728c2ecf20Sopenharmony_ci req->AdditionalInformation = cpu_to_le32(additional_info); 48738c2ecf20Sopenharmony_ci 48748c2ecf20Sopenharmony_ci req->BufferOffset = 48758c2ecf20Sopenharmony_ci cpu_to_le16(sizeof(struct smb2_set_info_req)); 48768c2ecf20Sopenharmony_ci req->BufferLength = cpu_to_le32(*size); 48778c2ecf20Sopenharmony_ci 48788c2ecf20Sopenharmony_ci memcpy(req->Buffer, *data, *size); 48798c2ecf20Sopenharmony_ci total_len += *size; 48808c2ecf20Sopenharmony_ci 48818c2ecf20Sopenharmony_ci iov[0].iov_base = (char *)req; 48828c2ecf20Sopenharmony_ci /* 1 for Buffer */ 48838c2ecf20Sopenharmony_ci iov[0].iov_len = total_len - 1; 48848c2ecf20Sopenharmony_ci 48858c2ecf20Sopenharmony_ci for (i = 1; i < rqst->rq_nvec; i++) { 48868c2ecf20Sopenharmony_ci le32_add_cpu(&req->BufferLength, size[i]); 48878c2ecf20Sopenharmony_ci iov[i].iov_base = (char *)data[i]; 48888c2ecf20Sopenharmony_ci iov[i].iov_len = size[i]; 48898c2ecf20Sopenharmony_ci } 48908c2ecf20Sopenharmony_ci 48918c2ecf20Sopenharmony_ci return 0; 48928c2ecf20Sopenharmony_ci} 48938c2ecf20Sopenharmony_ci 48948c2ecf20Sopenharmony_civoid 48958c2ecf20Sopenharmony_ciSMB2_set_info_free(struct smb_rqst *rqst) 48968c2ecf20Sopenharmony_ci{ 48978c2ecf20Sopenharmony_ci if (rqst && rqst->rq_iov) 48988c2ecf20Sopenharmony_ci cifs_buf_release(rqst->rq_iov[0].iov_base); /* request */ 48998c2ecf20Sopenharmony_ci} 49008c2ecf20Sopenharmony_ci 49018c2ecf20Sopenharmony_cistatic int 49028c2ecf20Sopenharmony_cisend_set_info(const unsigned int xid, struct cifs_tcon *tcon, 49038c2ecf20Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, u32 pid, u8 info_class, 49048c2ecf20Sopenharmony_ci u8 info_type, u32 additional_info, unsigned int num, 49058c2ecf20Sopenharmony_ci void **data, unsigned int *size) 49068c2ecf20Sopenharmony_ci{ 49078c2ecf20Sopenharmony_ci struct smb_rqst rqst; 49088c2ecf20Sopenharmony_ci struct smb2_set_info_rsp *rsp = NULL; 49098c2ecf20Sopenharmony_ci struct kvec *iov; 49108c2ecf20Sopenharmony_ci struct kvec rsp_iov; 49118c2ecf20Sopenharmony_ci int rc = 0; 49128c2ecf20Sopenharmony_ci int resp_buftype; 49138c2ecf20Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 49148c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(ses); 49158c2ecf20Sopenharmony_ci int flags = 0; 49168c2ecf20Sopenharmony_ci 49178c2ecf20Sopenharmony_ci if (!ses || !server) 49188c2ecf20Sopenharmony_ci return -EIO; 49198c2ecf20Sopenharmony_ci 49208c2ecf20Sopenharmony_ci if (!num) 49218c2ecf20Sopenharmony_ci return -EINVAL; 49228c2ecf20Sopenharmony_ci 49238c2ecf20Sopenharmony_ci if (smb3_encryption_required(tcon)) 49248c2ecf20Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 49258c2ecf20Sopenharmony_ci 49268c2ecf20Sopenharmony_ci iov = kmalloc_array(num, sizeof(struct kvec), GFP_KERNEL); 49278c2ecf20Sopenharmony_ci if (!iov) 49288c2ecf20Sopenharmony_ci return -ENOMEM; 49298c2ecf20Sopenharmony_ci 49308c2ecf20Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 49318c2ecf20Sopenharmony_ci rqst.rq_iov = iov; 49328c2ecf20Sopenharmony_ci rqst.rq_nvec = num; 49338c2ecf20Sopenharmony_ci 49348c2ecf20Sopenharmony_ci rc = SMB2_set_info_init(tcon, server, 49358c2ecf20Sopenharmony_ci &rqst, persistent_fid, volatile_fid, pid, 49368c2ecf20Sopenharmony_ci info_class, info_type, additional_info, 49378c2ecf20Sopenharmony_ci data, size); 49388c2ecf20Sopenharmony_ci if (rc) { 49398c2ecf20Sopenharmony_ci kfree(iov); 49408c2ecf20Sopenharmony_ci return rc; 49418c2ecf20Sopenharmony_ci } 49428c2ecf20Sopenharmony_ci 49438c2ecf20Sopenharmony_ci 49448c2ecf20Sopenharmony_ci rc = cifs_send_recv(xid, ses, server, 49458c2ecf20Sopenharmony_ci &rqst, &resp_buftype, flags, 49468c2ecf20Sopenharmony_ci &rsp_iov); 49478c2ecf20Sopenharmony_ci SMB2_set_info_free(&rqst); 49488c2ecf20Sopenharmony_ci rsp = (struct smb2_set_info_rsp *)rsp_iov.iov_base; 49498c2ecf20Sopenharmony_ci 49508c2ecf20Sopenharmony_ci if (rc != 0) { 49518c2ecf20Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_SET_INFO_HE); 49528c2ecf20Sopenharmony_ci trace_smb3_set_info_err(xid, persistent_fid, tcon->tid, 49538c2ecf20Sopenharmony_ci ses->Suid, info_class, (__u32)info_type, rc); 49548c2ecf20Sopenharmony_ci } 49558c2ecf20Sopenharmony_ci 49568c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype, rsp); 49578c2ecf20Sopenharmony_ci kfree(iov); 49588c2ecf20Sopenharmony_ci return rc; 49598c2ecf20Sopenharmony_ci} 49608c2ecf20Sopenharmony_ci 49618c2ecf20Sopenharmony_ciint 49628c2ecf20Sopenharmony_ciSMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, 49638c2ecf20Sopenharmony_ci u64 volatile_fid, u32 pid, __le64 *eof) 49648c2ecf20Sopenharmony_ci{ 49658c2ecf20Sopenharmony_ci struct smb2_file_eof_info info; 49668c2ecf20Sopenharmony_ci void *data; 49678c2ecf20Sopenharmony_ci unsigned int size; 49688c2ecf20Sopenharmony_ci 49698c2ecf20Sopenharmony_ci info.EndOfFile = *eof; 49708c2ecf20Sopenharmony_ci 49718c2ecf20Sopenharmony_ci data = &info; 49728c2ecf20Sopenharmony_ci size = sizeof(struct smb2_file_eof_info); 49738c2ecf20Sopenharmony_ci 49748c2ecf20Sopenharmony_ci return send_set_info(xid, tcon, persistent_fid, volatile_fid, 49758c2ecf20Sopenharmony_ci pid, FILE_END_OF_FILE_INFORMATION, SMB2_O_INFO_FILE, 49768c2ecf20Sopenharmony_ci 0, 1, &data, &size); 49778c2ecf20Sopenharmony_ci} 49788c2ecf20Sopenharmony_ci 49798c2ecf20Sopenharmony_ciint 49808c2ecf20Sopenharmony_ciSMB2_set_acl(const unsigned int xid, struct cifs_tcon *tcon, 49818c2ecf20Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, 49828c2ecf20Sopenharmony_ci struct cifs_ntsd *pnntsd, int pacllen, int aclflag) 49838c2ecf20Sopenharmony_ci{ 49848c2ecf20Sopenharmony_ci return send_set_info(xid, tcon, persistent_fid, volatile_fid, 49858c2ecf20Sopenharmony_ci current->tgid, 0, SMB2_O_INFO_SECURITY, aclflag, 49868c2ecf20Sopenharmony_ci 1, (void **)&pnntsd, &pacllen); 49878c2ecf20Sopenharmony_ci} 49888c2ecf20Sopenharmony_ci 49898c2ecf20Sopenharmony_ciint 49908c2ecf20Sopenharmony_ciSMB2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, 49918c2ecf20Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, 49928c2ecf20Sopenharmony_ci struct smb2_file_full_ea_info *buf, int len) 49938c2ecf20Sopenharmony_ci{ 49948c2ecf20Sopenharmony_ci return send_set_info(xid, tcon, persistent_fid, volatile_fid, 49958c2ecf20Sopenharmony_ci current->tgid, FILE_FULL_EA_INFORMATION, SMB2_O_INFO_FILE, 49968c2ecf20Sopenharmony_ci 0, 1, (void **)&buf, &len); 49978c2ecf20Sopenharmony_ci} 49988c2ecf20Sopenharmony_ci 49998c2ecf20Sopenharmony_ciint 50008c2ecf20Sopenharmony_ciSMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon, 50018c2ecf20Sopenharmony_ci const u64 persistent_fid, const u64 volatile_fid, 50028c2ecf20Sopenharmony_ci __u8 oplock_level) 50038c2ecf20Sopenharmony_ci{ 50048c2ecf20Sopenharmony_ci struct smb_rqst rqst; 50058c2ecf20Sopenharmony_ci int rc; 50068c2ecf20Sopenharmony_ci struct smb2_oplock_break *req = NULL; 50078c2ecf20Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 50088c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(ses); 50098c2ecf20Sopenharmony_ci int flags = CIFS_OBREAK_OP; 50108c2ecf20Sopenharmony_ci unsigned int total_len; 50118c2ecf20Sopenharmony_ci struct kvec iov[1]; 50128c2ecf20Sopenharmony_ci struct kvec rsp_iov; 50138c2ecf20Sopenharmony_ci int resp_buf_type; 50148c2ecf20Sopenharmony_ci 50158c2ecf20Sopenharmony_ci cifs_dbg(FYI, "SMB2_oplock_break\n"); 50168c2ecf20Sopenharmony_ci rc = smb2_plain_req_init(SMB2_OPLOCK_BREAK, tcon, server, 50178c2ecf20Sopenharmony_ci (void **) &req, &total_len); 50188c2ecf20Sopenharmony_ci if (rc) 50198c2ecf20Sopenharmony_ci return rc; 50208c2ecf20Sopenharmony_ci 50218c2ecf20Sopenharmony_ci if (smb3_encryption_required(tcon)) 50228c2ecf20Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 50238c2ecf20Sopenharmony_ci 50248c2ecf20Sopenharmony_ci req->VolatileFid = volatile_fid; 50258c2ecf20Sopenharmony_ci req->PersistentFid = persistent_fid; 50268c2ecf20Sopenharmony_ci req->OplockLevel = oplock_level; 50278c2ecf20Sopenharmony_ci req->sync_hdr.CreditRequest = cpu_to_le16(1); 50288c2ecf20Sopenharmony_ci 50298c2ecf20Sopenharmony_ci flags |= CIFS_NO_RSP_BUF; 50308c2ecf20Sopenharmony_ci 50318c2ecf20Sopenharmony_ci iov[0].iov_base = (char *)req; 50328c2ecf20Sopenharmony_ci iov[0].iov_len = total_len; 50338c2ecf20Sopenharmony_ci 50348c2ecf20Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 50358c2ecf20Sopenharmony_ci rqst.rq_iov = iov; 50368c2ecf20Sopenharmony_ci rqst.rq_nvec = 1; 50378c2ecf20Sopenharmony_ci 50388c2ecf20Sopenharmony_ci rc = cifs_send_recv(xid, ses, server, 50398c2ecf20Sopenharmony_ci &rqst, &resp_buf_type, flags, &rsp_iov); 50408c2ecf20Sopenharmony_ci cifs_small_buf_release(req); 50418c2ecf20Sopenharmony_ci 50428c2ecf20Sopenharmony_ci if (rc) { 50438c2ecf20Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE); 50448c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Send error in Oplock Break = %d\n", rc); 50458c2ecf20Sopenharmony_ci } 50468c2ecf20Sopenharmony_ci 50478c2ecf20Sopenharmony_ci return rc; 50488c2ecf20Sopenharmony_ci} 50498c2ecf20Sopenharmony_ci 50508c2ecf20Sopenharmony_civoid 50518c2ecf20Sopenharmony_cismb2_copy_fs_info_to_kstatfs(struct smb2_fs_full_size_info *pfs_inf, 50528c2ecf20Sopenharmony_ci struct kstatfs *kst) 50538c2ecf20Sopenharmony_ci{ 50548c2ecf20Sopenharmony_ci kst->f_bsize = le32_to_cpu(pfs_inf->BytesPerSector) * 50558c2ecf20Sopenharmony_ci le32_to_cpu(pfs_inf->SectorsPerAllocationUnit); 50568c2ecf20Sopenharmony_ci kst->f_blocks = le64_to_cpu(pfs_inf->TotalAllocationUnits); 50578c2ecf20Sopenharmony_ci kst->f_bfree = kst->f_bavail = 50588c2ecf20Sopenharmony_ci le64_to_cpu(pfs_inf->CallerAvailableAllocationUnits); 50598c2ecf20Sopenharmony_ci return; 50608c2ecf20Sopenharmony_ci} 50618c2ecf20Sopenharmony_ci 50628c2ecf20Sopenharmony_cistatic void 50638c2ecf20Sopenharmony_cicopy_posix_fs_info_to_kstatfs(FILE_SYSTEM_POSIX_INFO *response_data, 50648c2ecf20Sopenharmony_ci struct kstatfs *kst) 50658c2ecf20Sopenharmony_ci{ 50668c2ecf20Sopenharmony_ci kst->f_bsize = le32_to_cpu(response_data->BlockSize); 50678c2ecf20Sopenharmony_ci kst->f_blocks = le64_to_cpu(response_data->TotalBlocks); 50688c2ecf20Sopenharmony_ci kst->f_bfree = le64_to_cpu(response_data->BlocksAvail); 50698c2ecf20Sopenharmony_ci if (response_data->UserBlocksAvail == cpu_to_le64(-1)) 50708c2ecf20Sopenharmony_ci kst->f_bavail = kst->f_bfree; 50718c2ecf20Sopenharmony_ci else 50728c2ecf20Sopenharmony_ci kst->f_bavail = le64_to_cpu(response_data->UserBlocksAvail); 50738c2ecf20Sopenharmony_ci if (response_data->TotalFileNodes != cpu_to_le64(-1)) 50748c2ecf20Sopenharmony_ci kst->f_files = le64_to_cpu(response_data->TotalFileNodes); 50758c2ecf20Sopenharmony_ci if (response_data->FreeFileNodes != cpu_to_le64(-1)) 50768c2ecf20Sopenharmony_ci kst->f_ffree = le64_to_cpu(response_data->FreeFileNodes); 50778c2ecf20Sopenharmony_ci 50788c2ecf20Sopenharmony_ci return; 50798c2ecf20Sopenharmony_ci} 50808c2ecf20Sopenharmony_ci 50818c2ecf20Sopenharmony_cistatic int 50828c2ecf20Sopenharmony_cibuild_qfs_info_req(struct kvec *iov, struct cifs_tcon *tcon, 50838c2ecf20Sopenharmony_ci struct TCP_Server_Info *server, 50848c2ecf20Sopenharmony_ci int level, int outbuf_len, u64 persistent_fid, 50858c2ecf20Sopenharmony_ci u64 volatile_fid) 50868c2ecf20Sopenharmony_ci{ 50878c2ecf20Sopenharmony_ci int rc; 50888c2ecf20Sopenharmony_ci struct smb2_query_info_req *req; 50898c2ecf20Sopenharmony_ci unsigned int total_len; 50908c2ecf20Sopenharmony_ci 50918c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Query FSInfo level %d\n", level); 50928c2ecf20Sopenharmony_ci 50938c2ecf20Sopenharmony_ci if ((tcon->ses == NULL) || server == NULL) 50948c2ecf20Sopenharmony_ci return -EIO; 50958c2ecf20Sopenharmony_ci 50968c2ecf20Sopenharmony_ci rc = smb2_plain_req_init(SMB2_QUERY_INFO, tcon, server, 50978c2ecf20Sopenharmony_ci (void **) &req, &total_len); 50988c2ecf20Sopenharmony_ci if (rc) 50998c2ecf20Sopenharmony_ci return rc; 51008c2ecf20Sopenharmony_ci 51018c2ecf20Sopenharmony_ci req->InfoType = SMB2_O_INFO_FILESYSTEM; 51028c2ecf20Sopenharmony_ci req->FileInfoClass = level; 51038c2ecf20Sopenharmony_ci req->PersistentFileId = persistent_fid; 51048c2ecf20Sopenharmony_ci req->VolatileFileId = volatile_fid; 51058c2ecf20Sopenharmony_ci /* 1 for pad */ 51068c2ecf20Sopenharmony_ci req->InputBufferOffset = 51078c2ecf20Sopenharmony_ci cpu_to_le16(sizeof(struct smb2_query_info_req)); 51088c2ecf20Sopenharmony_ci req->OutputBufferLength = cpu_to_le32( 51098c2ecf20Sopenharmony_ci outbuf_len + sizeof(struct smb2_query_info_rsp)); 51108c2ecf20Sopenharmony_ci 51118c2ecf20Sopenharmony_ci iov->iov_base = (char *)req; 51128c2ecf20Sopenharmony_ci iov->iov_len = total_len; 51138c2ecf20Sopenharmony_ci return 0; 51148c2ecf20Sopenharmony_ci} 51158c2ecf20Sopenharmony_ci 51168c2ecf20Sopenharmony_cistatic inline void free_qfs_info_req(struct kvec *iov) 51178c2ecf20Sopenharmony_ci{ 51188c2ecf20Sopenharmony_ci cifs_buf_release(iov->iov_base); 51198c2ecf20Sopenharmony_ci} 51208c2ecf20Sopenharmony_ci 51218c2ecf20Sopenharmony_ciint 51228c2ecf20Sopenharmony_ciSMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon, 51238c2ecf20Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata) 51248c2ecf20Sopenharmony_ci{ 51258c2ecf20Sopenharmony_ci struct smb_rqst rqst; 51268c2ecf20Sopenharmony_ci struct smb2_query_info_rsp *rsp = NULL; 51278c2ecf20Sopenharmony_ci struct kvec iov; 51288c2ecf20Sopenharmony_ci struct kvec rsp_iov; 51298c2ecf20Sopenharmony_ci int rc = 0; 51308c2ecf20Sopenharmony_ci int resp_buftype; 51318c2ecf20Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 51328c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(ses); 51338c2ecf20Sopenharmony_ci FILE_SYSTEM_POSIX_INFO *info = NULL; 51348c2ecf20Sopenharmony_ci int flags = 0; 51358c2ecf20Sopenharmony_ci 51368c2ecf20Sopenharmony_ci rc = build_qfs_info_req(&iov, tcon, server, 51378c2ecf20Sopenharmony_ci FS_POSIX_INFORMATION, 51388c2ecf20Sopenharmony_ci sizeof(FILE_SYSTEM_POSIX_INFO), 51398c2ecf20Sopenharmony_ci persistent_fid, volatile_fid); 51408c2ecf20Sopenharmony_ci if (rc) 51418c2ecf20Sopenharmony_ci return rc; 51428c2ecf20Sopenharmony_ci 51438c2ecf20Sopenharmony_ci if (smb3_encryption_required(tcon)) 51448c2ecf20Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 51458c2ecf20Sopenharmony_ci 51468c2ecf20Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 51478c2ecf20Sopenharmony_ci rqst.rq_iov = &iov; 51488c2ecf20Sopenharmony_ci rqst.rq_nvec = 1; 51498c2ecf20Sopenharmony_ci 51508c2ecf20Sopenharmony_ci rc = cifs_send_recv(xid, ses, server, 51518c2ecf20Sopenharmony_ci &rqst, &resp_buftype, flags, &rsp_iov); 51528c2ecf20Sopenharmony_ci free_qfs_info_req(&iov); 51538c2ecf20Sopenharmony_ci if (rc) { 51548c2ecf20Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); 51558c2ecf20Sopenharmony_ci goto posix_qfsinf_exit; 51568c2ecf20Sopenharmony_ci } 51578c2ecf20Sopenharmony_ci rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; 51588c2ecf20Sopenharmony_ci 51598c2ecf20Sopenharmony_ci info = (FILE_SYSTEM_POSIX_INFO *)( 51608c2ecf20Sopenharmony_ci le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp); 51618c2ecf20Sopenharmony_ci rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), 51628c2ecf20Sopenharmony_ci le32_to_cpu(rsp->OutputBufferLength), &rsp_iov, 51638c2ecf20Sopenharmony_ci sizeof(FILE_SYSTEM_POSIX_INFO)); 51648c2ecf20Sopenharmony_ci if (!rc) 51658c2ecf20Sopenharmony_ci copy_posix_fs_info_to_kstatfs(info, fsdata); 51668c2ecf20Sopenharmony_ci 51678c2ecf20Sopenharmony_ciposix_qfsinf_exit: 51688c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype, rsp_iov.iov_base); 51698c2ecf20Sopenharmony_ci return rc; 51708c2ecf20Sopenharmony_ci} 51718c2ecf20Sopenharmony_ci 51728c2ecf20Sopenharmony_ciint 51738c2ecf20Sopenharmony_ciSMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, 51748c2ecf20Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata) 51758c2ecf20Sopenharmony_ci{ 51768c2ecf20Sopenharmony_ci struct smb_rqst rqst; 51778c2ecf20Sopenharmony_ci struct smb2_query_info_rsp *rsp = NULL; 51788c2ecf20Sopenharmony_ci struct kvec iov; 51798c2ecf20Sopenharmony_ci struct kvec rsp_iov; 51808c2ecf20Sopenharmony_ci int rc = 0; 51818c2ecf20Sopenharmony_ci int resp_buftype; 51828c2ecf20Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 51838c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(ses); 51848c2ecf20Sopenharmony_ci struct smb2_fs_full_size_info *info = NULL; 51858c2ecf20Sopenharmony_ci int flags = 0; 51868c2ecf20Sopenharmony_ci 51878c2ecf20Sopenharmony_ci rc = build_qfs_info_req(&iov, tcon, server, 51888c2ecf20Sopenharmony_ci FS_FULL_SIZE_INFORMATION, 51898c2ecf20Sopenharmony_ci sizeof(struct smb2_fs_full_size_info), 51908c2ecf20Sopenharmony_ci persistent_fid, volatile_fid); 51918c2ecf20Sopenharmony_ci if (rc) 51928c2ecf20Sopenharmony_ci return rc; 51938c2ecf20Sopenharmony_ci 51948c2ecf20Sopenharmony_ci if (smb3_encryption_required(tcon)) 51958c2ecf20Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 51968c2ecf20Sopenharmony_ci 51978c2ecf20Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 51988c2ecf20Sopenharmony_ci rqst.rq_iov = &iov; 51998c2ecf20Sopenharmony_ci rqst.rq_nvec = 1; 52008c2ecf20Sopenharmony_ci 52018c2ecf20Sopenharmony_ci rc = cifs_send_recv(xid, ses, server, 52028c2ecf20Sopenharmony_ci &rqst, &resp_buftype, flags, &rsp_iov); 52038c2ecf20Sopenharmony_ci free_qfs_info_req(&iov); 52048c2ecf20Sopenharmony_ci if (rc) { 52058c2ecf20Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); 52068c2ecf20Sopenharmony_ci goto qfsinf_exit; 52078c2ecf20Sopenharmony_ci } 52088c2ecf20Sopenharmony_ci rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; 52098c2ecf20Sopenharmony_ci 52108c2ecf20Sopenharmony_ci info = (struct smb2_fs_full_size_info *)( 52118c2ecf20Sopenharmony_ci le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp); 52128c2ecf20Sopenharmony_ci rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), 52138c2ecf20Sopenharmony_ci le32_to_cpu(rsp->OutputBufferLength), &rsp_iov, 52148c2ecf20Sopenharmony_ci sizeof(struct smb2_fs_full_size_info)); 52158c2ecf20Sopenharmony_ci if (!rc) 52168c2ecf20Sopenharmony_ci smb2_copy_fs_info_to_kstatfs(info, fsdata); 52178c2ecf20Sopenharmony_ci 52188c2ecf20Sopenharmony_ciqfsinf_exit: 52198c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype, rsp_iov.iov_base); 52208c2ecf20Sopenharmony_ci return rc; 52218c2ecf20Sopenharmony_ci} 52228c2ecf20Sopenharmony_ci 52238c2ecf20Sopenharmony_ciint 52248c2ecf20Sopenharmony_ciSMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, 52258c2ecf20Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, int level) 52268c2ecf20Sopenharmony_ci{ 52278c2ecf20Sopenharmony_ci struct smb_rqst rqst; 52288c2ecf20Sopenharmony_ci struct smb2_query_info_rsp *rsp = NULL; 52298c2ecf20Sopenharmony_ci struct kvec iov; 52308c2ecf20Sopenharmony_ci struct kvec rsp_iov; 52318c2ecf20Sopenharmony_ci int rc = 0; 52328c2ecf20Sopenharmony_ci int resp_buftype, max_len, min_len; 52338c2ecf20Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 52348c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(ses); 52358c2ecf20Sopenharmony_ci unsigned int rsp_len, offset; 52368c2ecf20Sopenharmony_ci int flags = 0; 52378c2ecf20Sopenharmony_ci 52388c2ecf20Sopenharmony_ci if (level == FS_DEVICE_INFORMATION) { 52398c2ecf20Sopenharmony_ci max_len = sizeof(FILE_SYSTEM_DEVICE_INFO); 52408c2ecf20Sopenharmony_ci min_len = sizeof(FILE_SYSTEM_DEVICE_INFO); 52418c2ecf20Sopenharmony_ci } else if (level == FS_ATTRIBUTE_INFORMATION) { 52428c2ecf20Sopenharmony_ci max_len = sizeof(FILE_SYSTEM_ATTRIBUTE_INFO); 52438c2ecf20Sopenharmony_ci min_len = MIN_FS_ATTR_INFO_SIZE; 52448c2ecf20Sopenharmony_ci } else if (level == FS_SECTOR_SIZE_INFORMATION) { 52458c2ecf20Sopenharmony_ci max_len = sizeof(struct smb3_fs_ss_info); 52468c2ecf20Sopenharmony_ci min_len = sizeof(struct smb3_fs_ss_info); 52478c2ecf20Sopenharmony_ci } else if (level == FS_VOLUME_INFORMATION) { 52488c2ecf20Sopenharmony_ci max_len = sizeof(struct smb3_fs_vol_info) + MAX_VOL_LABEL_LEN; 52498c2ecf20Sopenharmony_ci min_len = sizeof(struct smb3_fs_vol_info); 52508c2ecf20Sopenharmony_ci } else { 52518c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Invalid qfsinfo level %d\n", level); 52528c2ecf20Sopenharmony_ci return -EINVAL; 52538c2ecf20Sopenharmony_ci } 52548c2ecf20Sopenharmony_ci 52558c2ecf20Sopenharmony_ci rc = build_qfs_info_req(&iov, tcon, server, 52568c2ecf20Sopenharmony_ci level, max_len, 52578c2ecf20Sopenharmony_ci persistent_fid, volatile_fid); 52588c2ecf20Sopenharmony_ci if (rc) 52598c2ecf20Sopenharmony_ci return rc; 52608c2ecf20Sopenharmony_ci 52618c2ecf20Sopenharmony_ci if (smb3_encryption_required(tcon)) 52628c2ecf20Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 52638c2ecf20Sopenharmony_ci 52648c2ecf20Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 52658c2ecf20Sopenharmony_ci rqst.rq_iov = &iov; 52668c2ecf20Sopenharmony_ci rqst.rq_nvec = 1; 52678c2ecf20Sopenharmony_ci 52688c2ecf20Sopenharmony_ci rc = cifs_send_recv(xid, ses, server, 52698c2ecf20Sopenharmony_ci &rqst, &resp_buftype, flags, &rsp_iov); 52708c2ecf20Sopenharmony_ci free_qfs_info_req(&iov); 52718c2ecf20Sopenharmony_ci if (rc) { 52728c2ecf20Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); 52738c2ecf20Sopenharmony_ci goto qfsattr_exit; 52748c2ecf20Sopenharmony_ci } 52758c2ecf20Sopenharmony_ci rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; 52768c2ecf20Sopenharmony_ci 52778c2ecf20Sopenharmony_ci rsp_len = le32_to_cpu(rsp->OutputBufferLength); 52788c2ecf20Sopenharmony_ci offset = le16_to_cpu(rsp->OutputBufferOffset); 52798c2ecf20Sopenharmony_ci rc = smb2_validate_iov(offset, rsp_len, &rsp_iov, min_len); 52808c2ecf20Sopenharmony_ci if (rc) 52818c2ecf20Sopenharmony_ci goto qfsattr_exit; 52828c2ecf20Sopenharmony_ci 52838c2ecf20Sopenharmony_ci if (level == FS_ATTRIBUTE_INFORMATION) 52848c2ecf20Sopenharmony_ci memcpy(&tcon->fsAttrInfo, offset 52858c2ecf20Sopenharmony_ci + (char *)rsp, min_t(unsigned int, 52868c2ecf20Sopenharmony_ci rsp_len, max_len)); 52878c2ecf20Sopenharmony_ci else if (level == FS_DEVICE_INFORMATION) 52888c2ecf20Sopenharmony_ci memcpy(&tcon->fsDevInfo, offset 52898c2ecf20Sopenharmony_ci + (char *)rsp, sizeof(FILE_SYSTEM_DEVICE_INFO)); 52908c2ecf20Sopenharmony_ci else if (level == FS_SECTOR_SIZE_INFORMATION) { 52918c2ecf20Sopenharmony_ci struct smb3_fs_ss_info *ss_info = (struct smb3_fs_ss_info *) 52928c2ecf20Sopenharmony_ci (offset + (char *)rsp); 52938c2ecf20Sopenharmony_ci tcon->ss_flags = le32_to_cpu(ss_info->Flags); 52948c2ecf20Sopenharmony_ci tcon->perf_sector_size = 52958c2ecf20Sopenharmony_ci le32_to_cpu(ss_info->PhysicalBytesPerSectorForPerf); 52968c2ecf20Sopenharmony_ci } else if (level == FS_VOLUME_INFORMATION) { 52978c2ecf20Sopenharmony_ci struct smb3_fs_vol_info *vol_info = (struct smb3_fs_vol_info *) 52988c2ecf20Sopenharmony_ci (offset + (char *)rsp); 52998c2ecf20Sopenharmony_ci tcon->vol_serial_number = vol_info->VolumeSerialNumber; 53008c2ecf20Sopenharmony_ci tcon->vol_create_time = vol_info->VolumeCreationTime; 53018c2ecf20Sopenharmony_ci } 53028c2ecf20Sopenharmony_ci 53038c2ecf20Sopenharmony_ciqfsattr_exit: 53048c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype, rsp_iov.iov_base); 53058c2ecf20Sopenharmony_ci return rc; 53068c2ecf20Sopenharmony_ci} 53078c2ecf20Sopenharmony_ci 53088c2ecf20Sopenharmony_ciint 53098c2ecf20Sopenharmony_cismb2_lockv(const unsigned int xid, struct cifs_tcon *tcon, 53108c2ecf20Sopenharmony_ci const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid, 53118c2ecf20Sopenharmony_ci const __u32 num_lock, struct smb2_lock_element *buf) 53128c2ecf20Sopenharmony_ci{ 53138c2ecf20Sopenharmony_ci struct smb_rqst rqst; 53148c2ecf20Sopenharmony_ci int rc = 0; 53158c2ecf20Sopenharmony_ci struct smb2_lock_req *req = NULL; 53168c2ecf20Sopenharmony_ci struct kvec iov[2]; 53178c2ecf20Sopenharmony_ci struct kvec rsp_iov; 53188c2ecf20Sopenharmony_ci int resp_buf_type; 53198c2ecf20Sopenharmony_ci unsigned int count; 53208c2ecf20Sopenharmony_ci int flags = CIFS_NO_RSP_BUF; 53218c2ecf20Sopenharmony_ci unsigned int total_len; 53228c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); 53238c2ecf20Sopenharmony_ci 53248c2ecf20Sopenharmony_ci cifs_dbg(FYI, "smb2_lockv num lock %d\n", num_lock); 53258c2ecf20Sopenharmony_ci 53268c2ecf20Sopenharmony_ci rc = smb2_plain_req_init(SMB2_LOCK, tcon, server, 53278c2ecf20Sopenharmony_ci (void **) &req, &total_len); 53288c2ecf20Sopenharmony_ci if (rc) 53298c2ecf20Sopenharmony_ci return rc; 53308c2ecf20Sopenharmony_ci 53318c2ecf20Sopenharmony_ci if (smb3_encryption_required(tcon)) 53328c2ecf20Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 53338c2ecf20Sopenharmony_ci 53348c2ecf20Sopenharmony_ci req->sync_hdr.ProcessId = cpu_to_le32(pid); 53358c2ecf20Sopenharmony_ci req->LockCount = cpu_to_le16(num_lock); 53368c2ecf20Sopenharmony_ci 53378c2ecf20Sopenharmony_ci req->PersistentFileId = persist_fid; 53388c2ecf20Sopenharmony_ci req->VolatileFileId = volatile_fid; 53398c2ecf20Sopenharmony_ci 53408c2ecf20Sopenharmony_ci count = num_lock * sizeof(struct smb2_lock_element); 53418c2ecf20Sopenharmony_ci 53428c2ecf20Sopenharmony_ci iov[0].iov_base = (char *)req; 53438c2ecf20Sopenharmony_ci iov[0].iov_len = total_len - sizeof(struct smb2_lock_element); 53448c2ecf20Sopenharmony_ci iov[1].iov_base = (char *)buf; 53458c2ecf20Sopenharmony_ci iov[1].iov_len = count; 53468c2ecf20Sopenharmony_ci 53478c2ecf20Sopenharmony_ci cifs_stats_inc(&tcon->stats.cifs_stats.num_locks); 53488c2ecf20Sopenharmony_ci 53498c2ecf20Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 53508c2ecf20Sopenharmony_ci rqst.rq_iov = iov; 53518c2ecf20Sopenharmony_ci rqst.rq_nvec = 2; 53528c2ecf20Sopenharmony_ci 53538c2ecf20Sopenharmony_ci rc = cifs_send_recv(xid, tcon->ses, server, 53548c2ecf20Sopenharmony_ci &rqst, &resp_buf_type, flags, 53558c2ecf20Sopenharmony_ci &rsp_iov); 53568c2ecf20Sopenharmony_ci cifs_small_buf_release(req); 53578c2ecf20Sopenharmony_ci if (rc) { 53588c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Send error in smb2_lockv = %d\n", rc); 53598c2ecf20Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_LOCK_HE); 53608c2ecf20Sopenharmony_ci trace_smb3_lock_err(xid, persist_fid, tcon->tid, 53618c2ecf20Sopenharmony_ci tcon->ses->Suid, rc); 53628c2ecf20Sopenharmony_ci } 53638c2ecf20Sopenharmony_ci 53648c2ecf20Sopenharmony_ci return rc; 53658c2ecf20Sopenharmony_ci} 53668c2ecf20Sopenharmony_ci 53678c2ecf20Sopenharmony_ciint 53688c2ecf20Sopenharmony_ciSMB2_lock(const unsigned int xid, struct cifs_tcon *tcon, 53698c2ecf20Sopenharmony_ci const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid, 53708c2ecf20Sopenharmony_ci const __u64 length, const __u64 offset, const __u32 lock_flags, 53718c2ecf20Sopenharmony_ci const bool wait) 53728c2ecf20Sopenharmony_ci{ 53738c2ecf20Sopenharmony_ci struct smb2_lock_element lock; 53748c2ecf20Sopenharmony_ci 53758c2ecf20Sopenharmony_ci lock.Offset = cpu_to_le64(offset); 53768c2ecf20Sopenharmony_ci lock.Length = cpu_to_le64(length); 53778c2ecf20Sopenharmony_ci lock.Flags = cpu_to_le32(lock_flags); 53788c2ecf20Sopenharmony_ci if (!wait && lock_flags != SMB2_LOCKFLAG_UNLOCK) 53798c2ecf20Sopenharmony_ci lock.Flags |= cpu_to_le32(SMB2_LOCKFLAG_FAIL_IMMEDIATELY); 53808c2ecf20Sopenharmony_ci 53818c2ecf20Sopenharmony_ci return smb2_lockv(xid, tcon, persist_fid, volatile_fid, pid, 1, &lock); 53828c2ecf20Sopenharmony_ci} 53838c2ecf20Sopenharmony_ci 53848c2ecf20Sopenharmony_ciint 53858c2ecf20Sopenharmony_ciSMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon, 53868c2ecf20Sopenharmony_ci __u8 *lease_key, const __le32 lease_state) 53878c2ecf20Sopenharmony_ci{ 53888c2ecf20Sopenharmony_ci struct smb_rqst rqst; 53898c2ecf20Sopenharmony_ci int rc; 53908c2ecf20Sopenharmony_ci struct smb2_lease_ack *req = NULL; 53918c2ecf20Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 53928c2ecf20Sopenharmony_ci int flags = CIFS_OBREAK_OP; 53938c2ecf20Sopenharmony_ci unsigned int total_len; 53948c2ecf20Sopenharmony_ci struct kvec iov[1]; 53958c2ecf20Sopenharmony_ci struct kvec rsp_iov; 53968c2ecf20Sopenharmony_ci int resp_buf_type; 53978c2ecf20Sopenharmony_ci __u64 *please_key_high; 53988c2ecf20Sopenharmony_ci __u64 *please_key_low; 53998c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); 54008c2ecf20Sopenharmony_ci 54018c2ecf20Sopenharmony_ci cifs_dbg(FYI, "SMB2_lease_break\n"); 54028c2ecf20Sopenharmony_ci rc = smb2_plain_req_init(SMB2_OPLOCK_BREAK, tcon, server, 54038c2ecf20Sopenharmony_ci (void **) &req, &total_len); 54048c2ecf20Sopenharmony_ci if (rc) 54058c2ecf20Sopenharmony_ci return rc; 54068c2ecf20Sopenharmony_ci 54078c2ecf20Sopenharmony_ci if (smb3_encryption_required(tcon)) 54088c2ecf20Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 54098c2ecf20Sopenharmony_ci 54108c2ecf20Sopenharmony_ci req->sync_hdr.CreditRequest = cpu_to_le16(1); 54118c2ecf20Sopenharmony_ci req->StructureSize = cpu_to_le16(36); 54128c2ecf20Sopenharmony_ci total_len += 12; 54138c2ecf20Sopenharmony_ci 54148c2ecf20Sopenharmony_ci memcpy(req->LeaseKey, lease_key, 16); 54158c2ecf20Sopenharmony_ci req->LeaseState = lease_state; 54168c2ecf20Sopenharmony_ci 54178c2ecf20Sopenharmony_ci flags |= CIFS_NO_RSP_BUF; 54188c2ecf20Sopenharmony_ci 54198c2ecf20Sopenharmony_ci iov[0].iov_base = (char *)req; 54208c2ecf20Sopenharmony_ci iov[0].iov_len = total_len; 54218c2ecf20Sopenharmony_ci 54228c2ecf20Sopenharmony_ci memset(&rqst, 0, sizeof(struct smb_rqst)); 54238c2ecf20Sopenharmony_ci rqst.rq_iov = iov; 54248c2ecf20Sopenharmony_ci rqst.rq_nvec = 1; 54258c2ecf20Sopenharmony_ci 54268c2ecf20Sopenharmony_ci rc = cifs_send_recv(xid, ses, server, 54278c2ecf20Sopenharmony_ci &rqst, &resp_buf_type, flags, &rsp_iov); 54288c2ecf20Sopenharmony_ci cifs_small_buf_release(req); 54298c2ecf20Sopenharmony_ci 54308c2ecf20Sopenharmony_ci please_key_low = (__u64 *)lease_key; 54318c2ecf20Sopenharmony_ci please_key_high = (__u64 *)(lease_key+8); 54328c2ecf20Sopenharmony_ci if (rc) { 54338c2ecf20Sopenharmony_ci cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE); 54348c2ecf20Sopenharmony_ci trace_smb3_lease_err(le32_to_cpu(lease_state), tcon->tid, 54358c2ecf20Sopenharmony_ci ses->Suid, *please_key_low, *please_key_high, rc); 54368c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Send error in Lease Break = %d\n", rc); 54378c2ecf20Sopenharmony_ci } else 54388c2ecf20Sopenharmony_ci trace_smb3_lease_done(le32_to_cpu(lease_state), tcon->tid, 54398c2ecf20Sopenharmony_ci ses->Suid, *please_key_low, *please_key_high); 54408c2ecf20Sopenharmony_ci 54418c2ecf20Sopenharmony_ci return rc; 54428c2ecf20Sopenharmony_ci} 5443