18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * SMB2 version specific operations 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2012, Jeff Layton <jlayton@redhat.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 98c2ecf20Sopenharmony_ci#include <linux/vfs.h> 108c2ecf20Sopenharmony_ci#include <linux/falloc.h> 118c2ecf20Sopenharmony_ci#include <linux/scatterlist.h> 128c2ecf20Sopenharmony_ci#include <linux/uuid.h> 138c2ecf20Sopenharmony_ci#include <linux/sort.h> 148c2ecf20Sopenharmony_ci#include <crypto/aead.h> 158c2ecf20Sopenharmony_ci#include <linux/fiemap.h> 168c2ecf20Sopenharmony_ci#include "cifsfs.h" 178c2ecf20Sopenharmony_ci#include "cifsglob.h" 188c2ecf20Sopenharmony_ci#include "smb2pdu.h" 198c2ecf20Sopenharmony_ci#include "smb2proto.h" 208c2ecf20Sopenharmony_ci#include "cifsproto.h" 218c2ecf20Sopenharmony_ci#include "cifs_debug.h" 228c2ecf20Sopenharmony_ci#include "cifs_unicode.h" 238c2ecf20Sopenharmony_ci#include "smb2status.h" 248c2ecf20Sopenharmony_ci#include "smb2glob.h" 258c2ecf20Sopenharmony_ci#include "cifs_ioctl.h" 268c2ecf20Sopenharmony_ci#include "smbdirect.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* Change credits for different ops and return the total number of credits */ 298c2ecf20Sopenharmony_cistatic int 308c2ecf20Sopenharmony_cichange_conf(struct TCP_Server_Info *server) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci server->credits += server->echo_credits + server->oplock_credits; 338c2ecf20Sopenharmony_ci server->oplock_credits = server->echo_credits = 0; 348c2ecf20Sopenharmony_ci switch (server->credits) { 358c2ecf20Sopenharmony_ci case 0: 368c2ecf20Sopenharmony_ci return 0; 378c2ecf20Sopenharmony_ci case 1: 388c2ecf20Sopenharmony_ci server->echoes = false; 398c2ecf20Sopenharmony_ci server->oplocks = false; 408c2ecf20Sopenharmony_ci break; 418c2ecf20Sopenharmony_ci case 2: 428c2ecf20Sopenharmony_ci server->echoes = true; 438c2ecf20Sopenharmony_ci server->oplocks = false; 448c2ecf20Sopenharmony_ci server->echo_credits = 1; 458c2ecf20Sopenharmony_ci break; 468c2ecf20Sopenharmony_ci default: 478c2ecf20Sopenharmony_ci server->echoes = true; 488c2ecf20Sopenharmony_ci if (enable_oplocks) { 498c2ecf20Sopenharmony_ci server->oplocks = true; 508c2ecf20Sopenharmony_ci server->oplock_credits = 1; 518c2ecf20Sopenharmony_ci } else 528c2ecf20Sopenharmony_ci server->oplocks = false; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci server->echo_credits = 1; 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci server->credits -= server->echo_credits + server->oplock_credits; 578c2ecf20Sopenharmony_ci return server->credits + server->echo_credits + server->oplock_credits; 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic void 618c2ecf20Sopenharmony_cismb2_add_credits(struct TCP_Server_Info *server, 628c2ecf20Sopenharmony_ci const struct cifs_credits *credits, const int optype) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci int *val, rc = -1; 658c2ecf20Sopenharmony_ci unsigned int add = credits->value; 668c2ecf20Sopenharmony_ci unsigned int instance = credits->instance; 678c2ecf20Sopenharmony_ci bool reconnect_detected = false; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci spin_lock(&server->req_lock); 708c2ecf20Sopenharmony_ci val = server->ops->get_credits_field(server, optype); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci /* eg found case where write overlapping reconnect messed up credits */ 738c2ecf20Sopenharmony_ci if (((optype & CIFS_OP_MASK) == CIFS_NEG_OP) && (*val != 0)) 748c2ecf20Sopenharmony_ci trace_smb3_reconnect_with_invalid_credits(server->CurrentMid, 758c2ecf20Sopenharmony_ci server->hostname, *val, add); 768c2ecf20Sopenharmony_ci if ((instance == 0) || (instance == server->reconnect_instance)) 778c2ecf20Sopenharmony_ci *val += add; 788c2ecf20Sopenharmony_ci else 798c2ecf20Sopenharmony_ci reconnect_detected = true; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if (*val > 65000) { 828c2ecf20Sopenharmony_ci *val = 65000; /* Don't get near 64K credits, avoid srv bugs */ 838c2ecf20Sopenharmony_ci pr_warn_once("server overflowed SMB3 credits\n"); 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci server->in_flight--; 868c2ecf20Sopenharmony_ci if (server->in_flight == 0 && (optype & CIFS_OP_MASK) != CIFS_NEG_OP) 878c2ecf20Sopenharmony_ci rc = change_conf(server); 888c2ecf20Sopenharmony_ci /* 898c2ecf20Sopenharmony_ci * Sometimes server returns 0 credits on oplock break ack - we need to 908c2ecf20Sopenharmony_ci * rebalance credits in this case. 918c2ecf20Sopenharmony_ci */ 928c2ecf20Sopenharmony_ci else if (server->in_flight > 0 && server->oplock_credits == 0 && 938c2ecf20Sopenharmony_ci server->oplocks) { 948c2ecf20Sopenharmony_ci if (server->credits > 1) { 958c2ecf20Sopenharmony_ci server->credits--; 968c2ecf20Sopenharmony_ci server->oplock_credits++; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci spin_unlock(&server->req_lock); 1008c2ecf20Sopenharmony_ci wake_up(&server->request_q); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci if (reconnect_detected) 1038c2ecf20Sopenharmony_ci cifs_dbg(FYI, "trying to put %d credits from the old server instance %d\n", 1048c2ecf20Sopenharmony_ci add, instance); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (server->tcpStatus == CifsNeedReconnect 1078c2ecf20Sopenharmony_ci || server->tcpStatus == CifsExiting) 1088c2ecf20Sopenharmony_ci return; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci switch (rc) { 1118c2ecf20Sopenharmony_ci case -1: 1128c2ecf20Sopenharmony_ci /* change_conf hasn't been executed */ 1138c2ecf20Sopenharmony_ci break; 1148c2ecf20Sopenharmony_ci case 0: 1158c2ecf20Sopenharmony_ci cifs_server_dbg(VFS, "Possible client or server bug - zero credits\n"); 1168c2ecf20Sopenharmony_ci break; 1178c2ecf20Sopenharmony_ci case 1: 1188c2ecf20Sopenharmony_ci cifs_server_dbg(VFS, "disabling echoes and oplocks\n"); 1198c2ecf20Sopenharmony_ci break; 1208c2ecf20Sopenharmony_ci case 2: 1218c2ecf20Sopenharmony_ci cifs_dbg(FYI, "disabling oplocks\n"); 1228c2ecf20Sopenharmony_ci break; 1238c2ecf20Sopenharmony_ci default: 1248c2ecf20Sopenharmony_ci trace_smb3_add_credits(server->CurrentMid, 1258c2ecf20Sopenharmony_ci server->hostname, rc, add); 1268c2ecf20Sopenharmony_ci cifs_dbg(FYI, "add %u credits total=%d\n", add, rc); 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic void 1318c2ecf20Sopenharmony_cismb2_set_credits(struct TCP_Server_Info *server, const int val) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci spin_lock(&server->req_lock); 1348c2ecf20Sopenharmony_ci server->credits = val; 1358c2ecf20Sopenharmony_ci if (val == 1) 1368c2ecf20Sopenharmony_ci server->reconnect_instance++; 1378c2ecf20Sopenharmony_ci spin_unlock(&server->req_lock); 1388c2ecf20Sopenharmony_ci /* don't log while holding the lock */ 1398c2ecf20Sopenharmony_ci if (val == 1) 1408c2ecf20Sopenharmony_ci cifs_dbg(FYI, "set credits to 1 due to smb2 reconnect\n"); 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic int * 1448c2ecf20Sopenharmony_cismb2_get_credits_field(struct TCP_Server_Info *server, const int optype) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci switch (optype) { 1478c2ecf20Sopenharmony_ci case CIFS_ECHO_OP: 1488c2ecf20Sopenharmony_ci return &server->echo_credits; 1498c2ecf20Sopenharmony_ci case CIFS_OBREAK_OP: 1508c2ecf20Sopenharmony_ci return &server->oplock_credits; 1518c2ecf20Sopenharmony_ci default: 1528c2ecf20Sopenharmony_ci return &server->credits; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic unsigned int 1578c2ecf20Sopenharmony_cismb2_get_credits(struct mid_q_entry *mid) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci return mid->credits_received; 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic int 1638c2ecf20Sopenharmony_cismb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size, 1648c2ecf20Sopenharmony_ci unsigned int *num, struct cifs_credits *credits) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci int rc = 0; 1678c2ecf20Sopenharmony_ci unsigned int scredits; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci spin_lock(&server->req_lock); 1708c2ecf20Sopenharmony_ci while (1) { 1718c2ecf20Sopenharmony_ci if (server->credits <= 0) { 1728c2ecf20Sopenharmony_ci spin_unlock(&server->req_lock); 1738c2ecf20Sopenharmony_ci cifs_num_waiters_inc(server); 1748c2ecf20Sopenharmony_ci rc = wait_event_killable(server->request_q, 1758c2ecf20Sopenharmony_ci has_credits(server, &server->credits, 1)); 1768c2ecf20Sopenharmony_ci cifs_num_waiters_dec(server); 1778c2ecf20Sopenharmony_ci if (rc) 1788c2ecf20Sopenharmony_ci return rc; 1798c2ecf20Sopenharmony_ci spin_lock(&server->req_lock); 1808c2ecf20Sopenharmony_ci } else { 1818c2ecf20Sopenharmony_ci if (server->tcpStatus == CifsExiting) { 1828c2ecf20Sopenharmony_ci spin_unlock(&server->req_lock); 1838c2ecf20Sopenharmony_ci return -ENOENT; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci scredits = server->credits; 1878c2ecf20Sopenharmony_ci /* can deadlock with reopen */ 1888c2ecf20Sopenharmony_ci if (scredits <= 8) { 1898c2ecf20Sopenharmony_ci *num = SMB2_MAX_BUFFER_SIZE; 1908c2ecf20Sopenharmony_ci credits->value = 0; 1918c2ecf20Sopenharmony_ci credits->instance = 0; 1928c2ecf20Sopenharmony_ci break; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci /* leave some credits for reopen and other ops */ 1968c2ecf20Sopenharmony_ci scredits -= 8; 1978c2ecf20Sopenharmony_ci *num = min_t(unsigned int, size, 1988c2ecf20Sopenharmony_ci scredits * SMB2_MAX_BUFFER_SIZE); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci credits->value = 2018c2ecf20Sopenharmony_ci DIV_ROUND_UP(*num, SMB2_MAX_BUFFER_SIZE); 2028c2ecf20Sopenharmony_ci credits->instance = server->reconnect_instance; 2038c2ecf20Sopenharmony_ci server->credits -= credits->value; 2048c2ecf20Sopenharmony_ci server->in_flight++; 2058c2ecf20Sopenharmony_ci if (server->in_flight > server->max_in_flight) 2068c2ecf20Sopenharmony_ci server->max_in_flight = server->in_flight; 2078c2ecf20Sopenharmony_ci break; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci spin_unlock(&server->req_lock); 2118c2ecf20Sopenharmony_ci return rc; 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic int 2158c2ecf20Sopenharmony_cismb2_adjust_credits(struct TCP_Server_Info *server, 2168c2ecf20Sopenharmony_ci struct cifs_credits *credits, 2178c2ecf20Sopenharmony_ci const unsigned int payload_size) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci int new_val = DIV_ROUND_UP(payload_size, SMB2_MAX_BUFFER_SIZE); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci if (!credits->value || credits->value == new_val) 2228c2ecf20Sopenharmony_ci return 0; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (credits->value < new_val) { 2258c2ecf20Sopenharmony_ci WARN_ONCE(1, "request has less credits (%d) than required (%d)", 2268c2ecf20Sopenharmony_ci credits->value, new_val); 2278c2ecf20Sopenharmony_ci return -ENOTSUPP; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci spin_lock(&server->req_lock); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci if (server->reconnect_instance != credits->instance) { 2338c2ecf20Sopenharmony_ci spin_unlock(&server->req_lock); 2348c2ecf20Sopenharmony_ci cifs_server_dbg(VFS, "trying to return %d credits to old session\n", 2358c2ecf20Sopenharmony_ci credits->value - new_val); 2368c2ecf20Sopenharmony_ci return -EAGAIN; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci server->credits += credits->value - new_val; 2408c2ecf20Sopenharmony_ci spin_unlock(&server->req_lock); 2418c2ecf20Sopenharmony_ci wake_up(&server->request_q); 2428c2ecf20Sopenharmony_ci credits->value = new_val; 2438c2ecf20Sopenharmony_ci return 0; 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic __u64 2478c2ecf20Sopenharmony_cismb2_get_next_mid(struct TCP_Server_Info *server) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci __u64 mid; 2508c2ecf20Sopenharmony_ci /* for SMB2 we need the current value */ 2518c2ecf20Sopenharmony_ci spin_lock(&GlobalMid_Lock); 2528c2ecf20Sopenharmony_ci mid = server->CurrentMid++; 2538c2ecf20Sopenharmony_ci spin_unlock(&GlobalMid_Lock); 2548c2ecf20Sopenharmony_ci return mid; 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic void 2588c2ecf20Sopenharmony_cismb2_revert_current_mid(struct TCP_Server_Info *server, const unsigned int val) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci spin_lock(&GlobalMid_Lock); 2618c2ecf20Sopenharmony_ci if (server->CurrentMid >= val) 2628c2ecf20Sopenharmony_ci server->CurrentMid -= val; 2638c2ecf20Sopenharmony_ci spin_unlock(&GlobalMid_Lock); 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic struct mid_q_entry * 2678c2ecf20Sopenharmony_ci__smb2_find_mid(struct TCP_Server_Info *server, char *buf, bool dequeue) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci struct mid_q_entry *mid; 2708c2ecf20Sopenharmony_ci struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf; 2718c2ecf20Sopenharmony_ci __u64 wire_mid = le64_to_cpu(shdr->MessageId); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci if (shdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) { 2748c2ecf20Sopenharmony_ci cifs_server_dbg(VFS, "Encrypted frame parsing not supported yet\n"); 2758c2ecf20Sopenharmony_ci return NULL; 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci spin_lock(&GlobalMid_Lock); 2798c2ecf20Sopenharmony_ci list_for_each_entry(mid, &server->pending_mid_q, qhead) { 2808c2ecf20Sopenharmony_ci if ((mid->mid == wire_mid) && 2818c2ecf20Sopenharmony_ci (mid->mid_state == MID_REQUEST_SUBMITTED) && 2828c2ecf20Sopenharmony_ci (mid->command == shdr->Command)) { 2838c2ecf20Sopenharmony_ci kref_get(&mid->refcount); 2848c2ecf20Sopenharmony_ci if (dequeue) { 2858c2ecf20Sopenharmony_ci list_del_init(&mid->qhead); 2868c2ecf20Sopenharmony_ci mid->mid_flags |= MID_DELETED; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci spin_unlock(&GlobalMid_Lock); 2898c2ecf20Sopenharmony_ci return mid; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci spin_unlock(&GlobalMid_Lock); 2938c2ecf20Sopenharmony_ci return NULL; 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistatic struct mid_q_entry * 2978c2ecf20Sopenharmony_cismb2_find_mid(struct TCP_Server_Info *server, char *buf) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci return __smb2_find_mid(server, buf, false); 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic struct mid_q_entry * 3038c2ecf20Sopenharmony_cismb2_find_dequeue_mid(struct TCP_Server_Info *server, char *buf) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci return __smb2_find_mid(server, buf, true); 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistatic void 3098c2ecf20Sopenharmony_cismb2_dump_detail(void *buf, struct TCP_Server_Info *server) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_DEBUG2 3128c2ecf20Sopenharmony_ci struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci cifs_server_dbg(VFS, "Cmd: %d Err: 0x%x Flags: 0x%x Mid: %llu Pid: %d\n", 3158c2ecf20Sopenharmony_ci shdr->Command, shdr->Status, shdr->Flags, shdr->MessageId, 3168c2ecf20Sopenharmony_ci shdr->ProcessId); 3178c2ecf20Sopenharmony_ci cifs_server_dbg(VFS, "smb buf %p len %u\n", buf, 3188c2ecf20Sopenharmony_ci server->ops->calc_smb_size(buf, server)); 3198c2ecf20Sopenharmony_ci#endif 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic bool 3238c2ecf20Sopenharmony_cismb2_need_neg(struct TCP_Server_Info *server) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci return server->max_read == 0; 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic int 3298c2ecf20Sopenharmony_cismb2_negotiate(const unsigned int xid, struct cifs_ses *ses) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci int rc; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci cifs_ses_server(ses)->CurrentMid = 0; 3348c2ecf20Sopenharmony_ci rc = SMB2_negotiate(xid, ses); 3358c2ecf20Sopenharmony_ci /* BB we probably don't need to retry with modern servers */ 3368c2ecf20Sopenharmony_ci if (rc == -EAGAIN) 3378c2ecf20Sopenharmony_ci rc = -EHOSTDOWN; 3388c2ecf20Sopenharmony_ci return rc; 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cistatic unsigned int 3428c2ecf20Sopenharmony_cismb2_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = tcon->ses->server; 3458c2ecf20Sopenharmony_ci unsigned int wsize; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci /* start with specified wsize, or default */ 3488c2ecf20Sopenharmony_ci wsize = volume_info->wsize ? volume_info->wsize : CIFS_DEFAULT_IOSIZE; 3498c2ecf20Sopenharmony_ci wsize = min_t(unsigned int, wsize, server->max_write); 3508c2ecf20Sopenharmony_ci if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) 3518c2ecf20Sopenharmony_ci wsize = min_t(unsigned int, wsize, SMB2_MAX_BUFFER_SIZE); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci return wsize; 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic unsigned int 3578c2ecf20Sopenharmony_cismb3_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = tcon->ses->server; 3608c2ecf20Sopenharmony_ci unsigned int wsize; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci /* start with specified wsize, or default */ 3638c2ecf20Sopenharmony_ci wsize = volume_info->wsize ? volume_info->wsize : SMB3_DEFAULT_IOSIZE; 3648c2ecf20Sopenharmony_ci wsize = min_t(unsigned int, wsize, server->max_write); 3658c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_SMB_DIRECT 3668c2ecf20Sopenharmony_ci if (server->rdma) { 3678c2ecf20Sopenharmony_ci if (server->sign) 3688c2ecf20Sopenharmony_ci /* 3698c2ecf20Sopenharmony_ci * Account for SMB2 data transfer packet header and 3708c2ecf20Sopenharmony_ci * possible encryption header 3718c2ecf20Sopenharmony_ci */ 3728c2ecf20Sopenharmony_ci wsize = min_t(unsigned int, 3738c2ecf20Sopenharmony_ci wsize, 3748c2ecf20Sopenharmony_ci server->smbd_conn->max_fragmented_send_size - 3758c2ecf20Sopenharmony_ci SMB2_READWRITE_PDU_HEADER_SIZE - 3768c2ecf20Sopenharmony_ci sizeof(struct smb2_transform_hdr)); 3778c2ecf20Sopenharmony_ci else 3788c2ecf20Sopenharmony_ci wsize = min_t(unsigned int, 3798c2ecf20Sopenharmony_ci wsize, server->smbd_conn->max_readwrite_size); 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci#endif 3828c2ecf20Sopenharmony_ci if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) 3838c2ecf20Sopenharmony_ci wsize = min_t(unsigned int, wsize, SMB2_MAX_BUFFER_SIZE); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci return wsize; 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistatic unsigned int 3898c2ecf20Sopenharmony_cismb2_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = tcon->ses->server; 3928c2ecf20Sopenharmony_ci unsigned int rsize; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci /* start with specified rsize, or default */ 3958c2ecf20Sopenharmony_ci rsize = volume_info->rsize ? volume_info->rsize : CIFS_DEFAULT_IOSIZE; 3968c2ecf20Sopenharmony_ci rsize = min_t(unsigned int, rsize, server->max_read); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) 3998c2ecf20Sopenharmony_ci rsize = min_t(unsigned int, rsize, SMB2_MAX_BUFFER_SIZE); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci return rsize; 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic unsigned int 4058c2ecf20Sopenharmony_cismb3_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = tcon->ses->server; 4088c2ecf20Sopenharmony_ci unsigned int rsize; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci /* start with specified rsize, or default */ 4118c2ecf20Sopenharmony_ci rsize = volume_info->rsize ? volume_info->rsize : SMB3_DEFAULT_IOSIZE; 4128c2ecf20Sopenharmony_ci rsize = min_t(unsigned int, rsize, server->max_read); 4138c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_SMB_DIRECT 4148c2ecf20Sopenharmony_ci if (server->rdma) { 4158c2ecf20Sopenharmony_ci if (server->sign) 4168c2ecf20Sopenharmony_ci /* 4178c2ecf20Sopenharmony_ci * Account for SMB2 data transfer packet header and 4188c2ecf20Sopenharmony_ci * possible encryption header 4198c2ecf20Sopenharmony_ci */ 4208c2ecf20Sopenharmony_ci rsize = min_t(unsigned int, 4218c2ecf20Sopenharmony_ci rsize, 4228c2ecf20Sopenharmony_ci server->smbd_conn->max_fragmented_recv_size - 4238c2ecf20Sopenharmony_ci SMB2_READWRITE_PDU_HEADER_SIZE - 4248c2ecf20Sopenharmony_ci sizeof(struct smb2_transform_hdr)); 4258c2ecf20Sopenharmony_ci else 4268c2ecf20Sopenharmony_ci rsize = min_t(unsigned int, 4278c2ecf20Sopenharmony_ci rsize, server->smbd_conn->max_readwrite_size); 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci#endif 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) 4328c2ecf20Sopenharmony_ci rsize = min_t(unsigned int, rsize, SMB2_MAX_BUFFER_SIZE); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci return rsize; 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistatic int 4388c2ecf20Sopenharmony_ciparse_server_interfaces(struct network_interface_info_ioctl_rsp *buf, 4398c2ecf20Sopenharmony_ci size_t buf_len, 4408c2ecf20Sopenharmony_ci struct cifs_server_iface **iface_list, 4418c2ecf20Sopenharmony_ci size_t *iface_count) 4428c2ecf20Sopenharmony_ci{ 4438c2ecf20Sopenharmony_ci struct network_interface_info_ioctl_rsp *p; 4448c2ecf20Sopenharmony_ci struct sockaddr_in *addr4; 4458c2ecf20Sopenharmony_ci struct sockaddr_in6 *addr6; 4468c2ecf20Sopenharmony_ci struct iface_info_ipv4 *p4; 4478c2ecf20Sopenharmony_ci struct iface_info_ipv6 *p6; 4488c2ecf20Sopenharmony_ci struct cifs_server_iface *info; 4498c2ecf20Sopenharmony_ci ssize_t bytes_left; 4508c2ecf20Sopenharmony_ci size_t next = 0; 4518c2ecf20Sopenharmony_ci int nb_iface = 0; 4528c2ecf20Sopenharmony_ci int rc = 0; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci *iface_list = NULL; 4558c2ecf20Sopenharmony_ci *iface_count = 0; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci /* 4588c2ecf20Sopenharmony_ci * Fist pass: count and sanity check 4598c2ecf20Sopenharmony_ci */ 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci bytes_left = buf_len; 4628c2ecf20Sopenharmony_ci p = buf; 4638c2ecf20Sopenharmony_ci while (bytes_left >= sizeof(*p)) { 4648c2ecf20Sopenharmony_ci nb_iface++; 4658c2ecf20Sopenharmony_ci next = le32_to_cpu(p->Next); 4668c2ecf20Sopenharmony_ci if (!next) { 4678c2ecf20Sopenharmony_ci bytes_left -= sizeof(*p); 4688c2ecf20Sopenharmony_ci break; 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci p = (struct network_interface_info_ioctl_rsp *)((u8 *)p+next); 4718c2ecf20Sopenharmony_ci bytes_left -= next; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci if (!nb_iface) { 4758c2ecf20Sopenharmony_ci cifs_dbg(VFS, "%s: malformed interface info\n", __func__); 4768c2ecf20Sopenharmony_ci rc = -EINVAL; 4778c2ecf20Sopenharmony_ci goto out; 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci /* Azure rounds the buffer size up 8, to a 16 byte boundary */ 4818c2ecf20Sopenharmony_ci if ((bytes_left > 8) || p->Next) 4828c2ecf20Sopenharmony_ci cifs_dbg(VFS, "%s: incomplete interface info\n", __func__); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci /* 4868c2ecf20Sopenharmony_ci * Second pass: extract info to internal structure 4878c2ecf20Sopenharmony_ci */ 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci *iface_list = kcalloc(nb_iface, sizeof(**iface_list), GFP_KERNEL); 4908c2ecf20Sopenharmony_ci if (!*iface_list) { 4918c2ecf20Sopenharmony_ci rc = -ENOMEM; 4928c2ecf20Sopenharmony_ci goto out; 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci info = *iface_list; 4968c2ecf20Sopenharmony_ci bytes_left = buf_len; 4978c2ecf20Sopenharmony_ci p = buf; 4988c2ecf20Sopenharmony_ci while (bytes_left >= sizeof(*p)) { 4998c2ecf20Sopenharmony_ci info->speed = le64_to_cpu(p->LinkSpeed); 5008c2ecf20Sopenharmony_ci info->rdma_capable = le32_to_cpu(p->Capability & RDMA_CAPABLE) ? 1 : 0; 5018c2ecf20Sopenharmony_ci info->rss_capable = le32_to_cpu(p->Capability & RSS_CAPABLE) ? 1 : 0; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: adding iface %zu\n", __func__, *iface_count); 5048c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: speed %zu bps\n", __func__, info->speed); 5058c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: capabilities 0x%08x\n", __func__, 5068c2ecf20Sopenharmony_ci le32_to_cpu(p->Capability)); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci switch (p->Family) { 5098c2ecf20Sopenharmony_ci /* 5108c2ecf20Sopenharmony_ci * The kernel and wire socket structures have the same 5118c2ecf20Sopenharmony_ci * layout and use network byte order but make the 5128c2ecf20Sopenharmony_ci * conversion explicit in case either one changes. 5138c2ecf20Sopenharmony_ci */ 5148c2ecf20Sopenharmony_ci case INTERNETWORK: 5158c2ecf20Sopenharmony_ci addr4 = (struct sockaddr_in *)&info->sockaddr; 5168c2ecf20Sopenharmony_ci p4 = (struct iface_info_ipv4 *)p->Buffer; 5178c2ecf20Sopenharmony_ci addr4->sin_family = AF_INET; 5188c2ecf20Sopenharmony_ci memcpy(&addr4->sin_addr, &p4->IPv4Address, 4); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci /* [MS-SMB2] 2.2.32.5.1.1 Clients MUST ignore these */ 5218c2ecf20Sopenharmony_ci addr4->sin_port = cpu_to_be16(CIFS_PORT); 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: ipv4 %pI4\n", __func__, 5248c2ecf20Sopenharmony_ci &addr4->sin_addr); 5258c2ecf20Sopenharmony_ci break; 5268c2ecf20Sopenharmony_ci case INTERNETWORKV6: 5278c2ecf20Sopenharmony_ci addr6 = (struct sockaddr_in6 *)&info->sockaddr; 5288c2ecf20Sopenharmony_ci p6 = (struct iface_info_ipv6 *)p->Buffer; 5298c2ecf20Sopenharmony_ci addr6->sin6_family = AF_INET6; 5308c2ecf20Sopenharmony_ci memcpy(&addr6->sin6_addr, &p6->IPv6Address, 16); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci /* [MS-SMB2] 2.2.32.5.1.2 Clients MUST ignore these */ 5338c2ecf20Sopenharmony_ci addr6->sin6_flowinfo = 0; 5348c2ecf20Sopenharmony_ci addr6->sin6_scope_id = 0; 5358c2ecf20Sopenharmony_ci addr6->sin6_port = cpu_to_be16(CIFS_PORT); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: ipv6 %pI6\n", __func__, 5388c2ecf20Sopenharmony_ci &addr6->sin6_addr); 5398c2ecf20Sopenharmony_ci break; 5408c2ecf20Sopenharmony_ci default: 5418c2ecf20Sopenharmony_ci cifs_dbg(VFS, 5428c2ecf20Sopenharmony_ci "%s: skipping unsupported socket family\n", 5438c2ecf20Sopenharmony_ci __func__); 5448c2ecf20Sopenharmony_ci goto next_iface; 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci (*iface_count)++; 5488c2ecf20Sopenharmony_ci info++; 5498c2ecf20Sopenharmony_cinext_iface: 5508c2ecf20Sopenharmony_ci next = le32_to_cpu(p->Next); 5518c2ecf20Sopenharmony_ci if (!next) 5528c2ecf20Sopenharmony_ci break; 5538c2ecf20Sopenharmony_ci p = (struct network_interface_info_ioctl_rsp *)((u8 *)p+next); 5548c2ecf20Sopenharmony_ci bytes_left -= next; 5558c2ecf20Sopenharmony_ci } 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci if (!*iface_count) { 5588c2ecf20Sopenharmony_ci rc = -EINVAL; 5598c2ecf20Sopenharmony_ci goto out; 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ciout: 5638c2ecf20Sopenharmony_ci if (rc) { 5648c2ecf20Sopenharmony_ci kfree(*iface_list); 5658c2ecf20Sopenharmony_ci *iface_count = 0; 5668c2ecf20Sopenharmony_ci *iface_list = NULL; 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci return rc; 5698c2ecf20Sopenharmony_ci} 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_cistatic int compare_iface(const void *ia, const void *ib) 5728c2ecf20Sopenharmony_ci{ 5738c2ecf20Sopenharmony_ci const struct cifs_server_iface *a = (struct cifs_server_iface *)ia; 5748c2ecf20Sopenharmony_ci const struct cifs_server_iface *b = (struct cifs_server_iface *)ib; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci return a->speed == b->speed ? 0 : (a->speed > b->speed ? -1 : 1); 5778c2ecf20Sopenharmony_ci} 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_cistatic int 5808c2ecf20Sopenharmony_ciSMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon) 5818c2ecf20Sopenharmony_ci{ 5828c2ecf20Sopenharmony_ci int rc; 5838c2ecf20Sopenharmony_ci unsigned int ret_data_len = 0; 5848c2ecf20Sopenharmony_ci struct network_interface_info_ioctl_rsp *out_buf = NULL; 5858c2ecf20Sopenharmony_ci struct cifs_server_iface *iface_list; 5868c2ecf20Sopenharmony_ci size_t iface_count; 5878c2ecf20Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, 5908c2ecf20Sopenharmony_ci FSCTL_QUERY_NETWORK_INTERFACE_INFO, 5918c2ecf20Sopenharmony_ci NULL /* no data input */, 0 /* no data input */, 5928c2ecf20Sopenharmony_ci CIFSMaxBufSize, (char **)&out_buf, &ret_data_len); 5938c2ecf20Sopenharmony_ci if (rc == -EOPNOTSUPP) { 5948c2ecf20Sopenharmony_ci cifs_dbg(FYI, 5958c2ecf20Sopenharmony_ci "server does not support query network interfaces\n"); 5968c2ecf20Sopenharmony_ci ret_data_len = 0; 5978c2ecf20Sopenharmony_ci } else if (rc != 0) { 5988c2ecf20Sopenharmony_ci cifs_tcon_dbg(VFS, "error %d on ioctl to get interface list\n", rc); 5998c2ecf20Sopenharmony_ci goto out; 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci rc = parse_server_interfaces(out_buf, ret_data_len, 6038c2ecf20Sopenharmony_ci &iface_list, &iface_count); 6048c2ecf20Sopenharmony_ci if (rc) 6058c2ecf20Sopenharmony_ci goto out; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci /* sort interfaces from fastest to slowest */ 6088c2ecf20Sopenharmony_ci sort(iface_list, iface_count, sizeof(*iface_list), compare_iface, NULL); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci spin_lock(&ses->iface_lock); 6118c2ecf20Sopenharmony_ci kfree(ses->iface_list); 6128c2ecf20Sopenharmony_ci ses->iface_list = iface_list; 6138c2ecf20Sopenharmony_ci ses->iface_count = iface_count; 6148c2ecf20Sopenharmony_ci ses->iface_last_update = jiffies; 6158c2ecf20Sopenharmony_ci spin_unlock(&ses->iface_lock); 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ciout: 6188c2ecf20Sopenharmony_ci kfree(out_buf); 6198c2ecf20Sopenharmony_ci return rc; 6208c2ecf20Sopenharmony_ci} 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_cistatic void 6238c2ecf20Sopenharmony_cismb2_close_cached_fid(struct kref *ref) 6248c2ecf20Sopenharmony_ci{ 6258c2ecf20Sopenharmony_ci struct cached_fid *cfid = container_of(ref, struct cached_fid, 6268c2ecf20Sopenharmony_ci refcount); 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci if (cfid->is_valid) { 6298c2ecf20Sopenharmony_ci cifs_dbg(FYI, "clear cached root file handle\n"); 6308c2ecf20Sopenharmony_ci SMB2_close(0, cfid->tcon, cfid->fid->persistent_fid, 6318c2ecf20Sopenharmony_ci cfid->fid->volatile_fid); 6328c2ecf20Sopenharmony_ci cfid->is_valid = false; 6338c2ecf20Sopenharmony_ci cfid->file_all_info_is_valid = false; 6348c2ecf20Sopenharmony_ci cfid->has_lease = false; 6358c2ecf20Sopenharmony_ci } 6368c2ecf20Sopenharmony_ci} 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_civoid close_shroot(struct cached_fid *cfid) 6398c2ecf20Sopenharmony_ci{ 6408c2ecf20Sopenharmony_ci mutex_lock(&cfid->fid_mutex); 6418c2ecf20Sopenharmony_ci kref_put(&cfid->refcount, smb2_close_cached_fid); 6428c2ecf20Sopenharmony_ci mutex_unlock(&cfid->fid_mutex); 6438c2ecf20Sopenharmony_ci} 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_civoid close_shroot_lease_locked(struct cached_fid *cfid) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci if (cfid->has_lease) { 6488c2ecf20Sopenharmony_ci cfid->has_lease = false; 6498c2ecf20Sopenharmony_ci kref_put(&cfid->refcount, smb2_close_cached_fid); 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci} 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_civoid close_shroot_lease(struct cached_fid *cfid) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci mutex_lock(&cfid->fid_mutex); 6568c2ecf20Sopenharmony_ci close_shroot_lease_locked(cfid); 6578c2ecf20Sopenharmony_ci mutex_unlock(&cfid->fid_mutex); 6588c2ecf20Sopenharmony_ci} 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_civoid 6618c2ecf20Sopenharmony_cismb2_cached_lease_break(struct work_struct *work) 6628c2ecf20Sopenharmony_ci{ 6638c2ecf20Sopenharmony_ci struct cached_fid *cfid = container_of(work, 6648c2ecf20Sopenharmony_ci struct cached_fid, lease_break); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci close_shroot_lease(cfid); 6678c2ecf20Sopenharmony_ci} 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci/* 6708c2ecf20Sopenharmony_ci * Open the directory at the root of a share 6718c2ecf20Sopenharmony_ci */ 6728c2ecf20Sopenharmony_ciint open_shroot(unsigned int xid, struct cifs_tcon *tcon, 6738c2ecf20Sopenharmony_ci struct cifs_sb_info *cifs_sb, 6748c2ecf20Sopenharmony_ci struct cached_fid **cfid) 6758c2ecf20Sopenharmony_ci{ 6768c2ecf20Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 6778c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = ses->server; 6788c2ecf20Sopenharmony_ci struct cifs_open_parms oparms; 6798c2ecf20Sopenharmony_ci struct smb2_create_rsp *o_rsp = NULL; 6808c2ecf20Sopenharmony_ci struct smb2_query_info_rsp *qi_rsp = NULL; 6818c2ecf20Sopenharmony_ci int resp_buftype[2]; 6828c2ecf20Sopenharmony_ci struct smb_rqst rqst[2]; 6838c2ecf20Sopenharmony_ci struct kvec rsp_iov[2]; 6848c2ecf20Sopenharmony_ci struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; 6858c2ecf20Sopenharmony_ci struct kvec qi_iov[1]; 6868c2ecf20Sopenharmony_ci int rc, flags = 0; 6878c2ecf20Sopenharmony_ci __le16 utf16_path = 0; /* Null - since an open of top of share */ 6888c2ecf20Sopenharmony_ci u8 oplock = SMB2_OPLOCK_LEVEL_II; 6898c2ecf20Sopenharmony_ci struct cifs_fid *pfid; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci mutex_lock(&tcon->crfid.fid_mutex); 6928c2ecf20Sopenharmony_ci if (tcon->crfid.is_valid) { 6938c2ecf20Sopenharmony_ci cifs_dbg(FYI, "found a cached root file handle\n"); 6948c2ecf20Sopenharmony_ci *cfid = &tcon->crfid; 6958c2ecf20Sopenharmony_ci kref_get(&tcon->crfid.refcount); 6968c2ecf20Sopenharmony_ci mutex_unlock(&tcon->crfid.fid_mutex); 6978c2ecf20Sopenharmony_ci return 0; 6988c2ecf20Sopenharmony_ci } 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci /* 7018c2ecf20Sopenharmony_ci * We do not hold the lock for the open because in case 7028c2ecf20Sopenharmony_ci * SMB2_open needs to reconnect, it will end up calling 7038c2ecf20Sopenharmony_ci * cifs_mark_open_files_invalid() which takes the lock again 7048c2ecf20Sopenharmony_ci * thus causing a deadlock 7058c2ecf20Sopenharmony_ci */ 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci mutex_unlock(&tcon->crfid.fid_mutex); 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci if (smb3_encryption_required(tcon)) 7108c2ecf20Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci if (!server->ops->new_lease_key) 7138c2ecf20Sopenharmony_ci return -EIO; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci pfid = tcon->crfid.fid; 7168c2ecf20Sopenharmony_ci server->ops->new_lease_key(pfid); 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci memset(rqst, 0, sizeof(rqst)); 7198c2ecf20Sopenharmony_ci resp_buftype[0] = resp_buftype[1] = CIFS_NO_BUFFER; 7208c2ecf20Sopenharmony_ci memset(rsp_iov, 0, sizeof(rsp_iov)); 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci /* Open */ 7238c2ecf20Sopenharmony_ci memset(&open_iov, 0, sizeof(open_iov)); 7248c2ecf20Sopenharmony_ci rqst[0].rq_iov = open_iov; 7258c2ecf20Sopenharmony_ci rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci oparms.tcon = tcon; 7288c2ecf20Sopenharmony_ci oparms.create_options = cifs_create_options(cifs_sb, 0); 7298c2ecf20Sopenharmony_ci oparms.desired_access = FILE_READ_ATTRIBUTES; 7308c2ecf20Sopenharmony_ci oparms.disposition = FILE_OPEN; 7318c2ecf20Sopenharmony_ci oparms.fid = pfid; 7328c2ecf20Sopenharmony_ci oparms.reconnect = false; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci rc = SMB2_open_init(tcon, server, 7358c2ecf20Sopenharmony_ci &rqst[0], &oplock, &oparms, &utf16_path); 7368c2ecf20Sopenharmony_ci if (rc) 7378c2ecf20Sopenharmony_ci goto oshr_free; 7388c2ecf20Sopenharmony_ci smb2_set_next_command(tcon, &rqst[0]); 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci memset(&qi_iov, 0, sizeof(qi_iov)); 7418c2ecf20Sopenharmony_ci rqst[1].rq_iov = qi_iov; 7428c2ecf20Sopenharmony_ci rqst[1].rq_nvec = 1; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci rc = SMB2_query_info_init(tcon, server, 7458c2ecf20Sopenharmony_ci &rqst[1], COMPOUND_FID, 7468c2ecf20Sopenharmony_ci COMPOUND_FID, FILE_ALL_INFORMATION, 7478c2ecf20Sopenharmony_ci SMB2_O_INFO_FILE, 0, 7488c2ecf20Sopenharmony_ci sizeof(struct smb2_file_all_info) + 7498c2ecf20Sopenharmony_ci PATH_MAX * 2, 0, NULL); 7508c2ecf20Sopenharmony_ci if (rc) 7518c2ecf20Sopenharmony_ci goto oshr_free; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci smb2_set_related(&rqst[1]); 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci rc = compound_send_recv(xid, ses, server, 7568c2ecf20Sopenharmony_ci flags, 2, rqst, 7578c2ecf20Sopenharmony_ci resp_buftype, rsp_iov); 7588c2ecf20Sopenharmony_ci mutex_lock(&tcon->crfid.fid_mutex); 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci /* 7618c2ecf20Sopenharmony_ci * Now we need to check again as the cached root might have 7628c2ecf20Sopenharmony_ci * been successfully re-opened from a concurrent process 7638c2ecf20Sopenharmony_ci */ 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci if (tcon->crfid.is_valid) { 7668c2ecf20Sopenharmony_ci /* work was already done */ 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci /* stash fids for close() later */ 7698c2ecf20Sopenharmony_ci struct cifs_fid fid = { 7708c2ecf20Sopenharmony_ci .persistent_fid = pfid->persistent_fid, 7718c2ecf20Sopenharmony_ci .volatile_fid = pfid->volatile_fid, 7728c2ecf20Sopenharmony_ci }; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci /* 7758c2ecf20Sopenharmony_ci * caller expects this func to set pfid to a valid 7768c2ecf20Sopenharmony_ci * cached root, so we copy the existing one and get a 7778c2ecf20Sopenharmony_ci * reference. 7788c2ecf20Sopenharmony_ci */ 7798c2ecf20Sopenharmony_ci memcpy(pfid, tcon->crfid.fid, sizeof(*pfid)); 7808c2ecf20Sopenharmony_ci kref_get(&tcon->crfid.refcount); 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci mutex_unlock(&tcon->crfid.fid_mutex); 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci if (rc == 0) { 7858c2ecf20Sopenharmony_ci /* close extra handle outside of crit sec */ 7868c2ecf20Sopenharmony_ci SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); 7878c2ecf20Sopenharmony_ci } 7888c2ecf20Sopenharmony_ci rc = 0; 7898c2ecf20Sopenharmony_ci goto oshr_free; 7908c2ecf20Sopenharmony_ci } 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci /* Cached root is still invalid, continue normaly */ 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci if (rc) { 7958c2ecf20Sopenharmony_ci if (rc == -EREMCHG) { 7968c2ecf20Sopenharmony_ci tcon->need_reconnect = true; 7978c2ecf20Sopenharmony_ci pr_warn_once("server share %s deleted\n", 7988c2ecf20Sopenharmony_ci tcon->treeName); 7998c2ecf20Sopenharmony_ci } 8008c2ecf20Sopenharmony_ci goto oshr_exit; 8018c2ecf20Sopenharmony_ci } 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci atomic_inc(&tcon->num_remote_opens); 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci o_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base; 8068c2ecf20Sopenharmony_ci oparms.fid->persistent_fid = o_rsp->PersistentFileId; 8078c2ecf20Sopenharmony_ci oparms.fid->volatile_fid = o_rsp->VolatileFileId; 8088c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_DEBUG2 8098c2ecf20Sopenharmony_ci oparms.fid->mid = le64_to_cpu(o_rsp->sync_hdr.MessageId); 8108c2ecf20Sopenharmony_ci#endif /* CIFS_DEBUG2 */ 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci memcpy(tcon->crfid.fid, pfid, sizeof(struct cifs_fid)); 8138c2ecf20Sopenharmony_ci tcon->crfid.tcon = tcon; 8148c2ecf20Sopenharmony_ci tcon->crfid.is_valid = true; 8158c2ecf20Sopenharmony_ci kref_init(&tcon->crfid.refcount); 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci /* BB TBD check to see if oplock level check can be removed below */ 8188c2ecf20Sopenharmony_ci if (o_rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE) { 8198c2ecf20Sopenharmony_ci kref_get(&tcon->crfid.refcount); 8208c2ecf20Sopenharmony_ci tcon->crfid.has_lease = true; 8218c2ecf20Sopenharmony_ci smb2_parse_contexts(server, o_rsp, 8228c2ecf20Sopenharmony_ci &oparms.fid->epoch, 8238c2ecf20Sopenharmony_ci oparms.fid->lease_key, &oplock, 8248c2ecf20Sopenharmony_ci NULL, NULL); 8258c2ecf20Sopenharmony_ci } else 8268c2ecf20Sopenharmony_ci goto oshr_exit; 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base; 8298c2ecf20Sopenharmony_ci if (le32_to_cpu(qi_rsp->OutputBufferLength) < sizeof(struct smb2_file_all_info)) 8308c2ecf20Sopenharmony_ci goto oshr_exit; 8318c2ecf20Sopenharmony_ci if (!smb2_validate_and_copy_iov( 8328c2ecf20Sopenharmony_ci le16_to_cpu(qi_rsp->OutputBufferOffset), 8338c2ecf20Sopenharmony_ci sizeof(struct smb2_file_all_info), 8348c2ecf20Sopenharmony_ci &rsp_iov[1], sizeof(struct smb2_file_all_info), 8358c2ecf20Sopenharmony_ci (char *)&tcon->crfid.file_all_info)) 8368c2ecf20Sopenharmony_ci tcon->crfid.file_all_info_is_valid = true; 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_cioshr_exit: 8398c2ecf20Sopenharmony_ci mutex_unlock(&tcon->crfid.fid_mutex); 8408c2ecf20Sopenharmony_cioshr_free: 8418c2ecf20Sopenharmony_ci SMB2_open_free(&rqst[0]); 8428c2ecf20Sopenharmony_ci SMB2_query_info_free(&rqst[1]); 8438c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); 8448c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); 8458c2ecf20Sopenharmony_ci if (rc == 0) 8468c2ecf20Sopenharmony_ci *cfid = &tcon->crfid; 8478c2ecf20Sopenharmony_ci return rc; 8488c2ecf20Sopenharmony_ci} 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_cistatic void 8518c2ecf20Sopenharmony_cismb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon, 8528c2ecf20Sopenharmony_ci struct cifs_sb_info *cifs_sb) 8538c2ecf20Sopenharmony_ci{ 8548c2ecf20Sopenharmony_ci int rc; 8558c2ecf20Sopenharmony_ci __le16 srch_path = 0; /* Null - open root of share */ 8568c2ecf20Sopenharmony_ci u8 oplock = SMB2_OPLOCK_LEVEL_NONE; 8578c2ecf20Sopenharmony_ci struct cifs_open_parms oparms; 8588c2ecf20Sopenharmony_ci struct cifs_fid fid; 8598c2ecf20Sopenharmony_ci bool no_cached_open = tcon->nohandlecache; 8608c2ecf20Sopenharmony_ci struct cached_fid *cfid = NULL; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci oparms = (struct cifs_open_parms) { 8638c2ecf20Sopenharmony_ci .tcon = tcon, 8648c2ecf20Sopenharmony_ci .desired_access = FILE_READ_ATTRIBUTES, 8658c2ecf20Sopenharmony_ci .disposition = FILE_OPEN, 8668c2ecf20Sopenharmony_ci .create_options = cifs_create_options(cifs_sb, 0), 8678c2ecf20Sopenharmony_ci .fid = &fid, 8688c2ecf20Sopenharmony_ci }; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci if (no_cached_open) { 8718c2ecf20Sopenharmony_ci rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL, 8728c2ecf20Sopenharmony_ci NULL, NULL); 8738c2ecf20Sopenharmony_ci } else { 8748c2ecf20Sopenharmony_ci rc = open_shroot(xid, tcon, cifs_sb, &cfid); 8758c2ecf20Sopenharmony_ci if (rc == 0) 8768c2ecf20Sopenharmony_ci memcpy(&fid, cfid->fid, sizeof(struct cifs_fid)); 8778c2ecf20Sopenharmony_ci } 8788c2ecf20Sopenharmony_ci if (rc) 8798c2ecf20Sopenharmony_ci return; 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci SMB3_request_interfaces(xid, tcon); 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, 8848c2ecf20Sopenharmony_ci FS_ATTRIBUTE_INFORMATION); 8858c2ecf20Sopenharmony_ci SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, 8868c2ecf20Sopenharmony_ci FS_DEVICE_INFORMATION); 8878c2ecf20Sopenharmony_ci SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, 8888c2ecf20Sopenharmony_ci FS_VOLUME_INFORMATION); 8898c2ecf20Sopenharmony_ci SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, 8908c2ecf20Sopenharmony_ci FS_SECTOR_SIZE_INFORMATION); /* SMB3 specific */ 8918c2ecf20Sopenharmony_ci if (no_cached_open) 8928c2ecf20Sopenharmony_ci SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); 8938c2ecf20Sopenharmony_ci else 8948c2ecf20Sopenharmony_ci close_shroot(cfid); 8958c2ecf20Sopenharmony_ci} 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_cistatic void 8988c2ecf20Sopenharmony_cismb2_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon, 8998c2ecf20Sopenharmony_ci struct cifs_sb_info *cifs_sb) 9008c2ecf20Sopenharmony_ci{ 9018c2ecf20Sopenharmony_ci int rc; 9028c2ecf20Sopenharmony_ci __le16 srch_path = 0; /* Null - open root of share */ 9038c2ecf20Sopenharmony_ci u8 oplock = SMB2_OPLOCK_LEVEL_NONE; 9048c2ecf20Sopenharmony_ci struct cifs_open_parms oparms; 9058c2ecf20Sopenharmony_ci struct cifs_fid fid; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci oparms.tcon = tcon; 9088c2ecf20Sopenharmony_ci oparms.desired_access = FILE_READ_ATTRIBUTES; 9098c2ecf20Sopenharmony_ci oparms.disposition = FILE_OPEN; 9108c2ecf20Sopenharmony_ci oparms.create_options = cifs_create_options(cifs_sb, 0); 9118c2ecf20Sopenharmony_ci oparms.fid = &fid; 9128c2ecf20Sopenharmony_ci oparms.reconnect = false; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL, 9158c2ecf20Sopenharmony_ci NULL, NULL); 9168c2ecf20Sopenharmony_ci if (rc) 9178c2ecf20Sopenharmony_ci return; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, 9208c2ecf20Sopenharmony_ci FS_ATTRIBUTE_INFORMATION); 9218c2ecf20Sopenharmony_ci SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, 9228c2ecf20Sopenharmony_ci FS_DEVICE_INFORMATION); 9238c2ecf20Sopenharmony_ci SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); 9248c2ecf20Sopenharmony_ci} 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_cistatic int 9278c2ecf20Sopenharmony_cismb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon, 9288c2ecf20Sopenharmony_ci struct cifs_sb_info *cifs_sb, const char *full_path) 9298c2ecf20Sopenharmony_ci{ 9308c2ecf20Sopenharmony_ci int rc; 9318c2ecf20Sopenharmony_ci __le16 *utf16_path; 9328c2ecf20Sopenharmony_ci __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; 9338c2ecf20Sopenharmony_ci struct cifs_open_parms oparms; 9348c2ecf20Sopenharmony_ci struct cifs_fid fid; 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci if ((*full_path == 0) && tcon->crfid.is_valid) 9378c2ecf20Sopenharmony_ci return 0; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); 9408c2ecf20Sopenharmony_ci if (!utf16_path) 9418c2ecf20Sopenharmony_ci return -ENOMEM; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci oparms.tcon = tcon; 9448c2ecf20Sopenharmony_ci oparms.desired_access = FILE_READ_ATTRIBUTES; 9458c2ecf20Sopenharmony_ci oparms.disposition = FILE_OPEN; 9468c2ecf20Sopenharmony_ci oparms.create_options = cifs_create_options(cifs_sb, 0); 9478c2ecf20Sopenharmony_ci oparms.fid = &fid; 9488c2ecf20Sopenharmony_ci oparms.reconnect = false; 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL, 9518c2ecf20Sopenharmony_ci NULL); 9528c2ecf20Sopenharmony_ci if (rc) { 9538c2ecf20Sopenharmony_ci kfree(utf16_path); 9548c2ecf20Sopenharmony_ci return rc; 9558c2ecf20Sopenharmony_ci } 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci rc = SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); 9588c2ecf20Sopenharmony_ci kfree(utf16_path); 9598c2ecf20Sopenharmony_ci return rc; 9608c2ecf20Sopenharmony_ci} 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_cistatic int 9638c2ecf20Sopenharmony_cismb2_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon, 9648c2ecf20Sopenharmony_ci struct cifs_sb_info *cifs_sb, const char *full_path, 9658c2ecf20Sopenharmony_ci u64 *uniqueid, FILE_ALL_INFO *data) 9668c2ecf20Sopenharmony_ci{ 9678c2ecf20Sopenharmony_ci *uniqueid = le64_to_cpu(data->IndexNumber); 9688c2ecf20Sopenharmony_ci return 0; 9698c2ecf20Sopenharmony_ci} 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_cistatic int 9728c2ecf20Sopenharmony_cismb2_query_file_info(const unsigned int xid, struct cifs_tcon *tcon, 9738c2ecf20Sopenharmony_ci struct cifs_fid *fid, FILE_ALL_INFO *data) 9748c2ecf20Sopenharmony_ci{ 9758c2ecf20Sopenharmony_ci int rc; 9768c2ecf20Sopenharmony_ci struct smb2_file_all_info *smb2_data; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci smb2_data = kzalloc(sizeof(struct smb2_file_all_info) + PATH_MAX * 2, 9798c2ecf20Sopenharmony_ci GFP_KERNEL); 9808c2ecf20Sopenharmony_ci if (smb2_data == NULL) 9818c2ecf20Sopenharmony_ci return -ENOMEM; 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci rc = SMB2_query_info(xid, tcon, fid->persistent_fid, fid->volatile_fid, 9848c2ecf20Sopenharmony_ci smb2_data); 9858c2ecf20Sopenharmony_ci if (!rc) 9868c2ecf20Sopenharmony_ci move_smb2_info_to_cifs(data, smb2_data); 9878c2ecf20Sopenharmony_ci kfree(smb2_data); 9888c2ecf20Sopenharmony_ci return rc; 9898c2ecf20Sopenharmony_ci} 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_XATTR 9928c2ecf20Sopenharmony_cistatic ssize_t 9938c2ecf20Sopenharmony_cimove_smb2_ea_to_cifs(char *dst, size_t dst_size, 9948c2ecf20Sopenharmony_ci struct smb2_file_full_ea_info *src, size_t src_size, 9958c2ecf20Sopenharmony_ci const unsigned char *ea_name) 9968c2ecf20Sopenharmony_ci{ 9978c2ecf20Sopenharmony_ci int rc = 0; 9988c2ecf20Sopenharmony_ci unsigned int ea_name_len = ea_name ? strlen(ea_name) : 0; 9998c2ecf20Sopenharmony_ci char *name, *value; 10008c2ecf20Sopenharmony_ci size_t buf_size = dst_size; 10018c2ecf20Sopenharmony_ci size_t name_len, value_len, user_name_len; 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci while (src_size > 0) { 10048c2ecf20Sopenharmony_ci name_len = (size_t)src->ea_name_length; 10058c2ecf20Sopenharmony_ci value_len = (size_t)le16_to_cpu(src->ea_value_length); 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci if (name_len == 0) 10088c2ecf20Sopenharmony_ci break; 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci if (src_size < 8 + name_len + 1 + value_len) { 10118c2ecf20Sopenharmony_ci cifs_dbg(FYI, "EA entry goes beyond length of list\n"); 10128c2ecf20Sopenharmony_ci rc = -EIO; 10138c2ecf20Sopenharmony_ci goto out; 10148c2ecf20Sopenharmony_ci } 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci name = &src->ea_data[0]; 10178c2ecf20Sopenharmony_ci value = &src->ea_data[src->ea_name_length + 1]; 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci if (ea_name) { 10208c2ecf20Sopenharmony_ci if (ea_name_len == name_len && 10218c2ecf20Sopenharmony_ci memcmp(ea_name, name, name_len) == 0) { 10228c2ecf20Sopenharmony_ci rc = value_len; 10238c2ecf20Sopenharmony_ci if (dst_size == 0) 10248c2ecf20Sopenharmony_ci goto out; 10258c2ecf20Sopenharmony_ci if (dst_size < value_len) { 10268c2ecf20Sopenharmony_ci rc = -ERANGE; 10278c2ecf20Sopenharmony_ci goto out; 10288c2ecf20Sopenharmony_ci } 10298c2ecf20Sopenharmony_ci memcpy(dst, value, value_len); 10308c2ecf20Sopenharmony_ci goto out; 10318c2ecf20Sopenharmony_ci } 10328c2ecf20Sopenharmony_ci } else { 10338c2ecf20Sopenharmony_ci /* 'user.' plus a terminating null */ 10348c2ecf20Sopenharmony_ci user_name_len = 5 + 1 + name_len; 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci if (buf_size == 0) { 10378c2ecf20Sopenharmony_ci /* skip copy - calc size only */ 10388c2ecf20Sopenharmony_ci rc += user_name_len; 10398c2ecf20Sopenharmony_ci } else if (dst_size >= user_name_len) { 10408c2ecf20Sopenharmony_ci dst_size -= user_name_len; 10418c2ecf20Sopenharmony_ci memcpy(dst, "user.", 5); 10428c2ecf20Sopenharmony_ci dst += 5; 10438c2ecf20Sopenharmony_ci memcpy(dst, src->ea_data, name_len); 10448c2ecf20Sopenharmony_ci dst += name_len; 10458c2ecf20Sopenharmony_ci *dst = 0; 10468c2ecf20Sopenharmony_ci ++dst; 10478c2ecf20Sopenharmony_ci rc += user_name_len; 10488c2ecf20Sopenharmony_ci } else { 10498c2ecf20Sopenharmony_ci /* stop before overrun buffer */ 10508c2ecf20Sopenharmony_ci rc = -ERANGE; 10518c2ecf20Sopenharmony_ci break; 10528c2ecf20Sopenharmony_ci } 10538c2ecf20Sopenharmony_ci } 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci if (!src->next_entry_offset) 10568c2ecf20Sopenharmony_ci break; 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci if (src_size < le32_to_cpu(src->next_entry_offset)) { 10598c2ecf20Sopenharmony_ci /* stop before overrun buffer */ 10608c2ecf20Sopenharmony_ci rc = -ERANGE; 10618c2ecf20Sopenharmony_ci break; 10628c2ecf20Sopenharmony_ci } 10638c2ecf20Sopenharmony_ci src_size -= le32_to_cpu(src->next_entry_offset); 10648c2ecf20Sopenharmony_ci src = (void *)((char *)src + 10658c2ecf20Sopenharmony_ci le32_to_cpu(src->next_entry_offset)); 10668c2ecf20Sopenharmony_ci } 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci /* didn't find the named attribute */ 10698c2ecf20Sopenharmony_ci if (ea_name) 10708c2ecf20Sopenharmony_ci rc = -ENODATA; 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ciout: 10738c2ecf20Sopenharmony_ci return (ssize_t)rc; 10748c2ecf20Sopenharmony_ci} 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_cistatic ssize_t 10778c2ecf20Sopenharmony_cismb2_query_eas(const unsigned int xid, struct cifs_tcon *tcon, 10788c2ecf20Sopenharmony_ci const unsigned char *path, const unsigned char *ea_name, 10798c2ecf20Sopenharmony_ci char *ea_data, size_t buf_size, 10808c2ecf20Sopenharmony_ci struct cifs_sb_info *cifs_sb) 10818c2ecf20Sopenharmony_ci{ 10828c2ecf20Sopenharmony_ci int rc; 10838c2ecf20Sopenharmony_ci __le16 *utf16_path; 10848c2ecf20Sopenharmony_ci struct kvec rsp_iov = {NULL, 0}; 10858c2ecf20Sopenharmony_ci int buftype = CIFS_NO_BUFFER; 10868c2ecf20Sopenharmony_ci struct smb2_query_info_rsp *rsp; 10878c2ecf20Sopenharmony_ci struct smb2_file_full_ea_info *info = NULL; 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); 10908c2ecf20Sopenharmony_ci if (!utf16_path) 10918c2ecf20Sopenharmony_ci return -ENOMEM; 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci rc = smb2_query_info_compound(xid, tcon, utf16_path, 10948c2ecf20Sopenharmony_ci FILE_READ_EA, 10958c2ecf20Sopenharmony_ci FILE_FULL_EA_INFORMATION, 10968c2ecf20Sopenharmony_ci SMB2_O_INFO_FILE, 10978c2ecf20Sopenharmony_ci CIFSMaxBufSize - 10988c2ecf20Sopenharmony_ci MAX_SMB2_CREATE_RESPONSE_SIZE - 10998c2ecf20Sopenharmony_ci MAX_SMB2_CLOSE_RESPONSE_SIZE, 11008c2ecf20Sopenharmony_ci &rsp_iov, &buftype, cifs_sb); 11018c2ecf20Sopenharmony_ci if (rc) { 11028c2ecf20Sopenharmony_ci /* 11038c2ecf20Sopenharmony_ci * If ea_name is NULL (listxattr) and there are no EAs, 11048c2ecf20Sopenharmony_ci * return 0 as it's not an error. Otherwise, the specified 11058c2ecf20Sopenharmony_ci * ea_name was not found. 11068c2ecf20Sopenharmony_ci */ 11078c2ecf20Sopenharmony_ci if (!ea_name && rc == -ENODATA) 11088c2ecf20Sopenharmony_ci rc = 0; 11098c2ecf20Sopenharmony_ci goto qeas_exit; 11108c2ecf20Sopenharmony_ci } 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; 11138c2ecf20Sopenharmony_ci rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), 11148c2ecf20Sopenharmony_ci le32_to_cpu(rsp->OutputBufferLength), 11158c2ecf20Sopenharmony_ci &rsp_iov, 11168c2ecf20Sopenharmony_ci sizeof(struct smb2_file_full_ea_info)); 11178c2ecf20Sopenharmony_ci if (rc) 11188c2ecf20Sopenharmony_ci goto qeas_exit; 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci info = (struct smb2_file_full_ea_info *)( 11218c2ecf20Sopenharmony_ci le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp); 11228c2ecf20Sopenharmony_ci rc = move_smb2_ea_to_cifs(ea_data, buf_size, info, 11238c2ecf20Sopenharmony_ci le32_to_cpu(rsp->OutputBufferLength), ea_name); 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci qeas_exit: 11268c2ecf20Sopenharmony_ci kfree(utf16_path); 11278c2ecf20Sopenharmony_ci free_rsp_buf(buftype, rsp_iov.iov_base); 11288c2ecf20Sopenharmony_ci return rc; 11298c2ecf20Sopenharmony_ci} 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_cistatic int 11338c2ecf20Sopenharmony_cismb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, 11348c2ecf20Sopenharmony_ci const char *path, const char *ea_name, const void *ea_value, 11358c2ecf20Sopenharmony_ci const __u16 ea_value_len, const struct nls_table *nls_codepage, 11368c2ecf20Sopenharmony_ci struct cifs_sb_info *cifs_sb) 11378c2ecf20Sopenharmony_ci{ 11388c2ecf20Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 11398c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(ses); 11408c2ecf20Sopenharmony_ci __le16 *utf16_path = NULL; 11418c2ecf20Sopenharmony_ci int ea_name_len = strlen(ea_name); 11428c2ecf20Sopenharmony_ci int flags = CIFS_CP_CREATE_CLOSE_OP; 11438c2ecf20Sopenharmony_ci int len; 11448c2ecf20Sopenharmony_ci struct smb_rqst rqst[3]; 11458c2ecf20Sopenharmony_ci int resp_buftype[3]; 11468c2ecf20Sopenharmony_ci struct kvec rsp_iov[3]; 11478c2ecf20Sopenharmony_ci struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; 11488c2ecf20Sopenharmony_ci struct cifs_open_parms oparms; 11498c2ecf20Sopenharmony_ci __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; 11508c2ecf20Sopenharmony_ci struct cifs_fid fid; 11518c2ecf20Sopenharmony_ci struct kvec si_iov[SMB2_SET_INFO_IOV_SIZE]; 11528c2ecf20Sopenharmony_ci unsigned int size[1]; 11538c2ecf20Sopenharmony_ci void *data[1]; 11548c2ecf20Sopenharmony_ci struct smb2_file_full_ea_info *ea = NULL; 11558c2ecf20Sopenharmony_ci struct kvec close_iov[1]; 11568c2ecf20Sopenharmony_ci struct smb2_query_info_rsp *rsp; 11578c2ecf20Sopenharmony_ci int rc, used_len = 0; 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci if (smb3_encryption_required(tcon)) 11608c2ecf20Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci if (ea_name_len > 255) 11638c2ecf20Sopenharmony_ci return -EINVAL; 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); 11668c2ecf20Sopenharmony_ci if (!utf16_path) 11678c2ecf20Sopenharmony_ci return -ENOMEM; 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci memset(rqst, 0, sizeof(rqst)); 11708c2ecf20Sopenharmony_ci resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; 11718c2ecf20Sopenharmony_ci memset(rsp_iov, 0, sizeof(rsp_iov)); 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci if (ses->server->ops->query_all_EAs) { 11748c2ecf20Sopenharmony_ci if (!ea_value) { 11758c2ecf20Sopenharmony_ci rc = ses->server->ops->query_all_EAs(xid, tcon, path, 11768c2ecf20Sopenharmony_ci ea_name, NULL, 0, 11778c2ecf20Sopenharmony_ci cifs_sb); 11788c2ecf20Sopenharmony_ci if (rc == -ENODATA) 11798c2ecf20Sopenharmony_ci goto sea_exit; 11808c2ecf20Sopenharmony_ci } else { 11818c2ecf20Sopenharmony_ci /* If we are adding a attribute we should first check 11828c2ecf20Sopenharmony_ci * if there will be enough space available to store 11838c2ecf20Sopenharmony_ci * the new EA. If not we should not add it since we 11848c2ecf20Sopenharmony_ci * would not be able to even read the EAs back. 11858c2ecf20Sopenharmony_ci */ 11868c2ecf20Sopenharmony_ci rc = smb2_query_info_compound(xid, tcon, utf16_path, 11878c2ecf20Sopenharmony_ci FILE_READ_EA, 11888c2ecf20Sopenharmony_ci FILE_FULL_EA_INFORMATION, 11898c2ecf20Sopenharmony_ci SMB2_O_INFO_FILE, 11908c2ecf20Sopenharmony_ci CIFSMaxBufSize - 11918c2ecf20Sopenharmony_ci MAX_SMB2_CREATE_RESPONSE_SIZE - 11928c2ecf20Sopenharmony_ci MAX_SMB2_CLOSE_RESPONSE_SIZE, 11938c2ecf20Sopenharmony_ci &rsp_iov[1], &resp_buftype[1], cifs_sb); 11948c2ecf20Sopenharmony_ci if (rc == 0) { 11958c2ecf20Sopenharmony_ci rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base; 11968c2ecf20Sopenharmony_ci used_len = le32_to_cpu(rsp->OutputBufferLength); 11978c2ecf20Sopenharmony_ci } 11988c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); 11998c2ecf20Sopenharmony_ci resp_buftype[1] = CIFS_NO_BUFFER; 12008c2ecf20Sopenharmony_ci memset(&rsp_iov[1], 0, sizeof(rsp_iov[1])); 12018c2ecf20Sopenharmony_ci rc = 0; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci /* Use a fudge factor of 256 bytes in case we collide 12048c2ecf20Sopenharmony_ci * with a different set_EAs command. 12058c2ecf20Sopenharmony_ci */ 12068c2ecf20Sopenharmony_ci if(CIFSMaxBufSize - MAX_SMB2_CREATE_RESPONSE_SIZE - 12078c2ecf20Sopenharmony_ci MAX_SMB2_CLOSE_RESPONSE_SIZE - 256 < 12088c2ecf20Sopenharmony_ci used_len + ea_name_len + ea_value_len + 1) { 12098c2ecf20Sopenharmony_ci rc = -ENOSPC; 12108c2ecf20Sopenharmony_ci goto sea_exit; 12118c2ecf20Sopenharmony_ci } 12128c2ecf20Sopenharmony_ci } 12138c2ecf20Sopenharmony_ci } 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci /* Open */ 12168c2ecf20Sopenharmony_ci memset(&open_iov, 0, sizeof(open_iov)); 12178c2ecf20Sopenharmony_ci rqst[0].rq_iov = open_iov; 12188c2ecf20Sopenharmony_ci rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci memset(&oparms, 0, sizeof(oparms)); 12218c2ecf20Sopenharmony_ci oparms.tcon = tcon; 12228c2ecf20Sopenharmony_ci oparms.desired_access = FILE_WRITE_EA; 12238c2ecf20Sopenharmony_ci oparms.disposition = FILE_OPEN; 12248c2ecf20Sopenharmony_ci oparms.create_options = cifs_create_options(cifs_sb, 0); 12258c2ecf20Sopenharmony_ci oparms.fid = &fid; 12268c2ecf20Sopenharmony_ci oparms.reconnect = false; 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci rc = SMB2_open_init(tcon, server, 12298c2ecf20Sopenharmony_ci &rqst[0], &oplock, &oparms, utf16_path); 12308c2ecf20Sopenharmony_ci if (rc) 12318c2ecf20Sopenharmony_ci goto sea_exit; 12328c2ecf20Sopenharmony_ci smb2_set_next_command(tcon, &rqst[0]); 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci /* Set Info */ 12368c2ecf20Sopenharmony_ci memset(&si_iov, 0, sizeof(si_iov)); 12378c2ecf20Sopenharmony_ci rqst[1].rq_iov = si_iov; 12388c2ecf20Sopenharmony_ci rqst[1].rq_nvec = 1; 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci len = sizeof(*ea) + ea_name_len + ea_value_len + 1; 12418c2ecf20Sopenharmony_ci ea = kzalloc(len, GFP_KERNEL); 12428c2ecf20Sopenharmony_ci if (ea == NULL) { 12438c2ecf20Sopenharmony_ci rc = -ENOMEM; 12448c2ecf20Sopenharmony_ci goto sea_exit; 12458c2ecf20Sopenharmony_ci } 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci ea->ea_name_length = ea_name_len; 12488c2ecf20Sopenharmony_ci ea->ea_value_length = cpu_to_le16(ea_value_len); 12498c2ecf20Sopenharmony_ci memcpy(ea->ea_data, ea_name, ea_name_len + 1); 12508c2ecf20Sopenharmony_ci memcpy(ea->ea_data + ea_name_len + 1, ea_value, ea_value_len); 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci size[0] = len; 12538c2ecf20Sopenharmony_ci data[0] = ea; 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci rc = SMB2_set_info_init(tcon, server, 12568c2ecf20Sopenharmony_ci &rqst[1], COMPOUND_FID, 12578c2ecf20Sopenharmony_ci COMPOUND_FID, current->tgid, 12588c2ecf20Sopenharmony_ci FILE_FULL_EA_INFORMATION, 12598c2ecf20Sopenharmony_ci SMB2_O_INFO_FILE, 0, data, size); 12608c2ecf20Sopenharmony_ci if (rc) 12618c2ecf20Sopenharmony_ci goto sea_exit; 12628c2ecf20Sopenharmony_ci smb2_set_next_command(tcon, &rqst[1]); 12638c2ecf20Sopenharmony_ci smb2_set_related(&rqst[1]); 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci /* Close */ 12678c2ecf20Sopenharmony_ci memset(&close_iov, 0, sizeof(close_iov)); 12688c2ecf20Sopenharmony_ci rqst[2].rq_iov = close_iov; 12698c2ecf20Sopenharmony_ci rqst[2].rq_nvec = 1; 12708c2ecf20Sopenharmony_ci rc = SMB2_close_init(tcon, server, 12718c2ecf20Sopenharmony_ci &rqst[2], COMPOUND_FID, COMPOUND_FID, false); 12728c2ecf20Sopenharmony_ci if (rc) 12738c2ecf20Sopenharmony_ci goto sea_exit; 12748c2ecf20Sopenharmony_ci smb2_set_related(&rqst[2]); 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci rc = compound_send_recv(xid, ses, server, 12778c2ecf20Sopenharmony_ci flags, 3, rqst, 12788c2ecf20Sopenharmony_ci resp_buftype, rsp_iov); 12798c2ecf20Sopenharmony_ci /* no need to bump num_remote_opens because handle immediately closed */ 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci sea_exit: 12828c2ecf20Sopenharmony_ci kfree(ea); 12838c2ecf20Sopenharmony_ci kfree(utf16_path); 12848c2ecf20Sopenharmony_ci SMB2_open_free(&rqst[0]); 12858c2ecf20Sopenharmony_ci SMB2_set_info_free(&rqst[1]); 12868c2ecf20Sopenharmony_ci SMB2_close_free(&rqst[2]); 12878c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); 12888c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); 12898c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); 12908c2ecf20Sopenharmony_ci return rc; 12918c2ecf20Sopenharmony_ci} 12928c2ecf20Sopenharmony_ci#endif 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_cistatic bool 12958c2ecf20Sopenharmony_cismb2_can_echo(struct TCP_Server_Info *server) 12968c2ecf20Sopenharmony_ci{ 12978c2ecf20Sopenharmony_ci return server->echoes; 12988c2ecf20Sopenharmony_ci} 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_cistatic void 13018c2ecf20Sopenharmony_cismb2_clear_stats(struct cifs_tcon *tcon) 13028c2ecf20Sopenharmony_ci{ 13038c2ecf20Sopenharmony_ci int i; 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci for (i = 0; i < NUMBER_OF_SMB2_COMMANDS; i++) { 13068c2ecf20Sopenharmony_ci atomic_set(&tcon->stats.smb2_stats.smb2_com_sent[i], 0); 13078c2ecf20Sopenharmony_ci atomic_set(&tcon->stats.smb2_stats.smb2_com_failed[i], 0); 13088c2ecf20Sopenharmony_ci } 13098c2ecf20Sopenharmony_ci} 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_cistatic void 13128c2ecf20Sopenharmony_cismb2_dump_share_caps(struct seq_file *m, struct cifs_tcon *tcon) 13138c2ecf20Sopenharmony_ci{ 13148c2ecf20Sopenharmony_ci seq_puts(m, "\n\tShare Capabilities:"); 13158c2ecf20Sopenharmony_ci if (tcon->capabilities & SMB2_SHARE_CAP_DFS) 13168c2ecf20Sopenharmony_ci seq_puts(m, " DFS,"); 13178c2ecf20Sopenharmony_ci if (tcon->capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY) 13188c2ecf20Sopenharmony_ci seq_puts(m, " CONTINUOUS AVAILABILITY,"); 13198c2ecf20Sopenharmony_ci if (tcon->capabilities & SMB2_SHARE_CAP_SCALEOUT) 13208c2ecf20Sopenharmony_ci seq_puts(m, " SCALEOUT,"); 13218c2ecf20Sopenharmony_ci if (tcon->capabilities & SMB2_SHARE_CAP_CLUSTER) 13228c2ecf20Sopenharmony_ci seq_puts(m, " CLUSTER,"); 13238c2ecf20Sopenharmony_ci if (tcon->capabilities & SMB2_SHARE_CAP_ASYMMETRIC) 13248c2ecf20Sopenharmony_ci seq_puts(m, " ASYMMETRIC,"); 13258c2ecf20Sopenharmony_ci if (tcon->capabilities == 0) 13268c2ecf20Sopenharmony_ci seq_puts(m, " None"); 13278c2ecf20Sopenharmony_ci if (tcon->ss_flags & SSINFO_FLAGS_ALIGNED_DEVICE) 13288c2ecf20Sopenharmony_ci seq_puts(m, " Aligned,"); 13298c2ecf20Sopenharmony_ci if (tcon->ss_flags & SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE) 13308c2ecf20Sopenharmony_ci seq_puts(m, " Partition Aligned,"); 13318c2ecf20Sopenharmony_ci if (tcon->ss_flags & SSINFO_FLAGS_NO_SEEK_PENALTY) 13328c2ecf20Sopenharmony_ci seq_puts(m, " SSD,"); 13338c2ecf20Sopenharmony_ci if (tcon->ss_flags & SSINFO_FLAGS_TRIM_ENABLED) 13348c2ecf20Sopenharmony_ci seq_puts(m, " TRIM-support,"); 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci seq_printf(m, "\tShare Flags: 0x%x", tcon->share_flags); 13378c2ecf20Sopenharmony_ci seq_printf(m, "\n\ttid: 0x%x", tcon->tid); 13388c2ecf20Sopenharmony_ci if (tcon->perf_sector_size) 13398c2ecf20Sopenharmony_ci seq_printf(m, "\tOptimal sector size: 0x%x", 13408c2ecf20Sopenharmony_ci tcon->perf_sector_size); 13418c2ecf20Sopenharmony_ci seq_printf(m, "\tMaximal Access: 0x%x", tcon->maximal_access); 13428c2ecf20Sopenharmony_ci} 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_cistatic void 13458c2ecf20Sopenharmony_cismb2_print_stats(struct seq_file *m, struct cifs_tcon *tcon) 13468c2ecf20Sopenharmony_ci{ 13478c2ecf20Sopenharmony_ci atomic_t *sent = tcon->stats.smb2_stats.smb2_com_sent; 13488c2ecf20Sopenharmony_ci atomic_t *failed = tcon->stats.smb2_stats.smb2_com_failed; 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci /* 13518c2ecf20Sopenharmony_ci * Can't display SMB2_NEGOTIATE, SESSION_SETUP, LOGOFF, CANCEL and ECHO 13528c2ecf20Sopenharmony_ci * totals (requests sent) since those SMBs are per-session not per tcon 13538c2ecf20Sopenharmony_ci */ 13548c2ecf20Sopenharmony_ci seq_printf(m, "\nBytes read: %llu Bytes written: %llu", 13558c2ecf20Sopenharmony_ci (long long)(tcon->bytes_read), 13568c2ecf20Sopenharmony_ci (long long)(tcon->bytes_written)); 13578c2ecf20Sopenharmony_ci seq_printf(m, "\nOpen files: %d total (local), %d open on server", 13588c2ecf20Sopenharmony_ci atomic_read(&tcon->num_local_opens), 13598c2ecf20Sopenharmony_ci atomic_read(&tcon->num_remote_opens)); 13608c2ecf20Sopenharmony_ci seq_printf(m, "\nTreeConnects: %d total %d failed", 13618c2ecf20Sopenharmony_ci atomic_read(&sent[SMB2_TREE_CONNECT_HE]), 13628c2ecf20Sopenharmony_ci atomic_read(&failed[SMB2_TREE_CONNECT_HE])); 13638c2ecf20Sopenharmony_ci seq_printf(m, "\nTreeDisconnects: %d total %d failed", 13648c2ecf20Sopenharmony_ci atomic_read(&sent[SMB2_TREE_DISCONNECT_HE]), 13658c2ecf20Sopenharmony_ci atomic_read(&failed[SMB2_TREE_DISCONNECT_HE])); 13668c2ecf20Sopenharmony_ci seq_printf(m, "\nCreates: %d total %d failed", 13678c2ecf20Sopenharmony_ci atomic_read(&sent[SMB2_CREATE_HE]), 13688c2ecf20Sopenharmony_ci atomic_read(&failed[SMB2_CREATE_HE])); 13698c2ecf20Sopenharmony_ci seq_printf(m, "\nCloses: %d total %d failed", 13708c2ecf20Sopenharmony_ci atomic_read(&sent[SMB2_CLOSE_HE]), 13718c2ecf20Sopenharmony_ci atomic_read(&failed[SMB2_CLOSE_HE])); 13728c2ecf20Sopenharmony_ci seq_printf(m, "\nFlushes: %d total %d failed", 13738c2ecf20Sopenharmony_ci atomic_read(&sent[SMB2_FLUSH_HE]), 13748c2ecf20Sopenharmony_ci atomic_read(&failed[SMB2_FLUSH_HE])); 13758c2ecf20Sopenharmony_ci seq_printf(m, "\nReads: %d total %d failed", 13768c2ecf20Sopenharmony_ci atomic_read(&sent[SMB2_READ_HE]), 13778c2ecf20Sopenharmony_ci atomic_read(&failed[SMB2_READ_HE])); 13788c2ecf20Sopenharmony_ci seq_printf(m, "\nWrites: %d total %d failed", 13798c2ecf20Sopenharmony_ci atomic_read(&sent[SMB2_WRITE_HE]), 13808c2ecf20Sopenharmony_ci atomic_read(&failed[SMB2_WRITE_HE])); 13818c2ecf20Sopenharmony_ci seq_printf(m, "\nLocks: %d total %d failed", 13828c2ecf20Sopenharmony_ci atomic_read(&sent[SMB2_LOCK_HE]), 13838c2ecf20Sopenharmony_ci atomic_read(&failed[SMB2_LOCK_HE])); 13848c2ecf20Sopenharmony_ci seq_printf(m, "\nIOCTLs: %d total %d failed", 13858c2ecf20Sopenharmony_ci atomic_read(&sent[SMB2_IOCTL_HE]), 13868c2ecf20Sopenharmony_ci atomic_read(&failed[SMB2_IOCTL_HE])); 13878c2ecf20Sopenharmony_ci seq_printf(m, "\nQueryDirectories: %d total %d failed", 13888c2ecf20Sopenharmony_ci atomic_read(&sent[SMB2_QUERY_DIRECTORY_HE]), 13898c2ecf20Sopenharmony_ci atomic_read(&failed[SMB2_QUERY_DIRECTORY_HE])); 13908c2ecf20Sopenharmony_ci seq_printf(m, "\nChangeNotifies: %d total %d failed", 13918c2ecf20Sopenharmony_ci atomic_read(&sent[SMB2_CHANGE_NOTIFY_HE]), 13928c2ecf20Sopenharmony_ci atomic_read(&failed[SMB2_CHANGE_NOTIFY_HE])); 13938c2ecf20Sopenharmony_ci seq_printf(m, "\nQueryInfos: %d total %d failed", 13948c2ecf20Sopenharmony_ci atomic_read(&sent[SMB2_QUERY_INFO_HE]), 13958c2ecf20Sopenharmony_ci atomic_read(&failed[SMB2_QUERY_INFO_HE])); 13968c2ecf20Sopenharmony_ci seq_printf(m, "\nSetInfos: %d total %d failed", 13978c2ecf20Sopenharmony_ci atomic_read(&sent[SMB2_SET_INFO_HE]), 13988c2ecf20Sopenharmony_ci atomic_read(&failed[SMB2_SET_INFO_HE])); 13998c2ecf20Sopenharmony_ci seq_printf(m, "\nOplockBreaks: %d sent %d failed", 14008c2ecf20Sopenharmony_ci atomic_read(&sent[SMB2_OPLOCK_BREAK_HE]), 14018c2ecf20Sopenharmony_ci atomic_read(&failed[SMB2_OPLOCK_BREAK_HE])); 14028c2ecf20Sopenharmony_ci} 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_cistatic void 14058c2ecf20Sopenharmony_cismb2_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock) 14068c2ecf20Sopenharmony_ci{ 14078c2ecf20Sopenharmony_ci struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); 14088c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server; 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci cfile->fid.persistent_fid = fid->persistent_fid; 14118c2ecf20Sopenharmony_ci cfile->fid.volatile_fid = fid->volatile_fid; 14128c2ecf20Sopenharmony_ci cfile->fid.access = fid->access; 14138c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_DEBUG2 14148c2ecf20Sopenharmony_ci cfile->fid.mid = fid->mid; 14158c2ecf20Sopenharmony_ci#endif /* CIFS_DEBUG2 */ 14168c2ecf20Sopenharmony_ci server->ops->set_oplock_level(cinode, oplock, fid->epoch, 14178c2ecf20Sopenharmony_ci &fid->purge_cache); 14188c2ecf20Sopenharmony_ci cinode->can_cache_brlcks = CIFS_CACHE_WRITE(cinode); 14198c2ecf20Sopenharmony_ci memcpy(cfile->fid.create_guid, fid->create_guid, 16); 14208c2ecf20Sopenharmony_ci} 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_cistatic void 14238c2ecf20Sopenharmony_cismb2_close_file(const unsigned int xid, struct cifs_tcon *tcon, 14248c2ecf20Sopenharmony_ci struct cifs_fid *fid) 14258c2ecf20Sopenharmony_ci{ 14268c2ecf20Sopenharmony_ci SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid); 14278c2ecf20Sopenharmony_ci} 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_cistatic void 14308c2ecf20Sopenharmony_cismb2_close_getattr(const unsigned int xid, struct cifs_tcon *tcon, 14318c2ecf20Sopenharmony_ci struct cifsFileInfo *cfile) 14328c2ecf20Sopenharmony_ci{ 14338c2ecf20Sopenharmony_ci struct smb2_file_network_open_info file_inf; 14348c2ecf20Sopenharmony_ci struct inode *inode; 14358c2ecf20Sopenharmony_ci int rc; 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci rc = __SMB2_close(xid, tcon, cfile->fid.persistent_fid, 14388c2ecf20Sopenharmony_ci cfile->fid.volatile_fid, &file_inf); 14398c2ecf20Sopenharmony_ci if (rc) 14408c2ecf20Sopenharmony_ci return; 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci inode = d_inode(cfile->dentry); 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci spin_lock(&inode->i_lock); 14458c2ecf20Sopenharmony_ci CIFS_I(inode)->time = jiffies; 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci /* Creation time should not need to be updated on close */ 14488c2ecf20Sopenharmony_ci if (file_inf.LastWriteTime) 14498c2ecf20Sopenharmony_ci inode->i_mtime = cifs_NTtimeToUnix(file_inf.LastWriteTime); 14508c2ecf20Sopenharmony_ci if (file_inf.ChangeTime) 14518c2ecf20Sopenharmony_ci inode->i_ctime = cifs_NTtimeToUnix(file_inf.ChangeTime); 14528c2ecf20Sopenharmony_ci if (file_inf.LastAccessTime) 14538c2ecf20Sopenharmony_ci inode->i_atime = cifs_NTtimeToUnix(file_inf.LastAccessTime); 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_ci /* 14568c2ecf20Sopenharmony_ci * i_blocks is not related to (i_size / i_blksize), 14578c2ecf20Sopenharmony_ci * but instead 512 byte (2**9) size is required for 14588c2ecf20Sopenharmony_ci * calculating num blocks. 14598c2ecf20Sopenharmony_ci */ 14608c2ecf20Sopenharmony_ci if (le64_to_cpu(file_inf.AllocationSize) > 4096) 14618c2ecf20Sopenharmony_ci inode->i_blocks = 14628c2ecf20Sopenharmony_ci (512 - 1 + le64_to_cpu(file_inf.AllocationSize)) >> 9; 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci /* End of file and Attributes should not have to be updated on close */ 14658c2ecf20Sopenharmony_ci spin_unlock(&inode->i_lock); 14668c2ecf20Sopenharmony_ci} 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_cistatic int 14698c2ecf20Sopenharmony_ciSMB2_request_res_key(const unsigned int xid, struct cifs_tcon *tcon, 14708c2ecf20Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, 14718c2ecf20Sopenharmony_ci struct copychunk_ioctl *pcchunk) 14728c2ecf20Sopenharmony_ci{ 14738c2ecf20Sopenharmony_ci int rc; 14748c2ecf20Sopenharmony_ci unsigned int ret_data_len; 14758c2ecf20Sopenharmony_ci struct resume_key_req *res_key; 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid, 14788c2ecf20Sopenharmony_ci FSCTL_SRV_REQUEST_RESUME_KEY, NULL, 0 /* no input */, 14798c2ecf20Sopenharmony_ci CIFSMaxBufSize, (char **)&res_key, &ret_data_len); 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_ci if (rc) { 14828c2ecf20Sopenharmony_ci cifs_tcon_dbg(VFS, "refcpy ioctl error %d getting resume key\n", rc); 14838c2ecf20Sopenharmony_ci goto req_res_key_exit; 14848c2ecf20Sopenharmony_ci } 14858c2ecf20Sopenharmony_ci if (ret_data_len < sizeof(struct resume_key_req)) { 14868c2ecf20Sopenharmony_ci cifs_tcon_dbg(VFS, "Invalid refcopy resume key length\n"); 14878c2ecf20Sopenharmony_ci rc = -EINVAL; 14888c2ecf20Sopenharmony_ci goto req_res_key_exit; 14898c2ecf20Sopenharmony_ci } 14908c2ecf20Sopenharmony_ci memcpy(pcchunk->SourceKey, res_key->ResumeKey, COPY_CHUNK_RES_KEY_SIZE); 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_cireq_res_key_exit: 14938c2ecf20Sopenharmony_ci kfree(res_key); 14948c2ecf20Sopenharmony_ci return rc; 14958c2ecf20Sopenharmony_ci} 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_cistruct iqi_vars { 14988c2ecf20Sopenharmony_ci struct smb_rqst rqst[3]; 14998c2ecf20Sopenharmony_ci struct kvec rsp_iov[3]; 15008c2ecf20Sopenharmony_ci struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; 15018c2ecf20Sopenharmony_ci struct kvec qi_iov[1]; 15028c2ecf20Sopenharmony_ci struct kvec io_iov[SMB2_IOCTL_IOV_SIZE]; 15038c2ecf20Sopenharmony_ci struct kvec si_iov[SMB2_SET_INFO_IOV_SIZE]; 15048c2ecf20Sopenharmony_ci struct kvec close_iov[1]; 15058c2ecf20Sopenharmony_ci}; 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_cistatic int 15088c2ecf20Sopenharmony_cismb2_ioctl_query_info(const unsigned int xid, 15098c2ecf20Sopenharmony_ci struct cifs_tcon *tcon, 15108c2ecf20Sopenharmony_ci struct cifs_sb_info *cifs_sb, 15118c2ecf20Sopenharmony_ci __le16 *path, int is_dir, 15128c2ecf20Sopenharmony_ci unsigned long p) 15138c2ecf20Sopenharmony_ci{ 15148c2ecf20Sopenharmony_ci struct iqi_vars *vars; 15158c2ecf20Sopenharmony_ci struct smb_rqst *rqst; 15168c2ecf20Sopenharmony_ci struct kvec *rsp_iov; 15178c2ecf20Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 15188c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(ses); 15198c2ecf20Sopenharmony_ci char __user *arg = (char __user *)p; 15208c2ecf20Sopenharmony_ci struct smb_query_info qi; 15218c2ecf20Sopenharmony_ci struct smb_query_info __user *pqi; 15228c2ecf20Sopenharmony_ci int rc = 0; 15238c2ecf20Sopenharmony_ci int flags = CIFS_CP_CREATE_CLOSE_OP; 15248c2ecf20Sopenharmony_ci struct smb2_query_info_rsp *qi_rsp = NULL; 15258c2ecf20Sopenharmony_ci struct smb2_ioctl_rsp *io_rsp = NULL; 15268c2ecf20Sopenharmony_ci void *buffer = NULL; 15278c2ecf20Sopenharmony_ci int resp_buftype[3]; 15288c2ecf20Sopenharmony_ci struct cifs_open_parms oparms; 15298c2ecf20Sopenharmony_ci u8 oplock = SMB2_OPLOCK_LEVEL_NONE; 15308c2ecf20Sopenharmony_ci struct cifs_fid fid; 15318c2ecf20Sopenharmony_ci unsigned int size[2]; 15328c2ecf20Sopenharmony_ci void *data[2]; 15338c2ecf20Sopenharmony_ci int create_options = is_dir ? CREATE_NOT_FILE : CREATE_NOT_DIR; 15348c2ecf20Sopenharmony_ci void (*free_req1_func)(struct smb_rqst *r); 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci vars = kzalloc(sizeof(*vars), GFP_ATOMIC); 15378c2ecf20Sopenharmony_ci if (vars == NULL) 15388c2ecf20Sopenharmony_ci return -ENOMEM; 15398c2ecf20Sopenharmony_ci rqst = &vars->rqst[0]; 15408c2ecf20Sopenharmony_ci rsp_iov = &vars->rsp_iov[0]; 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci if (copy_from_user(&qi, arg, sizeof(struct smb_query_info))) { 15458c2ecf20Sopenharmony_ci rc = -EFAULT; 15468c2ecf20Sopenharmony_ci goto free_vars; 15478c2ecf20Sopenharmony_ci } 15488c2ecf20Sopenharmony_ci if (qi.output_buffer_length > 1024) { 15498c2ecf20Sopenharmony_ci rc = -EINVAL; 15508c2ecf20Sopenharmony_ci goto free_vars; 15518c2ecf20Sopenharmony_ci } 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci if (!ses || !server) { 15548c2ecf20Sopenharmony_ci rc = -EIO; 15558c2ecf20Sopenharmony_ci goto free_vars; 15568c2ecf20Sopenharmony_ci } 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci if (smb3_encryption_required(tcon)) 15598c2ecf20Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci if (qi.output_buffer_length) { 15628c2ecf20Sopenharmony_ci buffer = memdup_user(arg + sizeof(struct smb_query_info), qi.output_buffer_length); 15638c2ecf20Sopenharmony_ci if (IS_ERR(buffer)) { 15648c2ecf20Sopenharmony_ci rc = PTR_ERR(buffer); 15658c2ecf20Sopenharmony_ci goto free_vars; 15668c2ecf20Sopenharmony_ci } 15678c2ecf20Sopenharmony_ci } 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci /* Open */ 15708c2ecf20Sopenharmony_ci rqst[0].rq_iov = &vars->open_iov[0]; 15718c2ecf20Sopenharmony_ci rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_ci memset(&oparms, 0, sizeof(oparms)); 15748c2ecf20Sopenharmony_ci oparms.tcon = tcon; 15758c2ecf20Sopenharmony_ci oparms.disposition = FILE_OPEN; 15768c2ecf20Sopenharmony_ci oparms.create_options = cifs_create_options(cifs_sb, create_options); 15778c2ecf20Sopenharmony_ci oparms.fid = &fid; 15788c2ecf20Sopenharmony_ci oparms.reconnect = false; 15798c2ecf20Sopenharmony_ci 15808c2ecf20Sopenharmony_ci if (qi.flags & PASSTHRU_FSCTL) { 15818c2ecf20Sopenharmony_ci switch (qi.info_type & FSCTL_DEVICE_ACCESS_MASK) { 15828c2ecf20Sopenharmony_ci case FSCTL_DEVICE_ACCESS_FILE_READ_WRITE_ACCESS: 15838c2ecf20Sopenharmony_ci oparms.desired_access = FILE_READ_DATA | FILE_WRITE_DATA | FILE_READ_ATTRIBUTES | SYNCHRONIZE; 15848c2ecf20Sopenharmony_ci break; 15858c2ecf20Sopenharmony_ci case FSCTL_DEVICE_ACCESS_FILE_ANY_ACCESS: 15868c2ecf20Sopenharmony_ci oparms.desired_access = GENERIC_ALL; 15878c2ecf20Sopenharmony_ci break; 15888c2ecf20Sopenharmony_ci case FSCTL_DEVICE_ACCESS_FILE_READ_ACCESS: 15898c2ecf20Sopenharmony_ci oparms.desired_access = GENERIC_READ; 15908c2ecf20Sopenharmony_ci break; 15918c2ecf20Sopenharmony_ci case FSCTL_DEVICE_ACCESS_FILE_WRITE_ACCESS: 15928c2ecf20Sopenharmony_ci oparms.desired_access = GENERIC_WRITE; 15938c2ecf20Sopenharmony_ci break; 15948c2ecf20Sopenharmony_ci } 15958c2ecf20Sopenharmony_ci } else if (qi.flags & PASSTHRU_SET_INFO) { 15968c2ecf20Sopenharmony_ci oparms.desired_access = GENERIC_WRITE; 15978c2ecf20Sopenharmony_ci } else { 15988c2ecf20Sopenharmony_ci oparms.desired_access = FILE_READ_ATTRIBUTES | READ_CONTROL; 15998c2ecf20Sopenharmony_ci } 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_ci rc = SMB2_open_init(tcon, server, 16028c2ecf20Sopenharmony_ci &rqst[0], &oplock, &oparms, path); 16038c2ecf20Sopenharmony_ci if (rc) 16048c2ecf20Sopenharmony_ci goto free_output_buffer; 16058c2ecf20Sopenharmony_ci smb2_set_next_command(tcon, &rqst[0]); 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_ci /* Query */ 16088c2ecf20Sopenharmony_ci if (qi.flags & PASSTHRU_FSCTL) { 16098c2ecf20Sopenharmony_ci /* Can eventually relax perm check since server enforces too */ 16108c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) { 16118c2ecf20Sopenharmony_ci rc = -EPERM; 16128c2ecf20Sopenharmony_ci goto free_open_req; 16138c2ecf20Sopenharmony_ci } 16148c2ecf20Sopenharmony_ci rqst[1].rq_iov = &vars->io_iov[0]; 16158c2ecf20Sopenharmony_ci rqst[1].rq_nvec = SMB2_IOCTL_IOV_SIZE; 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_ci rc = SMB2_ioctl_init(tcon, server, &rqst[1], COMPOUND_FID, COMPOUND_FID, 16188c2ecf20Sopenharmony_ci qi.info_type, buffer, qi.output_buffer_length, 16198c2ecf20Sopenharmony_ci CIFSMaxBufSize - MAX_SMB2_CREATE_RESPONSE_SIZE - 16208c2ecf20Sopenharmony_ci MAX_SMB2_CLOSE_RESPONSE_SIZE); 16218c2ecf20Sopenharmony_ci free_req1_func = SMB2_ioctl_free; 16228c2ecf20Sopenharmony_ci } else if (qi.flags == PASSTHRU_SET_INFO) { 16238c2ecf20Sopenharmony_ci /* Can eventually relax perm check since server enforces too */ 16248c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) { 16258c2ecf20Sopenharmony_ci rc = -EPERM; 16268c2ecf20Sopenharmony_ci goto free_open_req; 16278c2ecf20Sopenharmony_ci } 16288c2ecf20Sopenharmony_ci if (qi.output_buffer_length < 8) { 16298c2ecf20Sopenharmony_ci rc = -EINVAL; 16308c2ecf20Sopenharmony_ci goto free_open_req; 16318c2ecf20Sopenharmony_ci } 16328c2ecf20Sopenharmony_ci rqst[1].rq_iov = &vars->si_iov[0]; 16338c2ecf20Sopenharmony_ci rqst[1].rq_nvec = 1; 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci /* MS-FSCC 2.4.13 FileEndOfFileInformation */ 16368c2ecf20Sopenharmony_ci size[0] = 8; 16378c2ecf20Sopenharmony_ci data[0] = buffer; 16388c2ecf20Sopenharmony_ci 16398c2ecf20Sopenharmony_ci rc = SMB2_set_info_init(tcon, server, &rqst[1], COMPOUND_FID, COMPOUND_FID, 16408c2ecf20Sopenharmony_ci current->tgid, FILE_END_OF_FILE_INFORMATION, 16418c2ecf20Sopenharmony_ci SMB2_O_INFO_FILE, 0, data, size); 16428c2ecf20Sopenharmony_ci free_req1_func = SMB2_set_info_free; 16438c2ecf20Sopenharmony_ci } else if (qi.flags == PASSTHRU_QUERY_INFO) { 16448c2ecf20Sopenharmony_ci rqst[1].rq_iov = &vars->qi_iov[0]; 16458c2ecf20Sopenharmony_ci rqst[1].rq_nvec = 1; 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_ci rc = SMB2_query_info_init(tcon, server, 16488c2ecf20Sopenharmony_ci &rqst[1], COMPOUND_FID, 16498c2ecf20Sopenharmony_ci COMPOUND_FID, qi.file_info_class, 16508c2ecf20Sopenharmony_ci qi.info_type, qi.additional_information, 16518c2ecf20Sopenharmony_ci qi.input_buffer_length, 16528c2ecf20Sopenharmony_ci qi.output_buffer_length, buffer); 16538c2ecf20Sopenharmony_ci free_req1_func = SMB2_query_info_free; 16548c2ecf20Sopenharmony_ci } else { /* unknown flags */ 16558c2ecf20Sopenharmony_ci cifs_tcon_dbg(VFS, "Invalid passthru query flags: 0x%x\n", 16568c2ecf20Sopenharmony_ci qi.flags); 16578c2ecf20Sopenharmony_ci rc = -EINVAL; 16588c2ecf20Sopenharmony_ci } 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_ci if (rc) 16618c2ecf20Sopenharmony_ci goto free_open_req; 16628c2ecf20Sopenharmony_ci smb2_set_next_command(tcon, &rqst[1]); 16638c2ecf20Sopenharmony_ci smb2_set_related(&rqst[1]); 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_ci /* Close */ 16668c2ecf20Sopenharmony_ci rqst[2].rq_iov = &vars->close_iov[0]; 16678c2ecf20Sopenharmony_ci rqst[2].rq_nvec = 1; 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_ci rc = SMB2_close_init(tcon, server, 16708c2ecf20Sopenharmony_ci &rqst[2], COMPOUND_FID, COMPOUND_FID, false); 16718c2ecf20Sopenharmony_ci if (rc) 16728c2ecf20Sopenharmony_ci goto free_req_1; 16738c2ecf20Sopenharmony_ci smb2_set_related(&rqst[2]); 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci rc = compound_send_recv(xid, ses, server, 16768c2ecf20Sopenharmony_ci flags, 3, rqst, 16778c2ecf20Sopenharmony_ci resp_buftype, rsp_iov); 16788c2ecf20Sopenharmony_ci if (rc) 16798c2ecf20Sopenharmony_ci goto out; 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ci /* No need to bump num_remote_opens since handle immediately closed */ 16828c2ecf20Sopenharmony_ci if (qi.flags & PASSTHRU_FSCTL) { 16838c2ecf20Sopenharmony_ci pqi = (struct smb_query_info __user *)arg; 16848c2ecf20Sopenharmony_ci io_rsp = (struct smb2_ioctl_rsp *)rsp_iov[1].iov_base; 16858c2ecf20Sopenharmony_ci if (le32_to_cpu(io_rsp->OutputCount) < qi.input_buffer_length) 16868c2ecf20Sopenharmony_ci qi.input_buffer_length = le32_to_cpu(io_rsp->OutputCount); 16878c2ecf20Sopenharmony_ci if (qi.input_buffer_length > 0 && 16888c2ecf20Sopenharmony_ci le32_to_cpu(io_rsp->OutputOffset) + qi.input_buffer_length 16898c2ecf20Sopenharmony_ci > rsp_iov[1].iov_len) { 16908c2ecf20Sopenharmony_ci rc = -EFAULT; 16918c2ecf20Sopenharmony_ci goto out; 16928c2ecf20Sopenharmony_ci } 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_ci if (copy_to_user(&pqi->input_buffer_length, 16958c2ecf20Sopenharmony_ci &qi.input_buffer_length, 16968c2ecf20Sopenharmony_ci sizeof(qi.input_buffer_length))) { 16978c2ecf20Sopenharmony_ci rc = -EFAULT; 16988c2ecf20Sopenharmony_ci goto out; 16998c2ecf20Sopenharmony_ci } 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_ci if (copy_to_user((void __user *)pqi + sizeof(struct smb_query_info), 17028c2ecf20Sopenharmony_ci (const void *)io_rsp + le32_to_cpu(io_rsp->OutputOffset), 17038c2ecf20Sopenharmony_ci qi.input_buffer_length)) 17048c2ecf20Sopenharmony_ci rc = -EFAULT; 17058c2ecf20Sopenharmony_ci } else { 17068c2ecf20Sopenharmony_ci pqi = (struct smb_query_info __user *)arg; 17078c2ecf20Sopenharmony_ci qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base; 17088c2ecf20Sopenharmony_ci if (le32_to_cpu(qi_rsp->OutputBufferLength) < qi.input_buffer_length) 17098c2ecf20Sopenharmony_ci qi.input_buffer_length = le32_to_cpu(qi_rsp->OutputBufferLength); 17108c2ecf20Sopenharmony_ci if (copy_to_user(&pqi->input_buffer_length, 17118c2ecf20Sopenharmony_ci &qi.input_buffer_length, 17128c2ecf20Sopenharmony_ci sizeof(qi.input_buffer_length))) { 17138c2ecf20Sopenharmony_ci rc = -EFAULT; 17148c2ecf20Sopenharmony_ci goto out; 17158c2ecf20Sopenharmony_ci } 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_ci if (copy_to_user(pqi + 1, qi_rsp->Buffer, 17188c2ecf20Sopenharmony_ci qi.input_buffer_length)) 17198c2ecf20Sopenharmony_ci rc = -EFAULT; 17208c2ecf20Sopenharmony_ci } 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_ciout: 17238c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); 17248c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); 17258c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); 17268c2ecf20Sopenharmony_ci SMB2_close_free(&rqst[2]); 17278c2ecf20Sopenharmony_cifree_req_1: 17288c2ecf20Sopenharmony_ci free_req1_func(&rqst[1]); 17298c2ecf20Sopenharmony_cifree_open_req: 17308c2ecf20Sopenharmony_ci SMB2_open_free(&rqst[0]); 17318c2ecf20Sopenharmony_cifree_output_buffer: 17328c2ecf20Sopenharmony_ci kfree(buffer); 17338c2ecf20Sopenharmony_cifree_vars: 17348c2ecf20Sopenharmony_ci kfree(vars); 17358c2ecf20Sopenharmony_ci return rc; 17368c2ecf20Sopenharmony_ci} 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_cistatic ssize_t 17398c2ecf20Sopenharmony_cismb2_copychunk_range(const unsigned int xid, 17408c2ecf20Sopenharmony_ci struct cifsFileInfo *srcfile, 17418c2ecf20Sopenharmony_ci struct cifsFileInfo *trgtfile, u64 src_off, 17428c2ecf20Sopenharmony_ci u64 len, u64 dest_off) 17438c2ecf20Sopenharmony_ci{ 17448c2ecf20Sopenharmony_ci int rc; 17458c2ecf20Sopenharmony_ci unsigned int ret_data_len; 17468c2ecf20Sopenharmony_ci struct copychunk_ioctl *pcchunk; 17478c2ecf20Sopenharmony_ci struct copychunk_ioctl_rsp *retbuf = NULL; 17488c2ecf20Sopenharmony_ci struct cifs_tcon *tcon; 17498c2ecf20Sopenharmony_ci int chunks_copied = 0; 17508c2ecf20Sopenharmony_ci bool chunk_sizes_updated = false; 17518c2ecf20Sopenharmony_ci ssize_t bytes_written, total_bytes_written = 0; 17528c2ecf20Sopenharmony_ci struct inode *inode; 17538c2ecf20Sopenharmony_ci 17548c2ecf20Sopenharmony_ci pcchunk = kmalloc(sizeof(struct copychunk_ioctl), GFP_KERNEL); 17558c2ecf20Sopenharmony_ci 17568c2ecf20Sopenharmony_ci /* 17578c2ecf20Sopenharmony_ci * We need to flush all unwritten data before we can send the 17588c2ecf20Sopenharmony_ci * copychunk ioctl to the server. 17598c2ecf20Sopenharmony_ci */ 17608c2ecf20Sopenharmony_ci inode = d_inode(trgtfile->dentry); 17618c2ecf20Sopenharmony_ci filemap_write_and_wait(inode->i_mapping); 17628c2ecf20Sopenharmony_ci 17638c2ecf20Sopenharmony_ci if (pcchunk == NULL) 17648c2ecf20Sopenharmony_ci return -ENOMEM; 17658c2ecf20Sopenharmony_ci 17668c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: about to call request res key\n", __func__); 17678c2ecf20Sopenharmony_ci /* Request a key from the server to identify the source of the copy */ 17688c2ecf20Sopenharmony_ci rc = SMB2_request_res_key(xid, tlink_tcon(srcfile->tlink), 17698c2ecf20Sopenharmony_ci srcfile->fid.persistent_fid, 17708c2ecf20Sopenharmony_ci srcfile->fid.volatile_fid, pcchunk); 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_ci /* Note: request_res_key sets res_key null only if rc !=0 */ 17738c2ecf20Sopenharmony_ci if (rc) 17748c2ecf20Sopenharmony_ci goto cchunk_out; 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_ci /* For now array only one chunk long, will make more flexible later */ 17778c2ecf20Sopenharmony_ci pcchunk->ChunkCount = cpu_to_le32(1); 17788c2ecf20Sopenharmony_ci pcchunk->Reserved = 0; 17798c2ecf20Sopenharmony_ci pcchunk->Reserved2 = 0; 17808c2ecf20Sopenharmony_ci 17818c2ecf20Sopenharmony_ci tcon = tlink_tcon(trgtfile->tlink); 17828c2ecf20Sopenharmony_ci 17838c2ecf20Sopenharmony_ci while (len > 0) { 17848c2ecf20Sopenharmony_ci pcchunk->SourceOffset = cpu_to_le64(src_off); 17858c2ecf20Sopenharmony_ci pcchunk->TargetOffset = cpu_to_le64(dest_off); 17868c2ecf20Sopenharmony_ci pcchunk->Length = 17878c2ecf20Sopenharmony_ci cpu_to_le32(min_t(u64, len, tcon->max_bytes_chunk)); 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci /* Request server copy to target from src identified by key */ 17908c2ecf20Sopenharmony_ci kfree(retbuf); 17918c2ecf20Sopenharmony_ci retbuf = NULL; 17928c2ecf20Sopenharmony_ci rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid, 17938c2ecf20Sopenharmony_ci trgtfile->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE, 17948c2ecf20Sopenharmony_ci (char *)pcchunk, sizeof(struct copychunk_ioctl), 17958c2ecf20Sopenharmony_ci CIFSMaxBufSize, (char **)&retbuf, &ret_data_len); 17968c2ecf20Sopenharmony_ci if (rc == 0) { 17978c2ecf20Sopenharmony_ci if (ret_data_len != 17988c2ecf20Sopenharmony_ci sizeof(struct copychunk_ioctl_rsp)) { 17998c2ecf20Sopenharmony_ci cifs_tcon_dbg(VFS, "Invalid cchunk response size\n"); 18008c2ecf20Sopenharmony_ci rc = -EIO; 18018c2ecf20Sopenharmony_ci goto cchunk_out; 18028c2ecf20Sopenharmony_ci } 18038c2ecf20Sopenharmony_ci if (retbuf->TotalBytesWritten == 0) { 18048c2ecf20Sopenharmony_ci cifs_dbg(FYI, "no bytes copied\n"); 18058c2ecf20Sopenharmony_ci rc = -EIO; 18068c2ecf20Sopenharmony_ci goto cchunk_out; 18078c2ecf20Sopenharmony_ci } 18088c2ecf20Sopenharmony_ci /* 18098c2ecf20Sopenharmony_ci * Check if server claimed to write more than we asked 18108c2ecf20Sopenharmony_ci */ 18118c2ecf20Sopenharmony_ci if (le32_to_cpu(retbuf->TotalBytesWritten) > 18128c2ecf20Sopenharmony_ci le32_to_cpu(pcchunk->Length)) { 18138c2ecf20Sopenharmony_ci cifs_tcon_dbg(VFS, "Invalid copy chunk response\n"); 18148c2ecf20Sopenharmony_ci rc = -EIO; 18158c2ecf20Sopenharmony_ci goto cchunk_out; 18168c2ecf20Sopenharmony_ci } 18178c2ecf20Sopenharmony_ci if (le32_to_cpu(retbuf->ChunksWritten) != 1) { 18188c2ecf20Sopenharmony_ci cifs_tcon_dbg(VFS, "Invalid num chunks written\n"); 18198c2ecf20Sopenharmony_ci rc = -EIO; 18208c2ecf20Sopenharmony_ci goto cchunk_out; 18218c2ecf20Sopenharmony_ci } 18228c2ecf20Sopenharmony_ci chunks_copied++; 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_ci bytes_written = le32_to_cpu(retbuf->TotalBytesWritten); 18258c2ecf20Sopenharmony_ci src_off += bytes_written; 18268c2ecf20Sopenharmony_ci dest_off += bytes_written; 18278c2ecf20Sopenharmony_ci len -= bytes_written; 18288c2ecf20Sopenharmony_ci total_bytes_written += bytes_written; 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Chunks %d PartialChunk %d Total %zu\n", 18318c2ecf20Sopenharmony_ci le32_to_cpu(retbuf->ChunksWritten), 18328c2ecf20Sopenharmony_ci le32_to_cpu(retbuf->ChunkBytesWritten), 18338c2ecf20Sopenharmony_ci bytes_written); 18348c2ecf20Sopenharmony_ci } else if (rc == -EINVAL) { 18358c2ecf20Sopenharmony_ci if (ret_data_len != sizeof(struct copychunk_ioctl_rsp)) 18368c2ecf20Sopenharmony_ci goto cchunk_out; 18378c2ecf20Sopenharmony_ci 18388c2ecf20Sopenharmony_ci cifs_dbg(FYI, "MaxChunks %d BytesChunk %d MaxCopy %d\n", 18398c2ecf20Sopenharmony_ci le32_to_cpu(retbuf->ChunksWritten), 18408c2ecf20Sopenharmony_ci le32_to_cpu(retbuf->ChunkBytesWritten), 18418c2ecf20Sopenharmony_ci le32_to_cpu(retbuf->TotalBytesWritten)); 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_ci /* 18448c2ecf20Sopenharmony_ci * Check if this is the first request using these sizes, 18458c2ecf20Sopenharmony_ci * (ie check if copy succeed once with original sizes 18468c2ecf20Sopenharmony_ci * and check if the server gave us different sizes after 18478c2ecf20Sopenharmony_ci * we already updated max sizes on previous request). 18488c2ecf20Sopenharmony_ci * if not then why is the server returning an error now 18498c2ecf20Sopenharmony_ci */ 18508c2ecf20Sopenharmony_ci if ((chunks_copied != 0) || chunk_sizes_updated) 18518c2ecf20Sopenharmony_ci goto cchunk_out; 18528c2ecf20Sopenharmony_ci 18538c2ecf20Sopenharmony_ci /* Check that server is not asking us to grow size */ 18548c2ecf20Sopenharmony_ci if (le32_to_cpu(retbuf->ChunkBytesWritten) < 18558c2ecf20Sopenharmony_ci tcon->max_bytes_chunk) 18568c2ecf20Sopenharmony_ci tcon->max_bytes_chunk = 18578c2ecf20Sopenharmony_ci le32_to_cpu(retbuf->ChunkBytesWritten); 18588c2ecf20Sopenharmony_ci else 18598c2ecf20Sopenharmony_ci goto cchunk_out; /* server gave us bogus size */ 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_ci /* No need to change MaxChunks since already set to 1 */ 18628c2ecf20Sopenharmony_ci chunk_sizes_updated = true; 18638c2ecf20Sopenharmony_ci } else 18648c2ecf20Sopenharmony_ci goto cchunk_out; 18658c2ecf20Sopenharmony_ci } 18668c2ecf20Sopenharmony_ci 18678c2ecf20Sopenharmony_cicchunk_out: 18688c2ecf20Sopenharmony_ci kfree(pcchunk); 18698c2ecf20Sopenharmony_ci kfree(retbuf); 18708c2ecf20Sopenharmony_ci if (rc) 18718c2ecf20Sopenharmony_ci return rc; 18728c2ecf20Sopenharmony_ci else 18738c2ecf20Sopenharmony_ci return total_bytes_written; 18748c2ecf20Sopenharmony_ci} 18758c2ecf20Sopenharmony_ci 18768c2ecf20Sopenharmony_cistatic int 18778c2ecf20Sopenharmony_cismb2_flush_file(const unsigned int xid, struct cifs_tcon *tcon, 18788c2ecf20Sopenharmony_ci struct cifs_fid *fid) 18798c2ecf20Sopenharmony_ci{ 18808c2ecf20Sopenharmony_ci return SMB2_flush(xid, tcon, fid->persistent_fid, fid->volatile_fid); 18818c2ecf20Sopenharmony_ci} 18828c2ecf20Sopenharmony_ci 18838c2ecf20Sopenharmony_cistatic unsigned int 18848c2ecf20Sopenharmony_cismb2_read_data_offset(char *buf) 18858c2ecf20Sopenharmony_ci{ 18868c2ecf20Sopenharmony_ci struct smb2_read_rsp *rsp = (struct smb2_read_rsp *)buf; 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_ci return rsp->DataOffset; 18898c2ecf20Sopenharmony_ci} 18908c2ecf20Sopenharmony_ci 18918c2ecf20Sopenharmony_cistatic unsigned int 18928c2ecf20Sopenharmony_cismb2_read_data_length(char *buf, bool in_remaining) 18938c2ecf20Sopenharmony_ci{ 18948c2ecf20Sopenharmony_ci struct smb2_read_rsp *rsp = (struct smb2_read_rsp *)buf; 18958c2ecf20Sopenharmony_ci 18968c2ecf20Sopenharmony_ci if (in_remaining) 18978c2ecf20Sopenharmony_ci return le32_to_cpu(rsp->DataRemaining); 18988c2ecf20Sopenharmony_ci 18998c2ecf20Sopenharmony_ci return le32_to_cpu(rsp->DataLength); 19008c2ecf20Sopenharmony_ci} 19018c2ecf20Sopenharmony_ci 19028c2ecf20Sopenharmony_ci 19038c2ecf20Sopenharmony_cistatic int 19048c2ecf20Sopenharmony_cismb2_sync_read(const unsigned int xid, struct cifs_fid *pfid, 19058c2ecf20Sopenharmony_ci struct cifs_io_parms *parms, unsigned int *bytes_read, 19068c2ecf20Sopenharmony_ci char **buf, int *buf_type) 19078c2ecf20Sopenharmony_ci{ 19088c2ecf20Sopenharmony_ci parms->persistent_fid = pfid->persistent_fid; 19098c2ecf20Sopenharmony_ci parms->volatile_fid = pfid->volatile_fid; 19108c2ecf20Sopenharmony_ci return SMB2_read(xid, parms, bytes_read, buf, buf_type); 19118c2ecf20Sopenharmony_ci} 19128c2ecf20Sopenharmony_ci 19138c2ecf20Sopenharmony_cistatic int 19148c2ecf20Sopenharmony_cismb2_sync_write(const unsigned int xid, struct cifs_fid *pfid, 19158c2ecf20Sopenharmony_ci struct cifs_io_parms *parms, unsigned int *written, 19168c2ecf20Sopenharmony_ci struct kvec *iov, unsigned long nr_segs) 19178c2ecf20Sopenharmony_ci{ 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_ci parms->persistent_fid = pfid->persistent_fid; 19208c2ecf20Sopenharmony_ci parms->volatile_fid = pfid->volatile_fid; 19218c2ecf20Sopenharmony_ci return SMB2_write(xid, parms, written, iov, nr_segs); 19228c2ecf20Sopenharmony_ci} 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_ci/* Set or clear the SPARSE_FILE attribute based on value passed in setsparse */ 19258c2ecf20Sopenharmony_cistatic bool smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon, 19268c2ecf20Sopenharmony_ci struct cifsFileInfo *cfile, struct inode *inode, __u8 setsparse) 19278c2ecf20Sopenharmony_ci{ 19288c2ecf20Sopenharmony_ci struct cifsInodeInfo *cifsi; 19298c2ecf20Sopenharmony_ci int rc; 19308c2ecf20Sopenharmony_ci 19318c2ecf20Sopenharmony_ci cifsi = CIFS_I(inode); 19328c2ecf20Sopenharmony_ci 19338c2ecf20Sopenharmony_ci /* if file already sparse don't bother setting sparse again */ 19348c2ecf20Sopenharmony_ci if ((cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) && setsparse) 19358c2ecf20Sopenharmony_ci return true; /* already sparse */ 19368c2ecf20Sopenharmony_ci 19378c2ecf20Sopenharmony_ci if (!(cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) && !setsparse) 19388c2ecf20Sopenharmony_ci return true; /* already not sparse */ 19398c2ecf20Sopenharmony_ci 19408c2ecf20Sopenharmony_ci /* 19418c2ecf20Sopenharmony_ci * Can't check for sparse support on share the usual way via the 19428c2ecf20Sopenharmony_ci * FS attribute info (FILE_SUPPORTS_SPARSE_FILES) on the share 19438c2ecf20Sopenharmony_ci * since Samba server doesn't set the flag on the share, yet 19448c2ecf20Sopenharmony_ci * supports the set sparse FSCTL and returns sparse correctly 19458c2ecf20Sopenharmony_ci * in the file attributes. If we fail setting sparse though we 19468c2ecf20Sopenharmony_ci * mark that server does not support sparse files for this share 19478c2ecf20Sopenharmony_ci * to avoid repeatedly sending the unsupported fsctl to server 19488c2ecf20Sopenharmony_ci * if the file is repeatedly extended. 19498c2ecf20Sopenharmony_ci */ 19508c2ecf20Sopenharmony_ci if (tcon->broken_sparse_sup) 19518c2ecf20Sopenharmony_ci return false; 19528c2ecf20Sopenharmony_ci 19538c2ecf20Sopenharmony_ci rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, 19548c2ecf20Sopenharmony_ci cfile->fid.volatile_fid, FSCTL_SET_SPARSE, 19558c2ecf20Sopenharmony_ci &setsparse, 1, CIFSMaxBufSize, NULL, NULL); 19568c2ecf20Sopenharmony_ci if (rc) { 19578c2ecf20Sopenharmony_ci tcon->broken_sparse_sup = true; 19588c2ecf20Sopenharmony_ci cifs_dbg(FYI, "set sparse rc = %d\n", rc); 19598c2ecf20Sopenharmony_ci return false; 19608c2ecf20Sopenharmony_ci } 19618c2ecf20Sopenharmony_ci 19628c2ecf20Sopenharmony_ci if (setsparse) 19638c2ecf20Sopenharmony_ci cifsi->cifsAttrs |= FILE_ATTRIBUTE_SPARSE_FILE; 19648c2ecf20Sopenharmony_ci else 19658c2ecf20Sopenharmony_ci cifsi->cifsAttrs &= (~FILE_ATTRIBUTE_SPARSE_FILE); 19668c2ecf20Sopenharmony_ci 19678c2ecf20Sopenharmony_ci return true; 19688c2ecf20Sopenharmony_ci} 19698c2ecf20Sopenharmony_ci 19708c2ecf20Sopenharmony_cistatic int 19718c2ecf20Sopenharmony_cismb2_set_file_size(const unsigned int xid, struct cifs_tcon *tcon, 19728c2ecf20Sopenharmony_ci struct cifsFileInfo *cfile, __u64 size, bool set_alloc) 19738c2ecf20Sopenharmony_ci{ 19748c2ecf20Sopenharmony_ci __le64 eof = cpu_to_le64(size); 19758c2ecf20Sopenharmony_ci struct inode *inode; 19768c2ecf20Sopenharmony_ci 19778c2ecf20Sopenharmony_ci /* 19788c2ecf20Sopenharmony_ci * If extending file more than one page make sparse. Many Linux fs 19798c2ecf20Sopenharmony_ci * make files sparse by default when extending via ftruncate 19808c2ecf20Sopenharmony_ci */ 19818c2ecf20Sopenharmony_ci inode = d_inode(cfile->dentry); 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_ci if (!set_alloc && (size > inode->i_size + 8192)) { 19848c2ecf20Sopenharmony_ci __u8 set_sparse = 1; 19858c2ecf20Sopenharmony_ci 19868c2ecf20Sopenharmony_ci /* whether set sparse succeeds or not, extend the file */ 19878c2ecf20Sopenharmony_ci smb2_set_sparse(xid, tcon, cfile, inode, set_sparse); 19888c2ecf20Sopenharmony_ci } 19898c2ecf20Sopenharmony_ci 19908c2ecf20Sopenharmony_ci return SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, 19918c2ecf20Sopenharmony_ci cfile->fid.volatile_fid, cfile->pid, &eof); 19928c2ecf20Sopenharmony_ci} 19938c2ecf20Sopenharmony_ci 19948c2ecf20Sopenharmony_cistatic int 19958c2ecf20Sopenharmony_cismb2_duplicate_extents(const unsigned int xid, 19968c2ecf20Sopenharmony_ci struct cifsFileInfo *srcfile, 19978c2ecf20Sopenharmony_ci struct cifsFileInfo *trgtfile, u64 src_off, 19988c2ecf20Sopenharmony_ci u64 len, u64 dest_off) 19998c2ecf20Sopenharmony_ci{ 20008c2ecf20Sopenharmony_ci int rc; 20018c2ecf20Sopenharmony_ci unsigned int ret_data_len; 20028c2ecf20Sopenharmony_ci struct inode *inode; 20038c2ecf20Sopenharmony_ci struct duplicate_extents_to_file dup_ext_buf; 20048c2ecf20Sopenharmony_ci struct cifs_tcon *tcon = tlink_tcon(trgtfile->tlink); 20058c2ecf20Sopenharmony_ci 20068c2ecf20Sopenharmony_ci /* server fileays advertise duplicate extent support with this flag */ 20078c2ecf20Sopenharmony_ci if ((le32_to_cpu(tcon->fsAttrInfo.Attributes) & 20088c2ecf20Sopenharmony_ci FILE_SUPPORTS_BLOCK_REFCOUNTING) == 0) 20098c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 20108c2ecf20Sopenharmony_ci 20118c2ecf20Sopenharmony_ci dup_ext_buf.VolatileFileHandle = srcfile->fid.volatile_fid; 20128c2ecf20Sopenharmony_ci dup_ext_buf.PersistentFileHandle = srcfile->fid.persistent_fid; 20138c2ecf20Sopenharmony_ci dup_ext_buf.SourceFileOffset = cpu_to_le64(src_off); 20148c2ecf20Sopenharmony_ci dup_ext_buf.TargetFileOffset = cpu_to_le64(dest_off); 20158c2ecf20Sopenharmony_ci dup_ext_buf.ByteCount = cpu_to_le64(len); 20168c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Duplicate extents: src off %lld dst off %lld len %lld\n", 20178c2ecf20Sopenharmony_ci src_off, dest_off, len); 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_ci inode = d_inode(trgtfile->dentry); 20208c2ecf20Sopenharmony_ci if (inode->i_size < dest_off + len) { 20218c2ecf20Sopenharmony_ci rc = smb2_set_file_size(xid, tcon, trgtfile, dest_off + len, false); 20228c2ecf20Sopenharmony_ci if (rc) 20238c2ecf20Sopenharmony_ci goto duplicate_extents_out; 20248c2ecf20Sopenharmony_ci 20258c2ecf20Sopenharmony_ci /* 20268c2ecf20Sopenharmony_ci * Although also could set plausible allocation size (i_blocks) 20278c2ecf20Sopenharmony_ci * here in addition to setting the file size, in reflink 20288c2ecf20Sopenharmony_ci * it is likely that the target file is sparse. Its allocation 20298c2ecf20Sopenharmony_ci * size will be queried on next revalidate, but it is important 20308c2ecf20Sopenharmony_ci * to make sure that file's cached size is updated immediately 20318c2ecf20Sopenharmony_ci */ 20328c2ecf20Sopenharmony_ci cifs_setsize(inode, dest_off + len); 20338c2ecf20Sopenharmony_ci } 20348c2ecf20Sopenharmony_ci rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid, 20358c2ecf20Sopenharmony_ci trgtfile->fid.volatile_fid, 20368c2ecf20Sopenharmony_ci FSCTL_DUPLICATE_EXTENTS_TO_FILE, 20378c2ecf20Sopenharmony_ci (char *)&dup_ext_buf, 20388c2ecf20Sopenharmony_ci sizeof(struct duplicate_extents_to_file), 20398c2ecf20Sopenharmony_ci CIFSMaxBufSize, NULL, 20408c2ecf20Sopenharmony_ci &ret_data_len); 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_ci if (ret_data_len > 0) 20438c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Non-zero response length in duplicate extents\n"); 20448c2ecf20Sopenharmony_ci 20458c2ecf20Sopenharmony_ciduplicate_extents_out: 20468c2ecf20Sopenharmony_ci return rc; 20478c2ecf20Sopenharmony_ci} 20488c2ecf20Sopenharmony_ci 20498c2ecf20Sopenharmony_cistatic int 20508c2ecf20Sopenharmony_cismb2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, 20518c2ecf20Sopenharmony_ci struct cifsFileInfo *cfile) 20528c2ecf20Sopenharmony_ci{ 20538c2ecf20Sopenharmony_ci return SMB2_set_compression(xid, tcon, cfile->fid.persistent_fid, 20548c2ecf20Sopenharmony_ci cfile->fid.volatile_fid); 20558c2ecf20Sopenharmony_ci} 20568c2ecf20Sopenharmony_ci 20578c2ecf20Sopenharmony_cistatic int 20588c2ecf20Sopenharmony_cismb3_set_integrity(const unsigned int xid, struct cifs_tcon *tcon, 20598c2ecf20Sopenharmony_ci struct cifsFileInfo *cfile) 20608c2ecf20Sopenharmony_ci{ 20618c2ecf20Sopenharmony_ci struct fsctl_set_integrity_information_req integr_info; 20628c2ecf20Sopenharmony_ci unsigned int ret_data_len; 20638c2ecf20Sopenharmony_ci 20648c2ecf20Sopenharmony_ci integr_info.ChecksumAlgorithm = cpu_to_le16(CHECKSUM_TYPE_UNCHANGED); 20658c2ecf20Sopenharmony_ci integr_info.Flags = 0; 20668c2ecf20Sopenharmony_ci integr_info.Reserved = 0; 20678c2ecf20Sopenharmony_ci 20688c2ecf20Sopenharmony_ci return SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, 20698c2ecf20Sopenharmony_ci cfile->fid.volatile_fid, 20708c2ecf20Sopenharmony_ci FSCTL_SET_INTEGRITY_INFORMATION, 20718c2ecf20Sopenharmony_ci (char *)&integr_info, 20728c2ecf20Sopenharmony_ci sizeof(struct fsctl_set_integrity_information_req), 20738c2ecf20Sopenharmony_ci CIFSMaxBufSize, NULL, 20748c2ecf20Sopenharmony_ci &ret_data_len); 20758c2ecf20Sopenharmony_ci 20768c2ecf20Sopenharmony_ci} 20778c2ecf20Sopenharmony_ci 20788c2ecf20Sopenharmony_ci/* GMT Token is @GMT-YYYY.MM.DD-HH.MM.SS Unicode which is 48 bytes + null */ 20798c2ecf20Sopenharmony_ci#define GMT_TOKEN_SIZE 50 20808c2ecf20Sopenharmony_ci 20818c2ecf20Sopenharmony_ci#define MIN_SNAPSHOT_ARRAY_SIZE 16 /* See MS-SMB2 section 3.3.5.15.1 */ 20828c2ecf20Sopenharmony_ci 20838c2ecf20Sopenharmony_ci/* 20848c2ecf20Sopenharmony_ci * Input buffer contains (empty) struct smb_snapshot array with size filled in 20858c2ecf20Sopenharmony_ci * For output see struct SRV_SNAPSHOT_ARRAY in MS-SMB2 section 2.2.32.2 20868c2ecf20Sopenharmony_ci */ 20878c2ecf20Sopenharmony_cistatic int 20888c2ecf20Sopenharmony_cismb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon, 20898c2ecf20Sopenharmony_ci struct cifsFileInfo *cfile, void __user *ioc_buf) 20908c2ecf20Sopenharmony_ci{ 20918c2ecf20Sopenharmony_ci char *retbuf = NULL; 20928c2ecf20Sopenharmony_ci unsigned int ret_data_len = 0; 20938c2ecf20Sopenharmony_ci int rc; 20948c2ecf20Sopenharmony_ci u32 max_response_size; 20958c2ecf20Sopenharmony_ci struct smb_snapshot_array snapshot_in; 20968c2ecf20Sopenharmony_ci 20978c2ecf20Sopenharmony_ci /* 20988c2ecf20Sopenharmony_ci * On the first query to enumerate the list of snapshots available 20998c2ecf20Sopenharmony_ci * for this volume the buffer begins with 0 (number of snapshots 21008c2ecf20Sopenharmony_ci * which can be returned is zero since at that point we do not know 21018c2ecf20Sopenharmony_ci * how big the buffer needs to be). On the second query, 21028c2ecf20Sopenharmony_ci * it (ret_data_len) is set to number of snapshots so we can 21038c2ecf20Sopenharmony_ci * know to set the maximum response size larger (see below). 21048c2ecf20Sopenharmony_ci */ 21058c2ecf20Sopenharmony_ci if (get_user(ret_data_len, (unsigned int __user *)ioc_buf)) 21068c2ecf20Sopenharmony_ci return -EFAULT; 21078c2ecf20Sopenharmony_ci 21088c2ecf20Sopenharmony_ci /* 21098c2ecf20Sopenharmony_ci * Note that for snapshot queries that servers like Azure expect that 21108c2ecf20Sopenharmony_ci * the first query be minimal size (and just used to get the number/size 21118c2ecf20Sopenharmony_ci * of previous versions) so response size must be specified as EXACTLY 21128c2ecf20Sopenharmony_ci * sizeof(struct snapshot_array) which is 16 when rounded up to multiple 21138c2ecf20Sopenharmony_ci * of eight bytes. 21148c2ecf20Sopenharmony_ci */ 21158c2ecf20Sopenharmony_ci if (ret_data_len == 0) 21168c2ecf20Sopenharmony_ci max_response_size = MIN_SNAPSHOT_ARRAY_SIZE; 21178c2ecf20Sopenharmony_ci else 21188c2ecf20Sopenharmony_ci max_response_size = CIFSMaxBufSize; 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_ci rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, 21218c2ecf20Sopenharmony_ci cfile->fid.volatile_fid, 21228c2ecf20Sopenharmony_ci FSCTL_SRV_ENUMERATE_SNAPSHOTS, 21238c2ecf20Sopenharmony_ci NULL, 0 /* no input data */, max_response_size, 21248c2ecf20Sopenharmony_ci (char **)&retbuf, 21258c2ecf20Sopenharmony_ci &ret_data_len); 21268c2ecf20Sopenharmony_ci cifs_dbg(FYI, "enum snaphots ioctl returned %d and ret buflen is %d\n", 21278c2ecf20Sopenharmony_ci rc, ret_data_len); 21288c2ecf20Sopenharmony_ci if (rc) 21298c2ecf20Sopenharmony_ci return rc; 21308c2ecf20Sopenharmony_ci 21318c2ecf20Sopenharmony_ci if (ret_data_len && (ioc_buf != NULL) && (retbuf != NULL)) { 21328c2ecf20Sopenharmony_ci /* Fixup buffer */ 21338c2ecf20Sopenharmony_ci if (copy_from_user(&snapshot_in, ioc_buf, 21348c2ecf20Sopenharmony_ci sizeof(struct smb_snapshot_array))) { 21358c2ecf20Sopenharmony_ci rc = -EFAULT; 21368c2ecf20Sopenharmony_ci kfree(retbuf); 21378c2ecf20Sopenharmony_ci return rc; 21388c2ecf20Sopenharmony_ci } 21398c2ecf20Sopenharmony_ci 21408c2ecf20Sopenharmony_ci /* 21418c2ecf20Sopenharmony_ci * Check for min size, ie not large enough to fit even one GMT 21428c2ecf20Sopenharmony_ci * token (snapshot). On the first ioctl some users may pass in 21438c2ecf20Sopenharmony_ci * smaller size (or zero) to simply get the size of the array 21448c2ecf20Sopenharmony_ci * so the user space caller can allocate sufficient memory 21458c2ecf20Sopenharmony_ci * and retry the ioctl again with larger array size sufficient 21468c2ecf20Sopenharmony_ci * to hold all of the snapshot GMT tokens on the second try. 21478c2ecf20Sopenharmony_ci */ 21488c2ecf20Sopenharmony_ci if (snapshot_in.snapshot_array_size < GMT_TOKEN_SIZE) 21498c2ecf20Sopenharmony_ci ret_data_len = sizeof(struct smb_snapshot_array); 21508c2ecf20Sopenharmony_ci 21518c2ecf20Sopenharmony_ci /* 21528c2ecf20Sopenharmony_ci * We return struct SRV_SNAPSHOT_ARRAY, followed by 21538c2ecf20Sopenharmony_ci * the snapshot array (of 50 byte GMT tokens) each 21548c2ecf20Sopenharmony_ci * representing an available previous version of the data 21558c2ecf20Sopenharmony_ci */ 21568c2ecf20Sopenharmony_ci if (ret_data_len > (snapshot_in.snapshot_array_size + 21578c2ecf20Sopenharmony_ci sizeof(struct smb_snapshot_array))) 21588c2ecf20Sopenharmony_ci ret_data_len = snapshot_in.snapshot_array_size + 21598c2ecf20Sopenharmony_ci sizeof(struct smb_snapshot_array); 21608c2ecf20Sopenharmony_ci 21618c2ecf20Sopenharmony_ci if (copy_to_user(ioc_buf, retbuf, ret_data_len)) 21628c2ecf20Sopenharmony_ci rc = -EFAULT; 21638c2ecf20Sopenharmony_ci } 21648c2ecf20Sopenharmony_ci 21658c2ecf20Sopenharmony_ci kfree(retbuf); 21668c2ecf20Sopenharmony_ci return rc; 21678c2ecf20Sopenharmony_ci} 21688c2ecf20Sopenharmony_ci 21698c2ecf20Sopenharmony_ci 21708c2ecf20Sopenharmony_ci 21718c2ecf20Sopenharmony_cistatic int 21728c2ecf20Sopenharmony_cismb3_notify(const unsigned int xid, struct file *pfile, 21738c2ecf20Sopenharmony_ci void __user *ioc_buf) 21748c2ecf20Sopenharmony_ci{ 21758c2ecf20Sopenharmony_ci struct smb3_notify notify; 21768c2ecf20Sopenharmony_ci struct dentry *dentry = pfile->f_path.dentry; 21778c2ecf20Sopenharmony_ci struct inode *inode = file_inode(pfile); 21788c2ecf20Sopenharmony_ci struct cifs_sb_info *cifs_sb; 21798c2ecf20Sopenharmony_ci struct cifs_open_parms oparms; 21808c2ecf20Sopenharmony_ci struct cifs_fid fid; 21818c2ecf20Sopenharmony_ci struct cifs_tcon *tcon; 21828c2ecf20Sopenharmony_ci unsigned char *path = NULL; 21838c2ecf20Sopenharmony_ci __le16 *utf16_path = NULL; 21848c2ecf20Sopenharmony_ci u8 oplock = SMB2_OPLOCK_LEVEL_NONE; 21858c2ecf20Sopenharmony_ci int rc = 0; 21868c2ecf20Sopenharmony_ci 21878c2ecf20Sopenharmony_ci path = build_path_from_dentry(dentry); 21888c2ecf20Sopenharmony_ci if (path == NULL) 21898c2ecf20Sopenharmony_ci return -ENOMEM; 21908c2ecf20Sopenharmony_ci 21918c2ecf20Sopenharmony_ci cifs_sb = CIFS_SB(inode->i_sb); 21928c2ecf20Sopenharmony_ci 21938c2ecf20Sopenharmony_ci utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); 21948c2ecf20Sopenharmony_ci if (utf16_path == NULL) { 21958c2ecf20Sopenharmony_ci rc = -ENOMEM; 21968c2ecf20Sopenharmony_ci goto notify_exit; 21978c2ecf20Sopenharmony_ci } 21988c2ecf20Sopenharmony_ci 21998c2ecf20Sopenharmony_ci if (copy_from_user(¬ify, ioc_buf, sizeof(struct smb3_notify))) { 22008c2ecf20Sopenharmony_ci rc = -EFAULT; 22018c2ecf20Sopenharmony_ci goto notify_exit; 22028c2ecf20Sopenharmony_ci } 22038c2ecf20Sopenharmony_ci 22048c2ecf20Sopenharmony_ci tcon = cifs_sb_master_tcon(cifs_sb); 22058c2ecf20Sopenharmony_ci oparms.tcon = tcon; 22068c2ecf20Sopenharmony_ci oparms.desired_access = FILE_READ_ATTRIBUTES | FILE_READ_DATA; 22078c2ecf20Sopenharmony_ci oparms.disposition = FILE_OPEN; 22088c2ecf20Sopenharmony_ci oparms.create_options = cifs_create_options(cifs_sb, 0); 22098c2ecf20Sopenharmony_ci oparms.fid = &fid; 22108c2ecf20Sopenharmony_ci oparms.reconnect = false; 22118c2ecf20Sopenharmony_ci 22128c2ecf20Sopenharmony_ci rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL, 22138c2ecf20Sopenharmony_ci NULL); 22148c2ecf20Sopenharmony_ci if (rc) 22158c2ecf20Sopenharmony_ci goto notify_exit; 22168c2ecf20Sopenharmony_ci 22178c2ecf20Sopenharmony_ci rc = SMB2_change_notify(xid, tcon, fid.persistent_fid, fid.volatile_fid, 22188c2ecf20Sopenharmony_ci notify.watch_tree, notify.completion_filter); 22198c2ecf20Sopenharmony_ci 22208c2ecf20Sopenharmony_ci SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); 22218c2ecf20Sopenharmony_ci 22228c2ecf20Sopenharmony_ci cifs_dbg(FYI, "change notify for path %s rc %d\n", path, rc); 22238c2ecf20Sopenharmony_ci 22248c2ecf20Sopenharmony_cinotify_exit: 22258c2ecf20Sopenharmony_ci kfree(path); 22268c2ecf20Sopenharmony_ci kfree(utf16_path); 22278c2ecf20Sopenharmony_ci return rc; 22288c2ecf20Sopenharmony_ci} 22298c2ecf20Sopenharmony_ci 22308c2ecf20Sopenharmony_cistatic int 22318c2ecf20Sopenharmony_cismb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon, 22328c2ecf20Sopenharmony_ci const char *path, struct cifs_sb_info *cifs_sb, 22338c2ecf20Sopenharmony_ci struct cifs_fid *fid, __u16 search_flags, 22348c2ecf20Sopenharmony_ci struct cifs_search_info *srch_inf) 22358c2ecf20Sopenharmony_ci{ 22368c2ecf20Sopenharmony_ci __le16 *utf16_path; 22378c2ecf20Sopenharmony_ci struct smb_rqst rqst[2]; 22388c2ecf20Sopenharmony_ci struct kvec rsp_iov[2]; 22398c2ecf20Sopenharmony_ci int resp_buftype[2]; 22408c2ecf20Sopenharmony_ci struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; 22418c2ecf20Sopenharmony_ci struct kvec qd_iov[SMB2_QUERY_DIRECTORY_IOV_SIZE]; 22428c2ecf20Sopenharmony_ci int rc, flags = 0; 22438c2ecf20Sopenharmony_ci u8 oplock = SMB2_OPLOCK_LEVEL_NONE; 22448c2ecf20Sopenharmony_ci struct cifs_open_parms oparms; 22458c2ecf20Sopenharmony_ci struct smb2_query_directory_rsp *qd_rsp = NULL; 22468c2ecf20Sopenharmony_ci struct smb2_create_rsp *op_rsp = NULL; 22478c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); 22488c2ecf20Sopenharmony_ci 22498c2ecf20Sopenharmony_ci utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); 22508c2ecf20Sopenharmony_ci if (!utf16_path) 22518c2ecf20Sopenharmony_ci return -ENOMEM; 22528c2ecf20Sopenharmony_ci 22538c2ecf20Sopenharmony_ci if (smb3_encryption_required(tcon)) 22548c2ecf20Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 22558c2ecf20Sopenharmony_ci 22568c2ecf20Sopenharmony_ci memset(rqst, 0, sizeof(rqst)); 22578c2ecf20Sopenharmony_ci resp_buftype[0] = resp_buftype[1] = CIFS_NO_BUFFER; 22588c2ecf20Sopenharmony_ci memset(rsp_iov, 0, sizeof(rsp_iov)); 22598c2ecf20Sopenharmony_ci 22608c2ecf20Sopenharmony_ci /* Open */ 22618c2ecf20Sopenharmony_ci memset(&open_iov, 0, sizeof(open_iov)); 22628c2ecf20Sopenharmony_ci rqst[0].rq_iov = open_iov; 22638c2ecf20Sopenharmony_ci rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; 22648c2ecf20Sopenharmony_ci 22658c2ecf20Sopenharmony_ci oparms.tcon = tcon; 22668c2ecf20Sopenharmony_ci oparms.desired_access = FILE_READ_ATTRIBUTES | FILE_READ_DATA; 22678c2ecf20Sopenharmony_ci oparms.disposition = FILE_OPEN; 22688c2ecf20Sopenharmony_ci oparms.create_options = cifs_create_options(cifs_sb, 0); 22698c2ecf20Sopenharmony_ci oparms.fid = fid; 22708c2ecf20Sopenharmony_ci oparms.reconnect = false; 22718c2ecf20Sopenharmony_ci 22728c2ecf20Sopenharmony_ci rc = SMB2_open_init(tcon, server, 22738c2ecf20Sopenharmony_ci &rqst[0], &oplock, &oparms, utf16_path); 22748c2ecf20Sopenharmony_ci if (rc) 22758c2ecf20Sopenharmony_ci goto qdf_free; 22768c2ecf20Sopenharmony_ci smb2_set_next_command(tcon, &rqst[0]); 22778c2ecf20Sopenharmony_ci 22788c2ecf20Sopenharmony_ci /* Query directory */ 22798c2ecf20Sopenharmony_ci srch_inf->entries_in_buffer = 0; 22808c2ecf20Sopenharmony_ci srch_inf->index_of_last_entry = 2; 22818c2ecf20Sopenharmony_ci 22828c2ecf20Sopenharmony_ci memset(&qd_iov, 0, sizeof(qd_iov)); 22838c2ecf20Sopenharmony_ci rqst[1].rq_iov = qd_iov; 22848c2ecf20Sopenharmony_ci rqst[1].rq_nvec = SMB2_QUERY_DIRECTORY_IOV_SIZE; 22858c2ecf20Sopenharmony_ci 22868c2ecf20Sopenharmony_ci rc = SMB2_query_directory_init(xid, tcon, server, 22878c2ecf20Sopenharmony_ci &rqst[1], 22888c2ecf20Sopenharmony_ci COMPOUND_FID, COMPOUND_FID, 22898c2ecf20Sopenharmony_ci 0, srch_inf->info_level); 22908c2ecf20Sopenharmony_ci if (rc) 22918c2ecf20Sopenharmony_ci goto qdf_free; 22928c2ecf20Sopenharmony_ci 22938c2ecf20Sopenharmony_ci smb2_set_related(&rqst[1]); 22948c2ecf20Sopenharmony_ci 22958c2ecf20Sopenharmony_ci rc = compound_send_recv(xid, tcon->ses, server, 22968c2ecf20Sopenharmony_ci flags, 2, rqst, 22978c2ecf20Sopenharmony_ci resp_buftype, rsp_iov); 22988c2ecf20Sopenharmony_ci 22998c2ecf20Sopenharmony_ci /* If the open failed there is nothing to do */ 23008c2ecf20Sopenharmony_ci op_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base; 23018c2ecf20Sopenharmony_ci if (op_rsp == NULL || op_rsp->sync_hdr.Status != STATUS_SUCCESS) { 23028c2ecf20Sopenharmony_ci cifs_dbg(FYI, "query_dir_first: open failed rc=%d\n", rc); 23038c2ecf20Sopenharmony_ci goto qdf_free; 23048c2ecf20Sopenharmony_ci } 23058c2ecf20Sopenharmony_ci fid->persistent_fid = op_rsp->PersistentFileId; 23068c2ecf20Sopenharmony_ci fid->volatile_fid = op_rsp->VolatileFileId; 23078c2ecf20Sopenharmony_ci 23088c2ecf20Sopenharmony_ci /* Anything else than ENODATA means a genuine error */ 23098c2ecf20Sopenharmony_ci if (rc && rc != -ENODATA) { 23108c2ecf20Sopenharmony_ci SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid); 23118c2ecf20Sopenharmony_ci cifs_dbg(FYI, "query_dir_first: query directory failed rc=%d\n", rc); 23128c2ecf20Sopenharmony_ci trace_smb3_query_dir_err(xid, fid->persistent_fid, 23138c2ecf20Sopenharmony_ci tcon->tid, tcon->ses->Suid, 0, 0, rc); 23148c2ecf20Sopenharmony_ci goto qdf_free; 23158c2ecf20Sopenharmony_ci } 23168c2ecf20Sopenharmony_ci 23178c2ecf20Sopenharmony_ci atomic_inc(&tcon->num_remote_opens); 23188c2ecf20Sopenharmony_ci 23198c2ecf20Sopenharmony_ci qd_rsp = (struct smb2_query_directory_rsp *)rsp_iov[1].iov_base; 23208c2ecf20Sopenharmony_ci if (qd_rsp->sync_hdr.Status == STATUS_NO_MORE_FILES) { 23218c2ecf20Sopenharmony_ci trace_smb3_query_dir_done(xid, fid->persistent_fid, 23228c2ecf20Sopenharmony_ci tcon->tid, tcon->ses->Suid, 0, 0); 23238c2ecf20Sopenharmony_ci srch_inf->endOfSearch = true; 23248c2ecf20Sopenharmony_ci rc = 0; 23258c2ecf20Sopenharmony_ci goto qdf_free; 23268c2ecf20Sopenharmony_ci } 23278c2ecf20Sopenharmony_ci 23288c2ecf20Sopenharmony_ci rc = smb2_parse_query_directory(tcon, &rsp_iov[1], resp_buftype[1], 23298c2ecf20Sopenharmony_ci srch_inf); 23308c2ecf20Sopenharmony_ci if (rc) { 23318c2ecf20Sopenharmony_ci trace_smb3_query_dir_err(xid, fid->persistent_fid, tcon->tid, 23328c2ecf20Sopenharmony_ci tcon->ses->Suid, 0, 0, rc); 23338c2ecf20Sopenharmony_ci goto qdf_free; 23348c2ecf20Sopenharmony_ci } 23358c2ecf20Sopenharmony_ci resp_buftype[1] = CIFS_NO_BUFFER; 23368c2ecf20Sopenharmony_ci 23378c2ecf20Sopenharmony_ci trace_smb3_query_dir_done(xid, fid->persistent_fid, tcon->tid, 23388c2ecf20Sopenharmony_ci tcon->ses->Suid, 0, srch_inf->entries_in_buffer); 23398c2ecf20Sopenharmony_ci 23408c2ecf20Sopenharmony_ci qdf_free: 23418c2ecf20Sopenharmony_ci kfree(utf16_path); 23428c2ecf20Sopenharmony_ci SMB2_open_free(&rqst[0]); 23438c2ecf20Sopenharmony_ci SMB2_query_directory_free(&rqst[1]); 23448c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); 23458c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); 23468c2ecf20Sopenharmony_ci return rc; 23478c2ecf20Sopenharmony_ci} 23488c2ecf20Sopenharmony_ci 23498c2ecf20Sopenharmony_cistatic int 23508c2ecf20Sopenharmony_cismb2_query_dir_next(const unsigned int xid, struct cifs_tcon *tcon, 23518c2ecf20Sopenharmony_ci struct cifs_fid *fid, __u16 search_flags, 23528c2ecf20Sopenharmony_ci struct cifs_search_info *srch_inf) 23538c2ecf20Sopenharmony_ci{ 23548c2ecf20Sopenharmony_ci return SMB2_query_directory(xid, tcon, fid->persistent_fid, 23558c2ecf20Sopenharmony_ci fid->volatile_fid, 0, srch_inf); 23568c2ecf20Sopenharmony_ci} 23578c2ecf20Sopenharmony_ci 23588c2ecf20Sopenharmony_cistatic int 23598c2ecf20Sopenharmony_cismb2_close_dir(const unsigned int xid, struct cifs_tcon *tcon, 23608c2ecf20Sopenharmony_ci struct cifs_fid *fid) 23618c2ecf20Sopenharmony_ci{ 23628c2ecf20Sopenharmony_ci return SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid); 23638c2ecf20Sopenharmony_ci} 23648c2ecf20Sopenharmony_ci 23658c2ecf20Sopenharmony_ci/* 23668c2ecf20Sopenharmony_ci * If we negotiate SMB2 protocol and get STATUS_PENDING - update 23678c2ecf20Sopenharmony_ci * the number of credits and return true. Otherwise - return false. 23688c2ecf20Sopenharmony_ci */ 23698c2ecf20Sopenharmony_cistatic bool 23708c2ecf20Sopenharmony_cismb2_is_status_pending(char *buf, struct TCP_Server_Info *server) 23718c2ecf20Sopenharmony_ci{ 23728c2ecf20Sopenharmony_ci struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf; 23738c2ecf20Sopenharmony_ci 23748c2ecf20Sopenharmony_ci if (shdr->Status != STATUS_PENDING) 23758c2ecf20Sopenharmony_ci return false; 23768c2ecf20Sopenharmony_ci 23778c2ecf20Sopenharmony_ci if (shdr->CreditRequest) { 23788c2ecf20Sopenharmony_ci spin_lock(&server->req_lock); 23798c2ecf20Sopenharmony_ci server->credits += le16_to_cpu(shdr->CreditRequest); 23808c2ecf20Sopenharmony_ci spin_unlock(&server->req_lock); 23818c2ecf20Sopenharmony_ci wake_up(&server->request_q); 23828c2ecf20Sopenharmony_ci } 23838c2ecf20Sopenharmony_ci 23848c2ecf20Sopenharmony_ci return true; 23858c2ecf20Sopenharmony_ci} 23868c2ecf20Sopenharmony_ci 23878c2ecf20Sopenharmony_cistatic bool 23888c2ecf20Sopenharmony_cismb2_is_session_expired(char *buf) 23898c2ecf20Sopenharmony_ci{ 23908c2ecf20Sopenharmony_ci struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf; 23918c2ecf20Sopenharmony_ci 23928c2ecf20Sopenharmony_ci if (shdr->Status != STATUS_NETWORK_SESSION_EXPIRED && 23938c2ecf20Sopenharmony_ci shdr->Status != STATUS_USER_SESSION_DELETED) 23948c2ecf20Sopenharmony_ci return false; 23958c2ecf20Sopenharmony_ci 23968c2ecf20Sopenharmony_ci trace_smb3_ses_expired(shdr->TreeId, shdr->SessionId, 23978c2ecf20Sopenharmony_ci le16_to_cpu(shdr->Command), 23988c2ecf20Sopenharmony_ci le64_to_cpu(shdr->MessageId)); 23998c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Session expired or deleted\n"); 24008c2ecf20Sopenharmony_ci 24018c2ecf20Sopenharmony_ci return true; 24028c2ecf20Sopenharmony_ci} 24038c2ecf20Sopenharmony_ci 24048c2ecf20Sopenharmony_cistatic bool 24058c2ecf20Sopenharmony_cismb2_is_status_io_timeout(char *buf) 24068c2ecf20Sopenharmony_ci{ 24078c2ecf20Sopenharmony_ci struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf; 24088c2ecf20Sopenharmony_ci 24098c2ecf20Sopenharmony_ci if (shdr->Status == STATUS_IO_TIMEOUT) 24108c2ecf20Sopenharmony_ci return true; 24118c2ecf20Sopenharmony_ci else 24128c2ecf20Sopenharmony_ci return false; 24138c2ecf20Sopenharmony_ci} 24148c2ecf20Sopenharmony_ci 24158c2ecf20Sopenharmony_cistatic int 24168c2ecf20Sopenharmony_cismb2_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid, 24178c2ecf20Sopenharmony_ci struct cifsInodeInfo *cinode) 24188c2ecf20Sopenharmony_ci{ 24198c2ecf20Sopenharmony_ci if (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING) 24208c2ecf20Sopenharmony_ci return SMB2_lease_break(0, tcon, cinode->lease_key, 24218c2ecf20Sopenharmony_ci smb2_get_lease_state(cinode)); 24228c2ecf20Sopenharmony_ci 24238c2ecf20Sopenharmony_ci return SMB2_oplock_break(0, tcon, fid->persistent_fid, 24248c2ecf20Sopenharmony_ci fid->volatile_fid, 24258c2ecf20Sopenharmony_ci CIFS_CACHE_READ(cinode) ? 1 : 0); 24268c2ecf20Sopenharmony_ci} 24278c2ecf20Sopenharmony_ci 24288c2ecf20Sopenharmony_civoid 24298c2ecf20Sopenharmony_cismb2_set_related(struct smb_rqst *rqst) 24308c2ecf20Sopenharmony_ci{ 24318c2ecf20Sopenharmony_ci struct smb2_sync_hdr *shdr; 24328c2ecf20Sopenharmony_ci 24338c2ecf20Sopenharmony_ci shdr = (struct smb2_sync_hdr *)(rqst->rq_iov[0].iov_base); 24348c2ecf20Sopenharmony_ci if (shdr == NULL) { 24358c2ecf20Sopenharmony_ci cifs_dbg(FYI, "shdr NULL in smb2_set_related\n"); 24368c2ecf20Sopenharmony_ci return; 24378c2ecf20Sopenharmony_ci } 24388c2ecf20Sopenharmony_ci shdr->Flags |= SMB2_FLAGS_RELATED_OPERATIONS; 24398c2ecf20Sopenharmony_ci} 24408c2ecf20Sopenharmony_ci 24418c2ecf20Sopenharmony_cichar smb2_padding[7] = {0, 0, 0, 0, 0, 0, 0}; 24428c2ecf20Sopenharmony_ci 24438c2ecf20Sopenharmony_civoid 24448c2ecf20Sopenharmony_cismb2_set_next_command(struct cifs_tcon *tcon, struct smb_rqst *rqst) 24458c2ecf20Sopenharmony_ci{ 24468c2ecf20Sopenharmony_ci struct smb2_sync_hdr *shdr; 24478c2ecf20Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 24488c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = ses->server; 24498c2ecf20Sopenharmony_ci unsigned long len = smb_rqst_len(server, rqst); 24508c2ecf20Sopenharmony_ci int i, num_padding; 24518c2ecf20Sopenharmony_ci 24528c2ecf20Sopenharmony_ci shdr = (struct smb2_sync_hdr *)(rqst->rq_iov[0].iov_base); 24538c2ecf20Sopenharmony_ci if (shdr == NULL) { 24548c2ecf20Sopenharmony_ci cifs_dbg(FYI, "shdr NULL in smb2_set_next_command\n"); 24558c2ecf20Sopenharmony_ci return; 24568c2ecf20Sopenharmony_ci } 24578c2ecf20Sopenharmony_ci 24588c2ecf20Sopenharmony_ci /* SMB headers in a compound are 8 byte aligned. */ 24598c2ecf20Sopenharmony_ci 24608c2ecf20Sopenharmony_ci /* No padding needed */ 24618c2ecf20Sopenharmony_ci if (!(len & 7)) 24628c2ecf20Sopenharmony_ci goto finished; 24638c2ecf20Sopenharmony_ci 24648c2ecf20Sopenharmony_ci num_padding = 8 - (len & 7); 24658c2ecf20Sopenharmony_ci if (!smb3_encryption_required(tcon)) { 24668c2ecf20Sopenharmony_ci /* 24678c2ecf20Sopenharmony_ci * If we do not have encryption then we can just add an extra 24688c2ecf20Sopenharmony_ci * iov for the padding. 24698c2ecf20Sopenharmony_ci */ 24708c2ecf20Sopenharmony_ci rqst->rq_iov[rqst->rq_nvec].iov_base = smb2_padding; 24718c2ecf20Sopenharmony_ci rqst->rq_iov[rqst->rq_nvec].iov_len = num_padding; 24728c2ecf20Sopenharmony_ci rqst->rq_nvec++; 24738c2ecf20Sopenharmony_ci len += num_padding; 24748c2ecf20Sopenharmony_ci } else { 24758c2ecf20Sopenharmony_ci /* 24768c2ecf20Sopenharmony_ci * We can not add a small padding iov for the encryption case 24778c2ecf20Sopenharmony_ci * because the encryption framework can not handle the padding 24788c2ecf20Sopenharmony_ci * iovs. 24798c2ecf20Sopenharmony_ci * We have to flatten this into a single buffer and add 24808c2ecf20Sopenharmony_ci * the padding to it. 24818c2ecf20Sopenharmony_ci */ 24828c2ecf20Sopenharmony_ci for (i = 1; i < rqst->rq_nvec; i++) { 24838c2ecf20Sopenharmony_ci memcpy(rqst->rq_iov[0].iov_base + 24848c2ecf20Sopenharmony_ci rqst->rq_iov[0].iov_len, 24858c2ecf20Sopenharmony_ci rqst->rq_iov[i].iov_base, 24868c2ecf20Sopenharmony_ci rqst->rq_iov[i].iov_len); 24878c2ecf20Sopenharmony_ci rqst->rq_iov[0].iov_len += rqst->rq_iov[i].iov_len; 24888c2ecf20Sopenharmony_ci } 24898c2ecf20Sopenharmony_ci memset(rqst->rq_iov[0].iov_base + rqst->rq_iov[0].iov_len, 24908c2ecf20Sopenharmony_ci 0, num_padding); 24918c2ecf20Sopenharmony_ci rqst->rq_iov[0].iov_len += num_padding; 24928c2ecf20Sopenharmony_ci len += num_padding; 24938c2ecf20Sopenharmony_ci rqst->rq_nvec = 1; 24948c2ecf20Sopenharmony_ci } 24958c2ecf20Sopenharmony_ci 24968c2ecf20Sopenharmony_ci finished: 24978c2ecf20Sopenharmony_ci shdr->NextCommand = cpu_to_le32(len); 24988c2ecf20Sopenharmony_ci} 24998c2ecf20Sopenharmony_ci 25008c2ecf20Sopenharmony_ci/* 25018c2ecf20Sopenharmony_ci * Passes the query info response back to the caller on success. 25028c2ecf20Sopenharmony_ci * Caller need to free this with free_rsp_buf(). 25038c2ecf20Sopenharmony_ci */ 25048c2ecf20Sopenharmony_ciint 25058c2ecf20Sopenharmony_cismb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon, 25068c2ecf20Sopenharmony_ci __le16 *utf16_path, u32 desired_access, 25078c2ecf20Sopenharmony_ci u32 class, u32 type, u32 output_len, 25088c2ecf20Sopenharmony_ci struct kvec *rsp, int *buftype, 25098c2ecf20Sopenharmony_ci struct cifs_sb_info *cifs_sb) 25108c2ecf20Sopenharmony_ci{ 25118c2ecf20Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 25128c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(ses); 25138c2ecf20Sopenharmony_ci int flags = CIFS_CP_CREATE_CLOSE_OP; 25148c2ecf20Sopenharmony_ci struct smb_rqst rqst[3]; 25158c2ecf20Sopenharmony_ci int resp_buftype[3]; 25168c2ecf20Sopenharmony_ci struct kvec rsp_iov[3]; 25178c2ecf20Sopenharmony_ci struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; 25188c2ecf20Sopenharmony_ci struct kvec qi_iov[1]; 25198c2ecf20Sopenharmony_ci struct kvec close_iov[1]; 25208c2ecf20Sopenharmony_ci u8 oplock = SMB2_OPLOCK_LEVEL_NONE; 25218c2ecf20Sopenharmony_ci struct cifs_open_parms oparms; 25228c2ecf20Sopenharmony_ci struct cifs_fid fid; 25238c2ecf20Sopenharmony_ci int rc; 25248c2ecf20Sopenharmony_ci 25258c2ecf20Sopenharmony_ci if (smb3_encryption_required(tcon)) 25268c2ecf20Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 25278c2ecf20Sopenharmony_ci 25288c2ecf20Sopenharmony_ci memset(rqst, 0, sizeof(rqst)); 25298c2ecf20Sopenharmony_ci resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; 25308c2ecf20Sopenharmony_ci memset(rsp_iov, 0, sizeof(rsp_iov)); 25318c2ecf20Sopenharmony_ci 25328c2ecf20Sopenharmony_ci memset(&open_iov, 0, sizeof(open_iov)); 25338c2ecf20Sopenharmony_ci rqst[0].rq_iov = open_iov; 25348c2ecf20Sopenharmony_ci rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; 25358c2ecf20Sopenharmony_ci 25368c2ecf20Sopenharmony_ci oparms.tcon = tcon; 25378c2ecf20Sopenharmony_ci oparms.desired_access = desired_access; 25388c2ecf20Sopenharmony_ci oparms.disposition = FILE_OPEN; 25398c2ecf20Sopenharmony_ci oparms.create_options = cifs_create_options(cifs_sb, 0); 25408c2ecf20Sopenharmony_ci oparms.fid = &fid; 25418c2ecf20Sopenharmony_ci oparms.reconnect = false; 25428c2ecf20Sopenharmony_ci 25438c2ecf20Sopenharmony_ci rc = SMB2_open_init(tcon, server, 25448c2ecf20Sopenharmony_ci &rqst[0], &oplock, &oparms, utf16_path); 25458c2ecf20Sopenharmony_ci if (rc) 25468c2ecf20Sopenharmony_ci goto qic_exit; 25478c2ecf20Sopenharmony_ci smb2_set_next_command(tcon, &rqst[0]); 25488c2ecf20Sopenharmony_ci 25498c2ecf20Sopenharmony_ci memset(&qi_iov, 0, sizeof(qi_iov)); 25508c2ecf20Sopenharmony_ci rqst[1].rq_iov = qi_iov; 25518c2ecf20Sopenharmony_ci rqst[1].rq_nvec = 1; 25528c2ecf20Sopenharmony_ci 25538c2ecf20Sopenharmony_ci rc = SMB2_query_info_init(tcon, server, 25548c2ecf20Sopenharmony_ci &rqst[1], COMPOUND_FID, COMPOUND_FID, 25558c2ecf20Sopenharmony_ci class, type, 0, 25568c2ecf20Sopenharmony_ci output_len, 0, 25578c2ecf20Sopenharmony_ci NULL); 25588c2ecf20Sopenharmony_ci if (rc) 25598c2ecf20Sopenharmony_ci goto qic_exit; 25608c2ecf20Sopenharmony_ci smb2_set_next_command(tcon, &rqst[1]); 25618c2ecf20Sopenharmony_ci smb2_set_related(&rqst[1]); 25628c2ecf20Sopenharmony_ci 25638c2ecf20Sopenharmony_ci memset(&close_iov, 0, sizeof(close_iov)); 25648c2ecf20Sopenharmony_ci rqst[2].rq_iov = close_iov; 25658c2ecf20Sopenharmony_ci rqst[2].rq_nvec = 1; 25668c2ecf20Sopenharmony_ci 25678c2ecf20Sopenharmony_ci rc = SMB2_close_init(tcon, server, 25688c2ecf20Sopenharmony_ci &rqst[2], COMPOUND_FID, COMPOUND_FID, false); 25698c2ecf20Sopenharmony_ci if (rc) 25708c2ecf20Sopenharmony_ci goto qic_exit; 25718c2ecf20Sopenharmony_ci smb2_set_related(&rqst[2]); 25728c2ecf20Sopenharmony_ci 25738c2ecf20Sopenharmony_ci rc = compound_send_recv(xid, ses, server, 25748c2ecf20Sopenharmony_ci flags, 3, rqst, 25758c2ecf20Sopenharmony_ci resp_buftype, rsp_iov); 25768c2ecf20Sopenharmony_ci if (rc) { 25778c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); 25788c2ecf20Sopenharmony_ci if (rc == -EREMCHG) { 25798c2ecf20Sopenharmony_ci tcon->need_reconnect = true; 25808c2ecf20Sopenharmony_ci pr_warn_once("server share %s deleted\n", 25818c2ecf20Sopenharmony_ci tcon->treeName); 25828c2ecf20Sopenharmony_ci } 25838c2ecf20Sopenharmony_ci goto qic_exit; 25848c2ecf20Sopenharmony_ci } 25858c2ecf20Sopenharmony_ci *rsp = rsp_iov[1]; 25868c2ecf20Sopenharmony_ci *buftype = resp_buftype[1]; 25878c2ecf20Sopenharmony_ci 25888c2ecf20Sopenharmony_ci qic_exit: 25898c2ecf20Sopenharmony_ci SMB2_open_free(&rqst[0]); 25908c2ecf20Sopenharmony_ci SMB2_query_info_free(&rqst[1]); 25918c2ecf20Sopenharmony_ci SMB2_close_free(&rqst[2]); 25928c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); 25938c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); 25948c2ecf20Sopenharmony_ci return rc; 25958c2ecf20Sopenharmony_ci} 25968c2ecf20Sopenharmony_ci 25978c2ecf20Sopenharmony_cistatic int 25988c2ecf20Sopenharmony_cismb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon, 25998c2ecf20Sopenharmony_ci struct cifs_sb_info *cifs_sb, struct kstatfs *buf) 26008c2ecf20Sopenharmony_ci{ 26018c2ecf20Sopenharmony_ci struct smb2_query_info_rsp *rsp; 26028c2ecf20Sopenharmony_ci struct smb2_fs_full_size_info *info = NULL; 26038c2ecf20Sopenharmony_ci __le16 utf16_path = 0; /* Null - open root of share */ 26048c2ecf20Sopenharmony_ci struct kvec rsp_iov = {NULL, 0}; 26058c2ecf20Sopenharmony_ci int buftype = CIFS_NO_BUFFER; 26068c2ecf20Sopenharmony_ci int rc; 26078c2ecf20Sopenharmony_ci 26088c2ecf20Sopenharmony_ci 26098c2ecf20Sopenharmony_ci rc = smb2_query_info_compound(xid, tcon, &utf16_path, 26108c2ecf20Sopenharmony_ci FILE_READ_ATTRIBUTES, 26118c2ecf20Sopenharmony_ci FS_FULL_SIZE_INFORMATION, 26128c2ecf20Sopenharmony_ci SMB2_O_INFO_FILESYSTEM, 26138c2ecf20Sopenharmony_ci sizeof(struct smb2_fs_full_size_info), 26148c2ecf20Sopenharmony_ci &rsp_iov, &buftype, cifs_sb); 26158c2ecf20Sopenharmony_ci if (rc) 26168c2ecf20Sopenharmony_ci goto qfs_exit; 26178c2ecf20Sopenharmony_ci 26188c2ecf20Sopenharmony_ci rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; 26198c2ecf20Sopenharmony_ci buf->f_type = SMB2_MAGIC_NUMBER; 26208c2ecf20Sopenharmony_ci info = (struct smb2_fs_full_size_info *)( 26218c2ecf20Sopenharmony_ci le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp); 26228c2ecf20Sopenharmony_ci rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), 26238c2ecf20Sopenharmony_ci le32_to_cpu(rsp->OutputBufferLength), 26248c2ecf20Sopenharmony_ci &rsp_iov, 26258c2ecf20Sopenharmony_ci sizeof(struct smb2_fs_full_size_info)); 26268c2ecf20Sopenharmony_ci if (!rc) 26278c2ecf20Sopenharmony_ci smb2_copy_fs_info_to_kstatfs(info, buf); 26288c2ecf20Sopenharmony_ci 26298c2ecf20Sopenharmony_ciqfs_exit: 26308c2ecf20Sopenharmony_ci free_rsp_buf(buftype, rsp_iov.iov_base); 26318c2ecf20Sopenharmony_ci return rc; 26328c2ecf20Sopenharmony_ci} 26338c2ecf20Sopenharmony_ci 26348c2ecf20Sopenharmony_cistatic int 26358c2ecf20Sopenharmony_cismb311_queryfs(const unsigned int xid, struct cifs_tcon *tcon, 26368c2ecf20Sopenharmony_ci struct cifs_sb_info *cifs_sb, struct kstatfs *buf) 26378c2ecf20Sopenharmony_ci{ 26388c2ecf20Sopenharmony_ci int rc; 26398c2ecf20Sopenharmony_ci __le16 srch_path = 0; /* Null - open root of share */ 26408c2ecf20Sopenharmony_ci u8 oplock = SMB2_OPLOCK_LEVEL_NONE; 26418c2ecf20Sopenharmony_ci struct cifs_open_parms oparms; 26428c2ecf20Sopenharmony_ci struct cifs_fid fid; 26438c2ecf20Sopenharmony_ci 26448c2ecf20Sopenharmony_ci if (!tcon->posix_extensions) 26458c2ecf20Sopenharmony_ci return smb2_queryfs(xid, tcon, cifs_sb, buf); 26468c2ecf20Sopenharmony_ci 26478c2ecf20Sopenharmony_ci oparms.tcon = tcon; 26488c2ecf20Sopenharmony_ci oparms.desired_access = FILE_READ_ATTRIBUTES; 26498c2ecf20Sopenharmony_ci oparms.disposition = FILE_OPEN; 26508c2ecf20Sopenharmony_ci oparms.create_options = cifs_create_options(cifs_sb, 0); 26518c2ecf20Sopenharmony_ci oparms.fid = &fid; 26528c2ecf20Sopenharmony_ci oparms.reconnect = false; 26538c2ecf20Sopenharmony_ci 26548c2ecf20Sopenharmony_ci rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL, 26558c2ecf20Sopenharmony_ci NULL, NULL); 26568c2ecf20Sopenharmony_ci if (rc) 26578c2ecf20Sopenharmony_ci return rc; 26588c2ecf20Sopenharmony_ci 26598c2ecf20Sopenharmony_ci rc = SMB311_posix_qfs_info(xid, tcon, fid.persistent_fid, 26608c2ecf20Sopenharmony_ci fid.volatile_fid, buf); 26618c2ecf20Sopenharmony_ci buf->f_type = SMB2_MAGIC_NUMBER; 26628c2ecf20Sopenharmony_ci SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); 26638c2ecf20Sopenharmony_ci return rc; 26648c2ecf20Sopenharmony_ci} 26658c2ecf20Sopenharmony_ci 26668c2ecf20Sopenharmony_cistatic bool 26678c2ecf20Sopenharmony_cismb2_compare_fids(struct cifsFileInfo *ob1, struct cifsFileInfo *ob2) 26688c2ecf20Sopenharmony_ci{ 26698c2ecf20Sopenharmony_ci return ob1->fid.persistent_fid == ob2->fid.persistent_fid && 26708c2ecf20Sopenharmony_ci ob1->fid.volatile_fid == ob2->fid.volatile_fid; 26718c2ecf20Sopenharmony_ci} 26728c2ecf20Sopenharmony_ci 26738c2ecf20Sopenharmony_cistatic int 26748c2ecf20Sopenharmony_cismb2_mand_lock(const unsigned int xid, struct cifsFileInfo *cfile, __u64 offset, 26758c2ecf20Sopenharmony_ci __u64 length, __u32 type, int lock, int unlock, bool wait) 26768c2ecf20Sopenharmony_ci{ 26778c2ecf20Sopenharmony_ci if (unlock && !lock) 26788c2ecf20Sopenharmony_ci type = SMB2_LOCKFLAG_UNLOCK; 26798c2ecf20Sopenharmony_ci return SMB2_lock(xid, tlink_tcon(cfile->tlink), 26808c2ecf20Sopenharmony_ci cfile->fid.persistent_fid, cfile->fid.volatile_fid, 26818c2ecf20Sopenharmony_ci current->tgid, length, offset, type, wait); 26828c2ecf20Sopenharmony_ci} 26838c2ecf20Sopenharmony_ci 26848c2ecf20Sopenharmony_cistatic void 26858c2ecf20Sopenharmony_cismb2_get_lease_key(struct inode *inode, struct cifs_fid *fid) 26868c2ecf20Sopenharmony_ci{ 26878c2ecf20Sopenharmony_ci memcpy(fid->lease_key, CIFS_I(inode)->lease_key, SMB2_LEASE_KEY_SIZE); 26888c2ecf20Sopenharmony_ci} 26898c2ecf20Sopenharmony_ci 26908c2ecf20Sopenharmony_cistatic void 26918c2ecf20Sopenharmony_cismb2_set_lease_key(struct inode *inode, struct cifs_fid *fid) 26928c2ecf20Sopenharmony_ci{ 26938c2ecf20Sopenharmony_ci memcpy(CIFS_I(inode)->lease_key, fid->lease_key, SMB2_LEASE_KEY_SIZE); 26948c2ecf20Sopenharmony_ci} 26958c2ecf20Sopenharmony_ci 26968c2ecf20Sopenharmony_cistatic void 26978c2ecf20Sopenharmony_cismb2_new_lease_key(struct cifs_fid *fid) 26988c2ecf20Sopenharmony_ci{ 26998c2ecf20Sopenharmony_ci generate_random_uuid(fid->lease_key); 27008c2ecf20Sopenharmony_ci} 27018c2ecf20Sopenharmony_ci 27028c2ecf20Sopenharmony_cistatic int 27038c2ecf20Sopenharmony_cismb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses, 27048c2ecf20Sopenharmony_ci const char *search_name, 27058c2ecf20Sopenharmony_ci struct dfs_info3_param **target_nodes, 27068c2ecf20Sopenharmony_ci unsigned int *num_of_nodes, 27078c2ecf20Sopenharmony_ci const struct nls_table *nls_codepage, int remap) 27088c2ecf20Sopenharmony_ci{ 27098c2ecf20Sopenharmony_ci int rc; 27108c2ecf20Sopenharmony_ci __le16 *utf16_path = NULL; 27118c2ecf20Sopenharmony_ci int utf16_path_len = 0; 27128c2ecf20Sopenharmony_ci struct cifs_tcon *tcon; 27138c2ecf20Sopenharmony_ci struct fsctl_get_dfs_referral_req *dfs_req = NULL; 27148c2ecf20Sopenharmony_ci struct get_dfs_referral_rsp *dfs_rsp = NULL; 27158c2ecf20Sopenharmony_ci u32 dfs_req_size = 0, dfs_rsp_size = 0; 27168c2ecf20Sopenharmony_ci 27178c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: path: %s\n", __func__, search_name); 27188c2ecf20Sopenharmony_ci 27198c2ecf20Sopenharmony_ci /* 27208c2ecf20Sopenharmony_ci * Try to use the IPC tcon, otherwise just use any 27218c2ecf20Sopenharmony_ci */ 27228c2ecf20Sopenharmony_ci tcon = ses->tcon_ipc; 27238c2ecf20Sopenharmony_ci if (tcon == NULL) { 27248c2ecf20Sopenharmony_ci spin_lock(&cifs_tcp_ses_lock); 27258c2ecf20Sopenharmony_ci tcon = list_first_entry_or_null(&ses->tcon_list, 27268c2ecf20Sopenharmony_ci struct cifs_tcon, 27278c2ecf20Sopenharmony_ci tcon_list); 27288c2ecf20Sopenharmony_ci if (tcon) 27298c2ecf20Sopenharmony_ci tcon->tc_count++; 27308c2ecf20Sopenharmony_ci spin_unlock(&cifs_tcp_ses_lock); 27318c2ecf20Sopenharmony_ci } 27328c2ecf20Sopenharmony_ci 27338c2ecf20Sopenharmony_ci if (tcon == NULL) { 27348c2ecf20Sopenharmony_ci cifs_dbg(VFS, "session %p has no tcon available for a dfs referral request\n", 27358c2ecf20Sopenharmony_ci ses); 27368c2ecf20Sopenharmony_ci rc = -ENOTCONN; 27378c2ecf20Sopenharmony_ci goto out; 27388c2ecf20Sopenharmony_ci } 27398c2ecf20Sopenharmony_ci 27408c2ecf20Sopenharmony_ci utf16_path = cifs_strndup_to_utf16(search_name, PATH_MAX, 27418c2ecf20Sopenharmony_ci &utf16_path_len, 27428c2ecf20Sopenharmony_ci nls_codepage, remap); 27438c2ecf20Sopenharmony_ci if (!utf16_path) { 27448c2ecf20Sopenharmony_ci rc = -ENOMEM; 27458c2ecf20Sopenharmony_ci goto out; 27468c2ecf20Sopenharmony_ci } 27478c2ecf20Sopenharmony_ci 27488c2ecf20Sopenharmony_ci dfs_req_size = sizeof(*dfs_req) + utf16_path_len; 27498c2ecf20Sopenharmony_ci dfs_req = kzalloc(dfs_req_size, GFP_KERNEL); 27508c2ecf20Sopenharmony_ci if (!dfs_req) { 27518c2ecf20Sopenharmony_ci rc = -ENOMEM; 27528c2ecf20Sopenharmony_ci goto out; 27538c2ecf20Sopenharmony_ci } 27548c2ecf20Sopenharmony_ci 27558c2ecf20Sopenharmony_ci /* Highest DFS referral version understood */ 27568c2ecf20Sopenharmony_ci dfs_req->MaxReferralLevel = DFS_VERSION; 27578c2ecf20Sopenharmony_ci 27588c2ecf20Sopenharmony_ci /* Path to resolve in an UTF-16 null-terminated string */ 27598c2ecf20Sopenharmony_ci memcpy(dfs_req->RequestFileName, utf16_path, utf16_path_len); 27608c2ecf20Sopenharmony_ci 27618c2ecf20Sopenharmony_ci do { 27628c2ecf20Sopenharmony_ci rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, 27638c2ecf20Sopenharmony_ci FSCTL_DFS_GET_REFERRALS, 27648c2ecf20Sopenharmony_ci (char *)dfs_req, dfs_req_size, CIFSMaxBufSize, 27658c2ecf20Sopenharmony_ci (char **)&dfs_rsp, &dfs_rsp_size); 27668c2ecf20Sopenharmony_ci } while (rc == -EAGAIN); 27678c2ecf20Sopenharmony_ci 27688c2ecf20Sopenharmony_ci if (!rc && !dfs_rsp) 27698c2ecf20Sopenharmony_ci rc = -EIO; 27708c2ecf20Sopenharmony_ci if (rc) { 27718c2ecf20Sopenharmony_ci if ((rc != -ENOENT) && (rc != -EOPNOTSUPP)) 27728c2ecf20Sopenharmony_ci cifs_tcon_dbg(VFS, "ioctl error in %s rc=%d\n", __func__, rc); 27738c2ecf20Sopenharmony_ci goto out; 27748c2ecf20Sopenharmony_ci } 27758c2ecf20Sopenharmony_ci 27768c2ecf20Sopenharmony_ci rc = parse_dfs_referrals(dfs_rsp, dfs_rsp_size, 27778c2ecf20Sopenharmony_ci num_of_nodes, target_nodes, 27788c2ecf20Sopenharmony_ci nls_codepage, remap, search_name, 27798c2ecf20Sopenharmony_ci true /* is_unicode */); 27808c2ecf20Sopenharmony_ci if (rc) { 27818c2ecf20Sopenharmony_ci cifs_tcon_dbg(VFS, "parse error in %s rc=%d\n", __func__, rc); 27828c2ecf20Sopenharmony_ci goto out; 27838c2ecf20Sopenharmony_ci } 27848c2ecf20Sopenharmony_ci 27858c2ecf20Sopenharmony_ci out: 27868c2ecf20Sopenharmony_ci if (tcon && !tcon->ipc) { 27878c2ecf20Sopenharmony_ci /* ipc tcons are not refcounted */ 27888c2ecf20Sopenharmony_ci spin_lock(&cifs_tcp_ses_lock); 27898c2ecf20Sopenharmony_ci tcon->tc_count--; 27908c2ecf20Sopenharmony_ci spin_unlock(&cifs_tcp_ses_lock); 27918c2ecf20Sopenharmony_ci } 27928c2ecf20Sopenharmony_ci kfree(utf16_path); 27938c2ecf20Sopenharmony_ci kfree(dfs_req); 27948c2ecf20Sopenharmony_ci kfree(dfs_rsp); 27958c2ecf20Sopenharmony_ci return rc; 27968c2ecf20Sopenharmony_ci} 27978c2ecf20Sopenharmony_ci 27988c2ecf20Sopenharmony_cistatic int 27998c2ecf20Sopenharmony_ciparse_reparse_posix(struct reparse_posix_data *symlink_buf, 28008c2ecf20Sopenharmony_ci u32 plen, char **target_path, 28018c2ecf20Sopenharmony_ci struct cifs_sb_info *cifs_sb) 28028c2ecf20Sopenharmony_ci{ 28038c2ecf20Sopenharmony_ci unsigned int len; 28048c2ecf20Sopenharmony_ci 28058c2ecf20Sopenharmony_ci /* See MS-FSCC 2.1.2.6 for the 'NFS' style reparse tags */ 28068c2ecf20Sopenharmony_ci len = le16_to_cpu(symlink_buf->ReparseDataLength); 28078c2ecf20Sopenharmony_ci 28088c2ecf20Sopenharmony_ci if (le64_to_cpu(symlink_buf->InodeType) != NFS_SPECFILE_LNK) { 28098c2ecf20Sopenharmony_ci cifs_dbg(VFS, "%lld not a supported symlink type\n", 28108c2ecf20Sopenharmony_ci le64_to_cpu(symlink_buf->InodeType)); 28118c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 28128c2ecf20Sopenharmony_ci } 28138c2ecf20Sopenharmony_ci 28148c2ecf20Sopenharmony_ci *target_path = cifs_strndup_from_utf16( 28158c2ecf20Sopenharmony_ci symlink_buf->PathBuffer, 28168c2ecf20Sopenharmony_ci len, true, cifs_sb->local_nls); 28178c2ecf20Sopenharmony_ci if (!(*target_path)) 28188c2ecf20Sopenharmony_ci return -ENOMEM; 28198c2ecf20Sopenharmony_ci 28208c2ecf20Sopenharmony_ci convert_delimiter(*target_path, '/'); 28218c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path); 28228c2ecf20Sopenharmony_ci 28238c2ecf20Sopenharmony_ci return 0; 28248c2ecf20Sopenharmony_ci} 28258c2ecf20Sopenharmony_ci 28268c2ecf20Sopenharmony_cistatic int 28278c2ecf20Sopenharmony_ciparse_reparse_symlink(struct reparse_symlink_data_buffer *symlink_buf, 28288c2ecf20Sopenharmony_ci u32 plen, char **target_path, 28298c2ecf20Sopenharmony_ci struct cifs_sb_info *cifs_sb) 28308c2ecf20Sopenharmony_ci{ 28318c2ecf20Sopenharmony_ci unsigned int sub_len; 28328c2ecf20Sopenharmony_ci unsigned int sub_offset; 28338c2ecf20Sopenharmony_ci 28348c2ecf20Sopenharmony_ci /* We handle Symbolic Link reparse tag here. See: MS-FSCC 2.1.2.4 */ 28358c2ecf20Sopenharmony_ci 28368c2ecf20Sopenharmony_ci sub_offset = le16_to_cpu(symlink_buf->SubstituteNameOffset); 28378c2ecf20Sopenharmony_ci sub_len = le16_to_cpu(symlink_buf->SubstituteNameLength); 28388c2ecf20Sopenharmony_ci if (sub_offset + 20 > plen || 28398c2ecf20Sopenharmony_ci sub_offset + sub_len + 20 > plen) { 28408c2ecf20Sopenharmony_ci cifs_dbg(VFS, "srv returned malformed symlink buffer\n"); 28418c2ecf20Sopenharmony_ci return -EIO; 28428c2ecf20Sopenharmony_ci } 28438c2ecf20Sopenharmony_ci 28448c2ecf20Sopenharmony_ci *target_path = cifs_strndup_from_utf16( 28458c2ecf20Sopenharmony_ci symlink_buf->PathBuffer + sub_offset, 28468c2ecf20Sopenharmony_ci sub_len, true, cifs_sb->local_nls); 28478c2ecf20Sopenharmony_ci if (!(*target_path)) 28488c2ecf20Sopenharmony_ci return -ENOMEM; 28498c2ecf20Sopenharmony_ci 28508c2ecf20Sopenharmony_ci convert_delimiter(*target_path, '/'); 28518c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path); 28528c2ecf20Sopenharmony_ci 28538c2ecf20Sopenharmony_ci return 0; 28548c2ecf20Sopenharmony_ci} 28558c2ecf20Sopenharmony_ci 28568c2ecf20Sopenharmony_cistatic int 28578c2ecf20Sopenharmony_ciparse_reparse_point(struct reparse_data_buffer *buf, 28588c2ecf20Sopenharmony_ci u32 plen, char **target_path, 28598c2ecf20Sopenharmony_ci struct cifs_sb_info *cifs_sb) 28608c2ecf20Sopenharmony_ci{ 28618c2ecf20Sopenharmony_ci if (plen < sizeof(struct reparse_data_buffer)) { 28628c2ecf20Sopenharmony_ci cifs_dbg(VFS, "reparse buffer is too small. Must be at least 8 bytes but was %d\n", 28638c2ecf20Sopenharmony_ci plen); 28648c2ecf20Sopenharmony_ci return -EIO; 28658c2ecf20Sopenharmony_ci } 28668c2ecf20Sopenharmony_ci 28678c2ecf20Sopenharmony_ci if (plen < le16_to_cpu(buf->ReparseDataLength) + 28688c2ecf20Sopenharmony_ci sizeof(struct reparse_data_buffer)) { 28698c2ecf20Sopenharmony_ci cifs_dbg(VFS, "srv returned invalid reparse buf length: %d\n", 28708c2ecf20Sopenharmony_ci plen); 28718c2ecf20Sopenharmony_ci return -EIO; 28728c2ecf20Sopenharmony_ci } 28738c2ecf20Sopenharmony_ci 28748c2ecf20Sopenharmony_ci /* See MS-FSCC 2.1.2 */ 28758c2ecf20Sopenharmony_ci switch (le32_to_cpu(buf->ReparseTag)) { 28768c2ecf20Sopenharmony_ci case IO_REPARSE_TAG_NFS: 28778c2ecf20Sopenharmony_ci return parse_reparse_posix( 28788c2ecf20Sopenharmony_ci (struct reparse_posix_data *)buf, 28798c2ecf20Sopenharmony_ci plen, target_path, cifs_sb); 28808c2ecf20Sopenharmony_ci case IO_REPARSE_TAG_SYMLINK: 28818c2ecf20Sopenharmony_ci return parse_reparse_symlink( 28828c2ecf20Sopenharmony_ci (struct reparse_symlink_data_buffer *)buf, 28838c2ecf20Sopenharmony_ci plen, target_path, cifs_sb); 28848c2ecf20Sopenharmony_ci default: 28858c2ecf20Sopenharmony_ci cifs_dbg(VFS, "srv returned unknown symlink buffer tag:0x%08x\n", 28868c2ecf20Sopenharmony_ci le32_to_cpu(buf->ReparseTag)); 28878c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 28888c2ecf20Sopenharmony_ci } 28898c2ecf20Sopenharmony_ci} 28908c2ecf20Sopenharmony_ci 28918c2ecf20Sopenharmony_ci#define SMB2_SYMLINK_STRUCT_SIZE \ 28928c2ecf20Sopenharmony_ci (sizeof(struct smb2_err_rsp) - 1 + sizeof(struct smb2_symlink_err_rsp)) 28938c2ecf20Sopenharmony_ci 28948c2ecf20Sopenharmony_cistatic int 28958c2ecf20Sopenharmony_cismb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, 28968c2ecf20Sopenharmony_ci struct cifs_sb_info *cifs_sb, const char *full_path, 28978c2ecf20Sopenharmony_ci char **target_path, bool is_reparse_point) 28988c2ecf20Sopenharmony_ci{ 28998c2ecf20Sopenharmony_ci int rc; 29008c2ecf20Sopenharmony_ci __le16 *utf16_path = NULL; 29018c2ecf20Sopenharmony_ci __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; 29028c2ecf20Sopenharmony_ci struct cifs_open_parms oparms; 29038c2ecf20Sopenharmony_ci struct cifs_fid fid; 29048c2ecf20Sopenharmony_ci struct kvec err_iov = {NULL, 0}; 29058c2ecf20Sopenharmony_ci struct smb2_err_rsp *err_buf = NULL; 29068c2ecf20Sopenharmony_ci struct smb2_symlink_err_rsp *symlink; 29078c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); 29088c2ecf20Sopenharmony_ci unsigned int sub_len; 29098c2ecf20Sopenharmony_ci unsigned int sub_offset; 29108c2ecf20Sopenharmony_ci unsigned int print_len; 29118c2ecf20Sopenharmony_ci unsigned int print_offset; 29128c2ecf20Sopenharmony_ci int flags = CIFS_CP_CREATE_CLOSE_OP; 29138c2ecf20Sopenharmony_ci struct smb_rqst rqst[3]; 29148c2ecf20Sopenharmony_ci int resp_buftype[3]; 29158c2ecf20Sopenharmony_ci struct kvec rsp_iov[3]; 29168c2ecf20Sopenharmony_ci struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; 29178c2ecf20Sopenharmony_ci struct kvec io_iov[SMB2_IOCTL_IOV_SIZE]; 29188c2ecf20Sopenharmony_ci struct kvec close_iov[1]; 29198c2ecf20Sopenharmony_ci struct smb2_create_rsp *create_rsp; 29208c2ecf20Sopenharmony_ci struct smb2_ioctl_rsp *ioctl_rsp; 29218c2ecf20Sopenharmony_ci struct reparse_data_buffer *reparse_buf; 29228c2ecf20Sopenharmony_ci int create_options = is_reparse_point ? OPEN_REPARSE_POINT : 0; 29238c2ecf20Sopenharmony_ci u32 plen; 29248c2ecf20Sopenharmony_ci 29258c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path); 29268c2ecf20Sopenharmony_ci 29278c2ecf20Sopenharmony_ci *target_path = NULL; 29288c2ecf20Sopenharmony_ci 29298c2ecf20Sopenharmony_ci if (smb3_encryption_required(tcon)) 29308c2ecf20Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 29318c2ecf20Sopenharmony_ci 29328c2ecf20Sopenharmony_ci memset(rqst, 0, sizeof(rqst)); 29338c2ecf20Sopenharmony_ci resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; 29348c2ecf20Sopenharmony_ci memset(rsp_iov, 0, sizeof(rsp_iov)); 29358c2ecf20Sopenharmony_ci 29368c2ecf20Sopenharmony_ci utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); 29378c2ecf20Sopenharmony_ci if (!utf16_path) 29388c2ecf20Sopenharmony_ci return -ENOMEM; 29398c2ecf20Sopenharmony_ci 29408c2ecf20Sopenharmony_ci /* Open */ 29418c2ecf20Sopenharmony_ci memset(&open_iov, 0, sizeof(open_iov)); 29428c2ecf20Sopenharmony_ci rqst[0].rq_iov = open_iov; 29438c2ecf20Sopenharmony_ci rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; 29448c2ecf20Sopenharmony_ci 29458c2ecf20Sopenharmony_ci memset(&oparms, 0, sizeof(oparms)); 29468c2ecf20Sopenharmony_ci oparms.tcon = tcon; 29478c2ecf20Sopenharmony_ci oparms.desired_access = FILE_READ_ATTRIBUTES; 29488c2ecf20Sopenharmony_ci oparms.disposition = FILE_OPEN; 29498c2ecf20Sopenharmony_ci oparms.create_options = cifs_create_options(cifs_sb, create_options); 29508c2ecf20Sopenharmony_ci oparms.fid = &fid; 29518c2ecf20Sopenharmony_ci oparms.reconnect = false; 29528c2ecf20Sopenharmony_ci 29538c2ecf20Sopenharmony_ci rc = SMB2_open_init(tcon, server, 29548c2ecf20Sopenharmony_ci &rqst[0], &oplock, &oparms, utf16_path); 29558c2ecf20Sopenharmony_ci if (rc) 29568c2ecf20Sopenharmony_ci goto querty_exit; 29578c2ecf20Sopenharmony_ci smb2_set_next_command(tcon, &rqst[0]); 29588c2ecf20Sopenharmony_ci 29598c2ecf20Sopenharmony_ci 29608c2ecf20Sopenharmony_ci /* IOCTL */ 29618c2ecf20Sopenharmony_ci memset(&io_iov, 0, sizeof(io_iov)); 29628c2ecf20Sopenharmony_ci rqst[1].rq_iov = io_iov; 29638c2ecf20Sopenharmony_ci rqst[1].rq_nvec = SMB2_IOCTL_IOV_SIZE; 29648c2ecf20Sopenharmony_ci 29658c2ecf20Sopenharmony_ci rc = SMB2_ioctl_init(tcon, server, 29668c2ecf20Sopenharmony_ci &rqst[1], fid.persistent_fid, 29678c2ecf20Sopenharmony_ci fid.volatile_fid, FSCTL_GET_REPARSE_POINT, NULL, 0, 29688c2ecf20Sopenharmony_ci CIFSMaxBufSize - 29698c2ecf20Sopenharmony_ci MAX_SMB2_CREATE_RESPONSE_SIZE - 29708c2ecf20Sopenharmony_ci MAX_SMB2_CLOSE_RESPONSE_SIZE); 29718c2ecf20Sopenharmony_ci if (rc) 29728c2ecf20Sopenharmony_ci goto querty_exit; 29738c2ecf20Sopenharmony_ci 29748c2ecf20Sopenharmony_ci smb2_set_next_command(tcon, &rqst[1]); 29758c2ecf20Sopenharmony_ci smb2_set_related(&rqst[1]); 29768c2ecf20Sopenharmony_ci 29778c2ecf20Sopenharmony_ci 29788c2ecf20Sopenharmony_ci /* Close */ 29798c2ecf20Sopenharmony_ci memset(&close_iov, 0, sizeof(close_iov)); 29808c2ecf20Sopenharmony_ci rqst[2].rq_iov = close_iov; 29818c2ecf20Sopenharmony_ci rqst[2].rq_nvec = 1; 29828c2ecf20Sopenharmony_ci 29838c2ecf20Sopenharmony_ci rc = SMB2_close_init(tcon, server, 29848c2ecf20Sopenharmony_ci &rqst[2], COMPOUND_FID, COMPOUND_FID, false); 29858c2ecf20Sopenharmony_ci if (rc) 29868c2ecf20Sopenharmony_ci goto querty_exit; 29878c2ecf20Sopenharmony_ci 29888c2ecf20Sopenharmony_ci smb2_set_related(&rqst[2]); 29898c2ecf20Sopenharmony_ci 29908c2ecf20Sopenharmony_ci rc = compound_send_recv(xid, tcon->ses, server, 29918c2ecf20Sopenharmony_ci flags, 3, rqst, 29928c2ecf20Sopenharmony_ci resp_buftype, rsp_iov); 29938c2ecf20Sopenharmony_ci 29948c2ecf20Sopenharmony_ci create_rsp = rsp_iov[0].iov_base; 29958c2ecf20Sopenharmony_ci if (create_rsp && create_rsp->sync_hdr.Status) 29968c2ecf20Sopenharmony_ci err_iov = rsp_iov[0]; 29978c2ecf20Sopenharmony_ci ioctl_rsp = rsp_iov[1].iov_base; 29988c2ecf20Sopenharmony_ci 29998c2ecf20Sopenharmony_ci /* 30008c2ecf20Sopenharmony_ci * Open was successful and we got an ioctl response. 30018c2ecf20Sopenharmony_ci */ 30028c2ecf20Sopenharmony_ci if ((rc == 0) && (is_reparse_point)) { 30038c2ecf20Sopenharmony_ci /* See MS-FSCC 2.3.23 */ 30048c2ecf20Sopenharmony_ci 30058c2ecf20Sopenharmony_ci reparse_buf = (struct reparse_data_buffer *) 30068c2ecf20Sopenharmony_ci ((char *)ioctl_rsp + 30078c2ecf20Sopenharmony_ci le32_to_cpu(ioctl_rsp->OutputOffset)); 30088c2ecf20Sopenharmony_ci plen = le32_to_cpu(ioctl_rsp->OutputCount); 30098c2ecf20Sopenharmony_ci 30108c2ecf20Sopenharmony_ci if (plen + le32_to_cpu(ioctl_rsp->OutputOffset) > 30118c2ecf20Sopenharmony_ci rsp_iov[1].iov_len) { 30128c2ecf20Sopenharmony_ci cifs_tcon_dbg(VFS, "srv returned invalid ioctl len: %d\n", 30138c2ecf20Sopenharmony_ci plen); 30148c2ecf20Sopenharmony_ci rc = -EIO; 30158c2ecf20Sopenharmony_ci goto querty_exit; 30168c2ecf20Sopenharmony_ci } 30178c2ecf20Sopenharmony_ci 30188c2ecf20Sopenharmony_ci rc = parse_reparse_point(reparse_buf, plen, target_path, 30198c2ecf20Sopenharmony_ci cifs_sb); 30208c2ecf20Sopenharmony_ci goto querty_exit; 30218c2ecf20Sopenharmony_ci } 30228c2ecf20Sopenharmony_ci 30238c2ecf20Sopenharmony_ci if (!rc || !err_iov.iov_base) { 30248c2ecf20Sopenharmony_ci rc = -ENOENT; 30258c2ecf20Sopenharmony_ci goto querty_exit; 30268c2ecf20Sopenharmony_ci } 30278c2ecf20Sopenharmony_ci 30288c2ecf20Sopenharmony_ci err_buf = err_iov.iov_base; 30298c2ecf20Sopenharmony_ci if (le32_to_cpu(err_buf->ByteCount) < sizeof(struct smb2_symlink_err_rsp) || 30308c2ecf20Sopenharmony_ci err_iov.iov_len < SMB2_SYMLINK_STRUCT_SIZE) { 30318c2ecf20Sopenharmony_ci rc = -EINVAL; 30328c2ecf20Sopenharmony_ci goto querty_exit; 30338c2ecf20Sopenharmony_ci } 30348c2ecf20Sopenharmony_ci 30358c2ecf20Sopenharmony_ci symlink = (struct smb2_symlink_err_rsp *)err_buf->ErrorData; 30368c2ecf20Sopenharmony_ci if (le32_to_cpu(symlink->SymLinkErrorTag) != SYMLINK_ERROR_TAG || 30378c2ecf20Sopenharmony_ci le32_to_cpu(symlink->ReparseTag) != IO_REPARSE_TAG_SYMLINK) { 30388c2ecf20Sopenharmony_ci rc = -EINVAL; 30398c2ecf20Sopenharmony_ci goto querty_exit; 30408c2ecf20Sopenharmony_ci } 30418c2ecf20Sopenharmony_ci 30428c2ecf20Sopenharmony_ci /* open must fail on symlink - reset rc */ 30438c2ecf20Sopenharmony_ci rc = 0; 30448c2ecf20Sopenharmony_ci sub_len = le16_to_cpu(symlink->SubstituteNameLength); 30458c2ecf20Sopenharmony_ci sub_offset = le16_to_cpu(symlink->SubstituteNameOffset); 30468c2ecf20Sopenharmony_ci print_len = le16_to_cpu(symlink->PrintNameLength); 30478c2ecf20Sopenharmony_ci print_offset = le16_to_cpu(symlink->PrintNameOffset); 30488c2ecf20Sopenharmony_ci 30498c2ecf20Sopenharmony_ci if (err_iov.iov_len < SMB2_SYMLINK_STRUCT_SIZE + sub_offset + sub_len) { 30508c2ecf20Sopenharmony_ci rc = -EINVAL; 30518c2ecf20Sopenharmony_ci goto querty_exit; 30528c2ecf20Sopenharmony_ci } 30538c2ecf20Sopenharmony_ci 30548c2ecf20Sopenharmony_ci if (err_iov.iov_len < 30558c2ecf20Sopenharmony_ci SMB2_SYMLINK_STRUCT_SIZE + print_offset + print_len) { 30568c2ecf20Sopenharmony_ci rc = -EINVAL; 30578c2ecf20Sopenharmony_ci goto querty_exit; 30588c2ecf20Sopenharmony_ci } 30598c2ecf20Sopenharmony_ci 30608c2ecf20Sopenharmony_ci *target_path = cifs_strndup_from_utf16( 30618c2ecf20Sopenharmony_ci (char *)symlink->PathBuffer + sub_offset, 30628c2ecf20Sopenharmony_ci sub_len, true, cifs_sb->local_nls); 30638c2ecf20Sopenharmony_ci if (!(*target_path)) { 30648c2ecf20Sopenharmony_ci rc = -ENOMEM; 30658c2ecf20Sopenharmony_ci goto querty_exit; 30668c2ecf20Sopenharmony_ci } 30678c2ecf20Sopenharmony_ci convert_delimiter(*target_path, '/'); 30688c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path); 30698c2ecf20Sopenharmony_ci 30708c2ecf20Sopenharmony_ci querty_exit: 30718c2ecf20Sopenharmony_ci cifs_dbg(FYI, "query symlink rc %d\n", rc); 30728c2ecf20Sopenharmony_ci kfree(utf16_path); 30738c2ecf20Sopenharmony_ci SMB2_open_free(&rqst[0]); 30748c2ecf20Sopenharmony_ci SMB2_ioctl_free(&rqst[1]); 30758c2ecf20Sopenharmony_ci SMB2_close_free(&rqst[2]); 30768c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); 30778c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); 30788c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); 30798c2ecf20Sopenharmony_ci return rc; 30808c2ecf20Sopenharmony_ci} 30818c2ecf20Sopenharmony_ci 30828c2ecf20Sopenharmony_ciint 30838c2ecf20Sopenharmony_cismb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon, 30848c2ecf20Sopenharmony_ci struct cifs_sb_info *cifs_sb, const char *full_path, 30858c2ecf20Sopenharmony_ci __u32 *tag) 30868c2ecf20Sopenharmony_ci{ 30878c2ecf20Sopenharmony_ci int rc; 30888c2ecf20Sopenharmony_ci __le16 *utf16_path = NULL; 30898c2ecf20Sopenharmony_ci __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; 30908c2ecf20Sopenharmony_ci struct cifs_open_parms oparms; 30918c2ecf20Sopenharmony_ci struct cifs_fid fid; 30928c2ecf20Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); 30938c2ecf20Sopenharmony_ci int flags = CIFS_CP_CREATE_CLOSE_OP; 30948c2ecf20Sopenharmony_ci struct smb_rqst rqst[3]; 30958c2ecf20Sopenharmony_ci int resp_buftype[3]; 30968c2ecf20Sopenharmony_ci struct kvec rsp_iov[3]; 30978c2ecf20Sopenharmony_ci struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; 30988c2ecf20Sopenharmony_ci struct kvec io_iov[SMB2_IOCTL_IOV_SIZE]; 30998c2ecf20Sopenharmony_ci struct kvec close_iov[1]; 31008c2ecf20Sopenharmony_ci struct smb2_ioctl_rsp *ioctl_rsp; 31018c2ecf20Sopenharmony_ci struct reparse_data_buffer *reparse_buf; 31028c2ecf20Sopenharmony_ci u32 off, count, len; 31038c2ecf20Sopenharmony_ci 31048c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path); 31058c2ecf20Sopenharmony_ci 31068c2ecf20Sopenharmony_ci if (smb3_encryption_required(tcon)) 31078c2ecf20Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 31088c2ecf20Sopenharmony_ci 31098c2ecf20Sopenharmony_ci memset(rqst, 0, sizeof(rqst)); 31108c2ecf20Sopenharmony_ci resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; 31118c2ecf20Sopenharmony_ci memset(rsp_iov, 0, sizeof(rsp_iov)); 31128c2ecf20Sopenharmony_ci 31138c2ecf20Sopenharmony_ci utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); 31148c2ecf20Sopenharmony_ci if (!utf16_path) 31158c2ecf20Sopenharmony_ci return -ENOMEM; 31168c2ecf20Sopenharmony_ci 31178c2ecf20Sopenharmony_ci /* 31188c2ecf20Sopenharmony_ci * setup smb2open - TODO add optimization to call cifs_get_readable_path 31198c2ecf20Sopenharmony_ci * to see if there is a handle already open that we can use 31208c2ecf20Sopenharmony_ci */ 31218c2ecf20Sopenharmony_ci memset(&open_iov, 0, sizeof(open_iov)); 31228c2ecf20Sopenharmony_ci rqst[0].rq_iov = open_iov; 31238c2ecf20Sopenharmony_ci rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; 31248c2ecf20Sopenharmony_ci 31258c2ecf20Sopenharmony_ci memset(&oparms, 0, sizeof(oparms)); 31268c2ecf20Sopenharmony_ci oparms.tcon = tcon; 31278c2ecf20Sopenharmony_ci oparms.desired_access = FILE_READ_ATTRIBUTES; 31288c2ecf20Sopenharmony_ci oparms.disposition = FILE_OPEN; 31298c2ecf20Sopenharmony_ci oparms.create_options = cifs_create_options(cifs_sb, OPEN_REPARSE_POINT); 31308c2ecf20Sopenharmony_ci oparms.fid = &fid; 31318c2ecf20Sopenharmony_ci oparms.reconnect = false; 31328c2ecf20Sopenharmony_ci 31338c2ecf20Sopenharmony_ci rc = SMB2_open_init(tcon, server, 31348c2ecf20Sopenharmony_ci &rqst[0], &oplock, &oparms, utf16_path); 31358c2ecf20Sopenharmony_ci if (rc) 31368c2ecf20Sopenharmony_ci goto query_rp_exit; 31378c2ecf20Sopenharmony_ci smb2_set_next_command(tcon, &rqst[0]); 31388c2ecf20Sopenharmony_ci 31398c2ecf20Sopenharmony_ci 31408c2ecf20Sopenharmony_ci /* IOCTL */ 31418c2ecf20Sopenharmony_ci memset(&io_iov, 0, sizeof(io_iov)); 31428c2ecf20Sopenharmony_ci rqst[1].rq_iov = io_iov; 31438c2ecf20Sopenharmony_ci rqst[1].rq_nvec = SMB2_IOCTL_IOV_SIZE; 31448c2ecf20Sopenharmony_ci 31458c2ecf20Sopenharmony_ci rc = SMB2_ioctl_init(tcon, server, 31468c2ecf20Sopenharmony_ci &rqst[1], COMPOUND_FID, 31478c2ecf20Sopenharmony_ci COMPOUND_FID, FSCTL_GET_REPARSE_POINT, NULL, 0, 31488c2ecf20Sopenharmony_ci CIFSMaxBufSize - 31498c2ecf20Sopenharmony_ci MAX_SMB2_CREATE_RESPONSE_SIZE - 31508c2ecf20Sopenharmony_ci MAX_SMB2_CLOSE_RESPONSE_SIZE); 31518c2ecf20Sopenharmony_ci if (rc) 31528c2ecf20Sopenharmony_ci goto query_rp_exit; 31538c2ecf20Sopenharmony_ci 31548c2ecf20Sopenharmony_ci smb2_set_next_command(tcon, &rqst[1]); 31558c2ecf20Sopenharmony_ci smb2_set_related(&rqst[1]); 31568c2ecf20Sopenharmony_ci 31578c2ecf20Sopenharmony_ci 31588c2ecf20Sopenharmony_ci /* Close */ 31598c2ecf20Sopenharmony_ci memset(&close_iov, 0, sizeof(close_iov)); 31608c2ecf20Sopenharmony_ci rqst[2].rq_iov = close_iov; 31618c2ecf20Sopenharmony_ci rqst[2].rq_nvec = 1; 31628c2ecf20Sopenharmony_ci 31638c2ecf20Sopenharmony_ci rc = SMB2_close_init(tcon, server, 31648c2ecf20Sopenharmony_ci &rqst[2], COMPOUND_FID, COMPOUND_FID, false); 31658c2ecf20Sopenharmony_ci if (rc) 31668c2ecf20Sopenharmony_ci goto query_rp_exit; 31678c2ecf20Sopenharmony_ci 31688c2ecf20Sopenharmony_ci smb2_set_related(&rqst[2]); 31698c2ecf20Sopenharmony_ci 31708c2ecf20Sopenharmony_ci rc = compound_send_recv(xid, tcon->ses, server, 31718c2ecf20Sopenharmony_ci flags, 3, rqst, 31728c2ecf20Sopenharmony_ci resp_buftype, rsp_iov); 31738c2ecf20Sopenharmony_ci 31748c2ecf20Sopenharmony_ci ioctl_rsp = rsp_iov[1].iov_base; 31758c2ecf20Sopenharmony_ci 31768c2ecf20Sopenharmony_ci /* 31778c2ecf20Sopenharmony_ci * Open was successful and we got an ioctl response. 31788c2ecf20Sopenharmony_ci */ 31798c2ecf20Sopenharmony_ci if (rc == 0) { 31808c2ecf20Sopenharmony_ci /* See MS-FSCC 2.3.23 */ 31818c2ecf20Sopenharmony_ci off = le32_to_cpu(ioctl_rsp->OutputOffset); 31828c2ecf20Sopenharmony_ci count = le32_to_cpu(ioctl_rsp->OutputCount); 31838c2ecf20Sopenharmony_ci if (check_add_overflow(off, count, &len) || 31848c2ecf20Sopenharmony_ci len > rsp_iov[1].iov_len) { 31858c2ecf20Sopenharmony_ci cifs_tcon_dbg(VFS, "%s: invalid ioctl: off=%d count=%d\n", 31868c2ecf20Sopenharmony_ci __func__, off, count); 31878c2ecf20Sopenharmony_ci rc = -EIO; 31888c2ecf20Sopenharmony_ci goto query_rp_exit; 31898c2ecf20Sopenharmony_ci } 31908c2ecf20Sopenharmony_ci 31918c2ecf20Sopenharmony_ci reparse_buf = (void *)((u8 *)ioctl_rsp + off); 31928c2ecf20Sopenharmony_ci len = sizeof(*reparse_buf); 31938c2ecf20Sopenharmony_ci if (count < len || 31948c2ecf20Sopenharmony_ci count < le16_to_cpu(reparse_buf->ReparseDataLength) + len) { 31958c2ecf20Sopenharmony_ci cifs_tcon_dbg(VFS, "%s: invalid ioctl: off=%d count=%d\n", 31968c2ecf20Sopenharmony_ci __func__, off, count); 31978c2ecf20Sopenharmony_ci rc = -EIO; 31988c2ecf20Sopenharmony_ci goto query_rp_exit; 31998c2ecf20Sopenharmony_ci } 32008c2ecf20Sopenharmony_ci *tag = le32_to_cpu(reparse_buf->ReparseTag); 32018c2ecf20Sopenharmony_ci } 32028c2ecf20Sopenharmony_ci 32038c2ecf20Sopenharmony_ci query_rp_exit: 32048c2ecf20Sopenharmony_ci kfree(utf16_path); 32058c2ecf20Sopenharmony_ci SMB2_open_free(&rqst[0]); 32068c2ecf20Sopenharmony_ci SMB2_ioctl_free(&rqst[1]); 32078c2ecf20Sopenharmony_ci SMB2_close_free(&rqst[2]); 32088c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); 32098c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); 32108c2ecf20Sopenharmony_ci free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); 32118c2ecf20Sopenharmony_ci return rc; 32128c2ecf20Sopenharmony_ci} 32138c2ecf20Sopenharmony_ci 32148c2ecf20Sopenharmony_cistatic struct cifs_ntsd * 32158c2ecf20Sopenharmony_ciget_smb2_acl_by_fid(struct cifs_sb_info *cifs_sb, 32168c2ecf20Sopenharmony_ci const struct cifs_fid *cifsfid, u32 *pacllen) 32178c2ecf20Sopenharmony_ci{ 32188c2ecf20Sopenharmony_ci struct cifs_ntsd *pntsd = NULL; 32198c2ecf20Sopenharmony_ci unsigned int xid; 32208c2ecf20Sopenharmony_ci int rc = -EOPNOTSUPP; 32218c2ecf20Sopenharmony_ci struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); 32228c2ecf20Sopenharmony_ci 32238c2ecf20Sopenharmony_ci if (IS_ERR(tlink)) 32248c2ecf20Sopenharmony_ci return ERR_CAST(tlink); 32258c2ecf20Sopenharmony_ci 32268c2ecf20Sopenharmony_ci xid = get_xid(); 32278c2ecf20Sopenharmony_ci cifs_dbg(FYI, "trying to get acl\n"); 32288c2ecf20Sopenharmony_ci 32298c2ecf20Sopenharmony_ci rc = SMB2_query_acl(xid, tlink_tcon(tlink), cifsfid->persistent_fid, 32308c2ecf20Sopenharmony_ci cifsfid->volatile_fid, (void **)&pntsd, pacllen); 32318c2ecf20Sopenharmony_ci free_xid(xid); 32328c2ecf20Sopenharmony_ci 32338c2ecf20Sopenharmony_ci cifs_put_tlink(tlink); 32348c2ecf20Sopenharmony_ci 32358c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: rc = %d ACL len %d\n", __func__, rc, *pacllen); 32368c2ecf20Sopenharmony_ci if (rc) 32378c2ecf20Sopenharmony_ci return ERR_PTR(rc); 32388c2ecf20Sopenharmony_ci return pntsd; 32398c2ecf20Sopenharmony_ci 32408c2ecf20Sopenharmony_ci} 32418c2ecf20Sopenharmony_ci 32428c2ecf20Sopenharmony_cistatic struct cifs_ntsd * 32438c2ecf20Sopenharmony_ciget_smb2_acl_by_path(struct cifs_sb_info *cifs_sb, 32448c2ecf20Sopenharmony_ci const char *path, u32 *pacllen) 32458c2ecf20Sopenharmony_ci{ 32468c2ecf20Sopenharmony_ci struct cifs_ntsd *pntsd = NULL; 32478c2ecf20Sopenharmony_ci u8 oplock = SMB2_OPLOCK_LEVEL_NONE; 32488c2ecf20Sopenharmony_ci unsigned int xid; 32498c2ecf20Sopenharmony_ci int rc; 32508c2ecf20Sopenharmony_ci struct cifs_tcon *tcon; 32518c2ecf20Sopenharmony_ci struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); 32528c2ecf20Sopenharmony_ci struct cifs_fid fid; 32538c2ecf20Sopenharmony_ci struct cifs_open_parms oparms; 32548c2ecf20Sopenharmony_ci __le16 *utf16_path; 32558c2ecf20Sopenharmony_ci 32568c2ecf20Sopenharmony_ci cifs_dbg(FYI, "get smb3 acl for path %s\n", path); 32578c2ecf20Sopenharmony_ci if (IS_ERR(tlink)) 32588c2ecf20Sopenharmony_ci return ERR_CAST(tlink); 32598c2ecf20Sopenharmony_ci 32608c2ecf20Sopenharmony_ci tcon = tlink_tcon(tlink); 32618c2ecf20Sopenharmony_ci xid = get_xid(); 32628c2ecf20Sopenharmony_ci 32638c2ecf20Sopenharmony_ci utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); 32648c2ecf20Sopenharmony_ci if (!utf16_path) { 32658c2ecf20Sopenharmony_ci rc = -ENOMEM; 32668c2ecf20Sopenharmony_ci free_xid(xid); 32678c2ecf20Sopenharmony_ci return ERR_PTR(rc); 32688c2ecf20Sopenharmony_ci } 32698c2ecf20Sopenharmony_ci 32708c2ecf20Sopenharmony_ci oparms.tcon = tcon; 32718c2ecf20Sopenharmony_ci oparms.desired_access = READ_CONTROL; 32728c2ecf20Sopenharmony_ci oparms.disposition = FILE_OPEN; 32738c2ecf20Sopenharmony_ci /* 32748c2ecf20Sopenharmony_ci * When querying an ACL, even if the file is a symlink we want to open 32758c2ecf20Sopenharmony_ci * the source not the target, and so the protocol requires that the 32768c2ecf20Sopenharmony_ci * client specify this flag when opening a reparse point 32778c2ecf20Sopenharmony_ci */ 32788c2ecf20Sopenharmony_ci oparms.create_options = cifs_create_options(cifs_sb, 0) | OPEN_REPARSE_POINT; 32798c2ecf20Sopenharmony_ci oparms.fid = &fid; 32808c2ecf20Sopenharmony_ci oparms.reconnect = false; 32818c2ecf20Sopenharmony_ci 32828c2ecf20Sopenharmony_ci rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL, 32838c2ecf20Sopenharmony_ci NULL); 32848c2ecf20Sopenharmony_ci kfree(utf16_path); 32858c2ecf20Sopenharmony_ci if (!rc) { 32868c2ecf20Sopenharmony_ci rc = SMB2_query_acl(xid, tlink_tcon(tlink), fid.persistent_fid, 32878c2ecf20Sopenharmony_ci fid.volatile_fid, (void **)&pntsd, pacllen); 32888c2ecf20Sopenharmony_ci SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); 32898c2ecf20Sopenharmony_ci } 32908c2ecf20Sopenharmony_ci 32918c2ecf20Sopenharmony_ci cifs_put_tlink(tlink); 32928c2ecf20Sopenharmony_ci free_xid(xid); 32938c2ecf20Sopenharmony_ci 32948c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: rc = %d ACL len %d\n", __func__, rc, *pacllen); 32958c2ecf20Sopenharmony_ci if (rc) 32968c2ecf20Sopenharmony_ci return ERR_PTR(rc); 32978c2ecf20Sopenharmony_ci return pntsd; 32988c2ecf20Sopenharmony_ci} 32998c2ecf20Sopenharmony_ci 33008c2ecf20Sopenharmony_cistatic int 33018c2ecf20Sopenharmony_ciset_smb2_acl(struct cifs_ntsd *pnntsd, __u32 acllen, 33028c2ecf20Sopenharmony_ci struct inode *inode, const char *path, int aclflag) 33038c2ecf20Sopenharmony_ci{ 33048c2ecf20Sopenharmony_ci u8 oplock = SMB2_OPLOCK_LEVEL_NONE; 33058c2ecf20Sopenharmony_ci unsigned int xid; 33068c2ecf20Sopenharmony_ci int rc, access_flags = 0; 33078c2ecf20Sopenharmony_ci struct cifs_tcon *tcon; 33088c2ecf20Sopenharmony_ci struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); 33098c2ecf20Sopenharmony_ci struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); 33108c2ecf20Sopenharmony_ci struct cifs_fid fid; 33118c2ecf20Sopenharmony_ci struct cifs_open_parms oparms; 33128c2ecf20Sopenharmony_ci __le16 *utf16_path; 33138c2ecf20Sopenharmony_ci 33148c2ecf20Sopenharmony_ci cifs_dbg(FYI, "set smb3 acl for path %s\n", path); 33158c2ecf20Sopenharmony_ci if (IS_ERR(tlink)) 33168c2ecf20Sopenharmony_ci return PTR_ERR(tlink); 33178c2ecf20Sopenharmony_ci 33188c2ecf20Sopenharmony_ci tcon = tlink_tcon(tlink); 33198c2ecf20Sopenharmony_ci xid = get_xid(); 33208c2ecf20Sopenharmony_ci 33218c2ecf20Sopenharmony_ci if (aclflag == CIFS_ACL_OWNER || aclflag == CIFS_ACL_GROUP) 33228c2ecf20Sopenharmony_ci access_flags = WRITE_OWNER; 33238c2ecf20Sopenharmony_ci else 33248c2ecf20Sopenharmony_ci access_flags = WRITE_DAC; 33258c2ecf20Sopenharmony_ci 33268c2ecf20Sopenharmony_ci utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); 33278c2ecf20Sopenharmony_ci if (!utf16_path) { 33288c2ecf20Sopenharmony_ci rc = -ENOMEM; 33298c2ecf20Sopenharmony_ci free_xid(xid); 33308c2ecf20Sopenharmony_ci return rc; 33318c2ecf20Sopenharmony_ci } 33328c2ecf20Sopenharmony_ci 33338c2ecf20Sopenharmony_ci oparms.tcon = tcon; 33348c2ecf20Sopenharmony_ci oparms.desired_access = access_flags; 33358c2ecf20Sopenharmony_ci oparms.create_options = cifs_create_options(cifs_sb, 0); 33368c2ecf20Sopenharmony_ci oparms.disposition = FILE_OPEN; 33378c2ecf20Sopenharmony_ci oparms.path = path; 33388c2ecf20Sopenharmony_ci oparms.fid = &fid; 33398c2ecf20Sopenharmony_ci oparms.reconnect = false; 33408c2ecf20Sopenharmony_ci 33418c2ecf20Sopenharmony_ci rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, 33428c2ecf20Sopenharmony_ci NULL, NULL); 33438c2ecf20Sopenharmony_ci kfree(utf16_path); 33448c2ecf20Sopenharmony_ci if (!rc) { 33458c2ecf20Sopenharmony_ci rc = SMB2_set_acl(xid, tlink_tcon(tlink), fid.persistent_fid, 33468c2ecf20Sopenharmony_ci fid.volatile_fid, pnntsd, acllen, aclflag); 33478c2ecf20Sopenharmony_ci SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); 33488c2ecf20Sopenharmony_ci } 33498c2ecf20Sopenharmony_ci 33508c2ecf20Sopenharmony_ci cifs_put_tlink(tlink); 33518c2ecf20Sopenharmony_ci free_xid(xid); 33528c2ecf20Sopenharmony_ci return rc; 33538c2ecf20Sopenharmony_ci} 33548c2ecf20Sopenharmony_ci 33558c2ecf20Sopenharmony_ci/* Retrieve an ACL from the server */ 33568c2ecf20Sopenharmony_cistatic struct cifs_ntsd * 33578c2ecf20Sopenharmony_ciget_smb2_acl(struct cifs_sb_info *cifs_sb, 33588c2ecf20Sopenharmony_ci struct inode *inode, const char *path, 33598c2ecf20Sopenharmony_ci u32 *pacllen) 33608c2ecf20Sopenharmony_ci{ 33618c2ecf20Sopenharmony_ci struct cifs_ntsd *pntsd = NULL; 33628c2ecf20Sopenharmony_ci struct cifsFileInfo *open_file = NULL; 33638c2ecf20Sopenharmony_ci 33648c2ecf20Sopenharmony_ci if (inode) 33658c2ecf20Sopenharmony_ci open_file = find_readable_file(CIFS_I(inode), true); 33668c2ecf20Sopenharmony_ci if (!open_file) 33678c2ecf20Sopenharmony_ci return get_smb2_acl_by_path(cifs_sb, path, pacllen); 33688c2ecf20Sopenharmony_ci 33698c2ecf20Sopenharmony_ci pntsd = get_smb2_acl_by_fid(cifs_sb, &open_file->fid, pacllen); 33708c2ecf20Sopenharmony_ci cifsFileInfo_put(open_file); 33718c2ecf20Sopenharmony_ci return pntsd; 33728c2ecf20Sopenharmony_ci} 33738c2ecf20Sopenharmony_ci 33748c2ecf20Sopenharmony_cistatic long smb3_zero_range(struct file *file, struct cifs_tcon *tcon, 33758c2ecf20Sopenharmony_ci loff_t offset, loff_t len, bool keep_size) 33768c2ecf20Sopenharmony_ci{ 33778c2ecf20Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 33788c2ecf20Sopenharmony_ci struct inode *inode; 33798c2ecf20Sopenharmony_ci struct cifsInodeInfo *cifsi; 33808c2ecf20Sopenharmony_ci struct cifsFileInfo *cfile = file->private_data; 33818c2ecf20Sopenharmony_ci struct file_zero_data_information fsctl_buf; 33828c2ecf20Sopenharmony_ci long rc; 33838c2ecf20Sopenharmony_ci unsigned int xid; 33848c2ecf20Sopenharmony_ci __le64 eof; 33858c2ecf20Sopenharmony_ci 33868c2ecf20Sopenharmony_ci xid = get_xid(); 33878c2ecf20Sopenharmony_ci 33888c2ecf20Sopenharmony_ci inode = d_inode(cfile->dentry); 33898c2ecf20Sopenharmony_ci cifsi = CIFS_I(inode); 33908c2ecf20Sopenharmony_ci 33918c2ecf20Sopenharmony_ci trace_smb3_zero_enter(xid, cfile->fid.persistent_fid, tcon->tid, 33928c2ecf20Sopenharmony_ci ses->Suid, offset, len); 33938c2ecf20Sopenharmony_ci 33948c2ecf20Sopenharmony_ci /* 33958c2ecf20Sopenharmony_ci * We zero the range through ioctl, so we need remove the page caches 33968c2ecf20Sopenharmony_ci * first, otherwise the data may be inconsistent with the server. 33978c2ecf20Sopenharmony_ci */ 33988c2ecf20Sopenharmony_ci truncate_pagecache_range(inode, offset, offset + len - 1); 33998c2ecf20Sopenharmony_ci 34008c2ecf20Sopenharmony_ci /* if file not oplocked can't be sure whether asking to extend size */ 34018c2ecf20Sopenharmony_ci if (!CIFS_CACHE_READ(cifsi)) 34028c2ecf20Sopenharmony_ci if (keep_size == false) { 34038c2ecf20Sopenharmony_ci rc = -EOPNOTSUPP; 34048c2ecf20Sopenharmony_ci trace_smb3_zero_err(xid, cfile->fid.persistent_fid, 34058c2ecf20Sopenharmony_ci tcon->tid, ses->Suid, offset, len, rc); 34068c2ecf20Sopenharmony_ci free_xid(xid); 34078c2ecf20Sopenharmony_ci return rc; 34088c2ecf20Sopenharmony_ci } 34098c2ecf20Sopenharmony_ci 34108c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Offset %lld len %lld\n", offset, len); 34118c2ecf20Sopenharmony_ci 34128c2ecf20Sopenharmony_ci fsctl_buf.FileOffset = cpu_to_le64(offset); 34138c2ecf20Sopenharmony_ci fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len); 34148c2ecf20Sopenharmony_ci 34158c2ecf20Sopenharmony_ci rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, 34168c2ecf20Sopenharmony_ci cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA, 34178c2ecf20Sopenharmony_ci (char *)&fsctl_buf, 34188c2ecf20Sopenharmony_ci sizeof(struct file_zero_data_information), 34198c2ecf20Sopenharmony_ci 0, NULL, NULL); 34208c2ecf20Sopenharmony_ci if (rc) 34218c2ecf20Sopenharmony_ci goto zero_range_exit; 34228c2ecf20Sopenharmony_ci 34238c2ecf20Sopenharmony_ci /* 34248c2ecf20Sopenharmony_ci * do we also need to change the size of the file? 34258c2ecf20Sopenharmony_ci */ 34268c2ecf20Sopenharmony_ci if (keep_size == false && i_size_read(inode) < offset + len) { 34278c2ecf20Sopenharmony_ci eof = cpu_to_le64(offset + len); 34288c2ecf20Sopenharmony_ci rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, 34298c2ecf20Sopenharmony_ci cfile->fid.volatile_fid, cfile->pid, &eof); 34308c2ecf20Sopenharmony_ci } 34318c2ecf20Sopenharmony_ci 34328c2ecf20Sopenharmony_ci zero_range_exit: 34338c2ecf20Sopenharmony_ci free_xid(xid); 34348c2ecf20Sopenharmony_ci if (rc) 34358c2ecf20Sopenharmony_ci trace_smb3_zero_err(xid, cfile->fid.persistent_fid, tcon->tid, 34368c2ecf20Sopenharmony_ci ses->Suid, offset, len, rc); 34378c2ecf20Sopenharmony_ci else 34388c2ecf20Sopenharmony_ci trace_smb3_zero_done(xid, cfile->fid.persistent_fid, tcon->tid, 34398c2ecf20Sopenharmony_ci ses->Suid, offset, len); 34408c2ecf20Sopenharmony_ci return rc; 34418c2ecf20Sopenharmony_ci} 34428c2ecf20Sopenharmony_ci 34438c2ecf20Sopenharmony_cistatic long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon, 34448c2ecf20Sopenharmony_ci loff_t offset, loff_t len) 34458c2ecf20Sopenharmony_ci{ 34468c2ecf20Sopenharmony_ci struct inode *inode = file_inode(file); 34478c2ecf20Sopenharmony_ci struct cifsFileInfo *cfile = file->private_data; 34488c2ecf20Sopenharmony_ci struct file_zero_data_information fsctl_buf; 34498c2ecf20Sopenharmony_ci long rc; 34508c2ecf20Sopenharmony_ci unsigned int xid; 34518c2ecf20Sopenharmony_ci __u8 set_sparse = 1; 34528c2ecf20Sopenharmony_ci 34538c2ecf20Sopenharmony_ci xid = get_xid(); 34548c2ecf20Sopenharmony_ci 34558c2ecf20Sopenharmony_ci inode_lock(inode); 34568c2ecf20Sopenharmony_ci /* Need to make file sparse, if not already, before freeing range. */ 34578c2ecf20Sopenharmony_ci /* Consider adding equivalent for compressed since it could also work */ 34588c2ecf20Sopenharmony_ci if (!smb2_set_sparse(xid, tcon, cfile, inode, set_sparse)) { 34598c2ecf20Sopenharmony_ci rc = -EOPNOTSUPP; 34608c2ecf20Sopenharmony_ci goto out; 34618c2ecf20Sopenharmony_ci } 34628c2ecf20Sopenharmony_ci 34638c2ecf20Sopenharmony_ci /* 34648c2ecf20Sopenharmony_ci * We implement the punch hole through ioctl, so we need remove the page 34658c2ecf20Sopenharmony_ci * caches first, otherwise the data may be inconsistent with the server. 34668c2ecf20Sopenharmony_ci */ 34678c2ecf20Sopenharmony_ci truncate_pagecache_range(inode, offset, offset + len - 1); 34688c2ecf20Sopenharmony_ci 34698c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Offset %lld len %lld\n", offset, len); 34708c2ecf20Sopenharmony_ci 34718c2ecf20Sopenharmony_ci fsctl_buf.FileOffset = cpu_to_le64(offset); 34728c2ecf20Sopenharmony_ci fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len); 34738c2ecf20Sopenharmony_ci 34748c2ecf20Sopenharmony_ci rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, 34758c2ecf20Sopenharmony_ci cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA, 34768c2ecf20Sopenharmony_ci (char *)&fsctl_buf, 34778c2ecf20Sopenharmony_ci sizeof(struct file_zero_data_information), 34788c2ecf20Sopenharmony_ci CIFSMaxBufSize, NULL, NULL); 34798c2ecf20Sopenharmony_ciout: 34808c2ecf20Sopenharmony_ci inode_unlock(inode); 34818c2ecf20Sopenharmony_ci free_xid(xid); 34828c2ecf20Sopenharmony_ci return rc; 34838c2ecf20Sopenharmony_ci} 34848c2ecf20Sopenharmony_ci 34858c2ecf20Sopenharmony_cistatic int smb3_simple_fallocate_write_range(unsigned int xid, 34868c2ecf20Sopenharmony_ci struct cifs_tcon *tcon, 34878c2ecf20Sopenharmony_ci struct cifsFileInfo *cfile, 34888c2ecf20Sopenharmony_ci loff_t off, loff_t len, 34898c2ecf20Sopenharmony_ci char *buf) 34908c2ecf20Sopenharmony_ci{ 34918c2ecf20Sopenharmony_ci struct cifs_io_parms io_parms = {0}; 34928c2ecf20Sopenharmony_ci int nbytes; 34938c2ecf20Sopenharmony_ci int rc = 0; 34948c2ecf20Sopenharmony_ci struct kvec iov[2]; 34958c2ecf20Sopenharmony_ci 34968c2ecf20Sopenharmony_ci io_parms.netfid = cfile->fid.netfid; 34978c2ecf20Sopenharmony_ci io_parms.pid = current->tgid; 34988c2ecf20Sopenharmony_ci io_parms.tcon = tcon; 34998c2ecf20Sopenharmony_ci io_parms.persistent_fid = cfile->fid.persistent_fid; 35008c2ecf20Sopenharmony_ci io_parms.volatile_fid = cfile->fid.volatile_fid; 35018c2ecf20Sopenharmony_ci 35028c2ecf20Sopenharmony_ci while (len) { 35038c2ecf20Sopenharmony_ci io_parms.offset = off; 35048c2ecf20Sopenharmony_ci io_parms.length = len; 35058c2ecf20Sopenharmony_ci if (io_parms.length > SMB2_MAX_BUFFER_SIZE) 35068c2ecf20Sopenharmony_ci io_parms.length = SMB2_MAX_BUFFER_SIZE; 35078c2ecf20Sopenharmony_ci /* iov[0] is reserved for smb header */ 35088c2ecf20Sopenharmony_ci iov[1].iov_base = buf; 35098c2ecf20Sopenharmony_ci iov[1].iov_len = io_parms.length; 35108c2ecf20Sopenharmony_ci rc = SMB2_write(xid, &io_parms, &nbytes, iov, 1); 35118c2ecf20Sopenharmony_ci if (rc) 35128c2ecf20Sopenharmony_ci break; 35138c2ecf20Sopenharmony_ci if (nbytes > len) 35148c2ecf20Sopenharmony_ci return -EINVAL; 35158c2ecf20Sopenharmony_ci buf += nbytes; 35168c2ecf20Sopenharmony_ci off += nbytes; 35178c2ecf20Sopenharmony_ci len -= nbytes; 35188c2ecf20Sopenharmony_ci } 35198c2ecf20Sopenharmony_ci return rc; 35208c2ecf20Sopenharmony_ci} 35218c2ecf20Sopenharmony_ci 35228c2ecf20Sopenharmony_cistatic int smb3_simple_fallocate_range(unsigned int xid, 35238c2ecf20Sopenharmony_ci struct cifs_tcon *tcon, 35248c2ecf20Sopenharmony_ci struct cifsFileInfo *cfile, 35258c2ecf20Sopenharmony_ci loff_t off, loff_t len) 35268c2ecf20Sopenharmony_ci{ 35278c2ecf20Sopenharmony_ci struct file_allocated_range_buffer in_data, *out_data = NULL, *tmp_data; 35288c2ecf20Sopenharmony_ci u32 out_data_len; 35298c2ecf20Sopenharmony_ci char *buf = NULL; 35308c2ecf20Sopenharmony_ci loff_t l; 35318c2ecf20Sopenharmony_ci int rc; 35328c2ecf20Sopenharmony_ci 35338c2ecf20Sopenharmony_ci in_data.file_offset = cpu_to_le64(off); 35348c2ecf20Sopenharmony_ci in_data.length = cpu_to_le64(len); 35358c2ecf20Sopenharmony_ci rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, 35368c2ecf20Sopenharmony_ci cfile->fid.volatile_fid, 35378c2ecf20Sopenharmony_ci FSCTL_QUERY_ALLOCATED_RANGES, 35388c2ecf20Sopenharmony_ci (char *)&in_data, sizeof(in_data), 35398c2ecf20Sopenharmony_ci 1024 * sizeof(struct file_allocated_range_buffer), 35408c2ecf20Sopenharmony_ci (char **)&out_data, &out_data_len); 35418c2ecf20Sopenharmony_ci if (rc) 35428c2ecf20Sopenharmony_ci goto out; 35438c2ecf20Sopenharmony_ci 35448c2ecf20Sopenharmony_ci buf = kzalloc(1024 * 1024, GFP_KERNEL); 35458c2ecf20Sopenharmony_ci if (buf == NULL) { 35468c2ecf20Sopenharmony_ci rc = -ENOMEM; 35478c2ecf20Sopenharmony_ci goto out; 35488c2ecf20Sopenharmony_ci } 35498c2ecf20Sopenharmony_ci 35508c2ecf20Sopenharmony_ci tmp_data = out_data; 35518c2ecf20Sopenharmony_ci while (len) { 35528c2ecf20Sopenharmony_ci /* 35538c2ecf20Sopenharmony_ci * The rest of the region is unmapped so write it all. 35548c2ecf20Sopenharmony_ci */ 35558c2ecf20Sopenharmony_ci if (out_data_len == 0) { 35568c2ecf20Sopenharmony_ci rc = smb3_simple_fallocate_write_range(xid, tcon, 35578c2ecf20Sopenharmony_ci cfile, off, len, buf); 35588c2ecf20Sopenharmony_ci goto out; 35598c2ecf20Sopenharmony_ci } 35608c2ecf20Sopenharmony_ci 35618c2ecf20Sopenharmony_ci if (out_data_len < sizeof(struct file_allocated_range_buffer)) { 35628c2ecf20Sopenharmony_ci rc = -EINVAL; 35638c2ecf20Sopenharmony_ci goto out; 35648c2ecf20Sopenharmony_ci } 35658c2ecf20Sopenharmony_ci 35668c2ecf20Sopenharmony_ci if (off < le64_to_cpu(tmp_data->file_offset)) { 35678c2ecf20Sopenharmony_ci /* 35688c2ecf20Sopenharmony_ci * We are at a hole. Write until the end of the region 35698c2ecf20Sopenharmony_ci * or until the next allocated data, 35708c2ecf20Sopenharmony_ci * whichever comes next. 35718c2ecf20Sopenharmony_ci */ 35728c2ecf20Sopenharmony_ci l = le64_to_cpu(tmp_data->file_offset) - off; 35738c2ecf20Sopenharmony_ci if (len < l) 35748c2ecf20Sopenharmony_ci l = len; 35758c2ecf20Sopenharmony_ci rc = smb3_simple_fallocate_write_range(xid, tcon, 35768c2ecf20Sopenharmony_ci cfile, off, l, buf); 35778c2ecf20Sopenharmony_ci if (rc) 35788c2ecf20Sopenharmony_ci goto out; 35798c2ecf20Sopenharmony_ci off = off + l; 35808c2ecf20Sopenharmony_ci len = len - l; 35818c2ecf20Sopenharmony_ci if (len == 0) 35828c2ecf20Sopenharmony_ci goto out; 35838c2ecf20Sopenharmony_ci } 35848c2ecf20Sopenharmony_ci /* 35858c2ecf20Sopenharmony_ci * We are at a section of allocated data, just skip forward 35868c2ecf20Sopenharmony_ci * until the end of the data or the end of the region 35878c2ecf20Sopenharmony_ci * we are supposed to fallocate, whichever comes first. 35888c2ecf20Sopenharmony_ci */ 35898c2ecf20Sopenharmony_ci l = le64_to_cpu(tmp_data->length); 35908c2ecf20Sopenharmony_ci if (len < l) 35918c2ecf20Sopenharmony_ci l = len; 35928c2ecf20Sopenharmony_ci off += l; 35938c2ecf20Sopenharmony_ci len -= l; 35948c2ecf20Sopenharmony_ci 35958c2ecf20Sopenharmony_ci tmp_data = &tmp_data[1]; 35968c2ecf20Sopenharmony_ci out_data_len -= sizeof(struct file_allocated_range_buffer); 35978c2ecf20Sopenharmony_ci } 35988c2ecf20Sopenharmony_ci 35998c2ecf20Sopenharmony_ci out: 36008c2ecf20Sopenharmony_ci kfree(out_data); 36018c2ecf20Sopenharmony_ci kfree(buf); 36028c2ecf20Sopenharmony_ci return rc; 36038c2ecf20Sopenharmony_ci} 36048c2ecf20Sopenharmony_ci 36058c2ecf20Sopenharmony_ci 36068c2ecf20Sopenharmony_cistatic long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon, 36078c2ecf20Sopenharmony_ci loff_t off, loff_t len, bool keep_size) 36088c2ecf20Sopenharmony_ci{ 36098c2ecf20Sopenharmony_ci struct inode *inode; 36108c2ecf20Sopenharmony_ci struct cifsInodeInfo *cifsi; 36118c2ecf20Sopenharmony_ci struct cifsFileInfo *cfile = file->private_data; 36128c2ecf20Sopenharmony_ci long rc = -EOPNOTSUPP; 36138c2ecf20Sopenharmony_ci unsigned int xid; 36148c2ecf20Sopenharmony_ci __le64 eof; 36158c2ecf20Sopenharmony_ci 36168c2ecf20Sopenharmony_ci xid = get_xid(); 36178c2ecf20Sopenharmony_ci 36188c2ecf20Sopenharmony_ci inode = d_inode(cfile->dentry); 36198c2ecf20Sopenharmony_ci cifsi = CIFS_I(inode); 36208c2ecf20Sopenharmony_ci 36218c2ecf20Sopenharmony_ci trace_smb3_falloc_enter(xid, cfile->fid.persistent_fid, tcon->tid, 36228c2ecf20Sopenharmony_ci tcon->ses->Suid, off, len); 36238c2ecf20Sopenharmony_ci /* if file not oplocked can't be sure whether asking to extend size */ 36248c2ecf20Sopenharmony_ci if (!CIFS_CACHE_READ(cifsi)) 36258c2ecf20Sopenharmony_ci if (keep_size == false) { 36268c2ecf20Sopenharmony_ci trace_smb3_falloc_err(xid, cfile->fid.persistent_fid, 36278c2ecf20Sopenharmony_ci tcon->tid, tcon->ses->Suid, off, len, rc); 36288c2ecf20Sopenharmony_ci free_xid(xid); 36298c2ecf20Sopenharmony_ci return rc; 36308c2ecf20Sopenharmony_ci } 36318c2ecf20Sopenharmony_ci 36328c2ecf20Sopenharmony_ci /* 36338c2ecf20Sopenharmony_ci * Extending the file 36348c2ecf20Sopenharmony_ci */ 36358c2ecf20Sopenharmony_ci if ((keep_size == false) && i_size_read(inode) < off + len) { 36368c2ecf20Sopenharmony_ci rc = inode_newsize_ok(inode, off + len); 36378c2ecf20Sopenharmony_ci if (rc) 36388c2ecf20Sopenharmony_ci goto out; 36398c2ecf20Sopenharmony_ci 36408c2ecf20Sopenharmony_ci if (cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) 36418c2ecf20Sopenharmony_ci smb2_set_sparse(xid, tcon, cfile, inode, false); 36428c2ecf20Sopenharmony_ci 36438c2ecf20Sopenharmony_ci eof = cpu_to_le64(off + len); 36448c2ecf20Sopenharmony_ci rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, 36458c2ecf20Sopenharmony_ci cfile->fid.volatile_fid, cfile->pid, &eof); 36468c2ecf20Sopenharmony_ci if (rc == 0) { 36478c2ecf20Sopenharmony_ci cifsi->server_eof = off + len; 36488c2ecf20Sopenharmony_ci cifs_setsize(inode, off + len); 36498c2ecf20Sopenharmony_ci cifs_truncate_page(inode->i_mapping, inode->i_size); 36508c2ecf20Sopenharmony_ci truncate_setsize(inode, off + len); 36518c2ecf20Sopenharmony_ci } 36528c2ecf20Sopenharmony_ci goto out; 36538c2ecf20Sopenharmony_ci } 36548c2ecf20Sopenharmony_ci 36558c2ecf20Sopenharmony_ci /* 36568c2ecf20Sopenharmony_ci * Files are non-sparse by default so falloc may be a no-op 36578c2ecf20Sopenharmony_ci * Must check if file sparse. If not sparse, and since we are not 36588c2ecf20Sopenharmony_ci * extending then no need to do anything since file already allocated 36598c2ecf20Sopenharmony_ci */ 36608c2ecf20Sopenharmony_ci if ((cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) == 0) { 36618c2ecf20Sopenharmony_ci rc = 0; 36628c2ecf20Sopenharmony_ci goto out; 36638c2ecf20Sopenharmony_ci } 36648c2ecf20Sopenharmony_ci 36658c2ecf20Sopenharmony_ci if (keep_size == true) { 36668c2ecf20Sopenharmony_ci /* 36678c2ecf20Sopenharmony_ci * We can not preallocate pages beyond the end of the file 36688c2ecf20Sopenharmony_ci * in SMB2 36698c2ecf20Sopenharmony_ci */ 36708c2ecf20Sopenharmony_ci if (off >= i_size_read(inode)) { 36718c2ecf20Sopenharmony_ci rc = 0; 36728c2ecf20Sopenharmony_ci goto out; 36738c2ecf20Sopenharmony_ci } 36748c2ecf20Sopenharmony_ci /* 36758c2ecf20Sopenharmony_ci * For fallocates that are partially beyond the end of file, 36768c2ecf20Sopenharmony_ci * clamp len so we only fallocate up to the end of file. 36778c2ecf20Sopenharmony_ci */ 36788c2ecf20Sopenharmony_ci if (off + len > i_size_read(inode)) { 36798c2ecf20Sopenharmony_ci len = i_size_read(inode) - off; 36808c2ecf20Sopenharmony_ci } 36818c2ecf20Sopenharmony_ci } 36828c2ecf20Sopenharmony_ci 36838c2ecf20Sopenharmony_ci if ((keep_size == true) || (i_size_read(inode) >= off + len)) { 36848c2ecf20Sopenharmony_ci /* 36858c2ecf20Sopenharmony_ci * At this point, we are trying to fallocate an internal 36868c2ecf20Sopenharmony_ci * regions of a sparse file. Since smb2 does not have a 36878c2ecf20Sopenharmony_ci * fallocate command we have two otions on how to emulate this. 36888c2ecf20Sopenharmony_ci * We can either turn the entire file to become non-sparse 36898c2ecf20Sopenharmony_ci * which we only do if the fallocate is for virtually 36908c2ecf20Sopenharmony_ci * the whole file, or we can overwrite the region with zeroes 36918c2ecf20Sopenharmony_ci * using SMB2_write, which could be prohibitevly expensive 36928c2ecf20Sopenharmony_ci * if len is large. 36938c2ecf20Sopenharmony_ci */ 36948c2ecf20Sopenharmony_ci /* 36958c2ecf20Sopenharmony_ci * We are only trying to fallocate a small region so 36968c2ecf20Sopenharmony_ci * just write it with zero. 36978c2ecf20Sopenharmony_ci */ 36988c2ecf20Sopenharmony_ci if (len <= 1024 * 1024) { 36998c2ecf20Sopenharmony_ci rc = smb3_simple_fallocate_range(xid, tcon, cfile, 37008c2ecf20Sopenharmony_ci off, len); 37018c2ecf20Sopenharmony_ci goto out; 37028c2ecf20Sopenharmony_ci } 37038c2ecf20Sopenharmony_ci 37048c2ecf20Sopenharmony_ci /* 37058c2ecf20Sopenharmony_ci * Check if falloc starts within first few pages of file 37068c2ecf20Sopenharmony_ci * and ends within a few pages of the end of file to 37078c2ecf20Sopenharmony_ci * ensure that most of file is being forced to be 37088c2ecf20Sopenharmony_ci * fallocated now. If so then setting whole file sparse 37098c2ecf20Sopenharmony_ci * ie potentially making a few extra pages at the beginning 37108c2ecf20Sopenharmony_ci * or end of the file non-sparse via set_sparse is harmless. 37118c2ecf20Sopenharmony_ci */ 37128c2ecf20Sopenharmony_ci if ((off > 8192) || (off + len + 8192 < i_size_read(inode))) { 37138c2ecf20Sopenharmony_ci rc = -EOPNOTSUPP; 37148c2ecf20Sopenharmony_ci goto out; 37158c2ecf20Sopenharmony_ci } 37168c2ecf20Sopenharmony_ci } 37178c2ecf20Sopenharmony_ci 37188c2ecf20Sopenharmony_ci smb2_set_sparse(xid, tcon, cfile, inode, false); 37198c2ecf20Sopenharmony_ci rc = 0; 37208c2ecf20Sopenharmony_ci 37218c2ecf20Sopenharmony_ciout: 37228c2ecf20Sopenharmony_ci if (rc) 37238c2ecf20Sopenharmony_ci trace_smb3_falloc_err(xid, cfile->fid.persistent_fid, tcon->tid, 37248c2ecf20Sopenharmony_ci tcon->ses->Suid, off, len, rc); 37258c2ecf20Sopenharmony_ci else 37268c2ecf20Sopenharmony_ci trace_smb3_falloc_done(xid, cfile->fid.persistent_fid, tcon->tid, 37278c2ecf20Sopenharmony_ci tcon->ses->Suid, off, len); 37288c2ecf20Sopenharmony_ci 37298c2ecf20Sopenharmony_ci free_xid(xid); 37308c2ecf20Sopenharmony_ci return rc; 37318c2ecf20Sopenharmony_ci} 37328c2ecf20Sopenharmony_ci 37338c2ecf20Sopenharmony_cistatic loff_t smb3_llseek(struct file *file, struct cifs_tcon *tcon, loff_t offset, int whence) 37348c2ecf20Sopenharmony_ci{ 37358c2ecf20Sopenharmony_ci struct cifsFileInfo *wrcfile, *cfile = file->private_data; 37368c2ecf20Sopenharmony_ci struct cifsInodeInfo *cifsi; 37378c2ecf20Sopenharmony_ci struct inode *inode; 37388c2ecf20Sopenharmony_ci int rc = 0; 37398c2ecf20Sopenharmony_ci struct file_allocated_range_buffer in_data, *out_data = NULL; 37408c2ecf20Sopenharmony_ci u32 out_data_len; 37418c2ecf20Sopenharmony_ci unsigned int xid; 37428c2ecf20Sopenharmony_ci 37438c2ecf20Sopenharmony_ci if (whence != SEEK_HOLE && whence != SEEK_DATA) 37448c2ecf20Sopenharmony_ci return generic_file_llseek(file, offset, whence); 37458c2ecf20Sopenharmony_ci 37468c2ecf20Sopenharmony_ci inode = d_inode(cfile->dentry); 37478c2ecf20Sopenharmony_ci cifsi = CIFS_I(inode); 37488c2ecf20Sopenharmony_ci 37498c2ecf20Sopenharmony_ci if (offset < 0 || offset >= i_size_read(inode)) 37508c2ecf20Sopenharmony_ci return -ENXIO; 37518c2ecf20Sopenharmony_ci 37528c2ecf20Sopenharmony_ci xid = get_xid(); 37538c2ecf20Sopenharmony_ci /* 37548c2ecf20Sopenharmony_ci * We need to be sure that all dirty pages are written as they 37558c2ecf20Sopenharmony_ci * might fill holes on the server. 37568c2ecf20Sopenharmony_ci * Note that we also MUST flush any written pages since at least 37578c2ecf20Sopenharmony_ci * some servers (Windows2016) will not reflect recent writes in 37588c2ecf20Sopenharmony_ci * QUERY_ALLOCATED_RANGES until SMB2_flush is called. 37598c2ecf20Sopenharmony_ci */ 37608c2ecf20Sopenharmony_ci wrcfile = find_writable_file(cifsi, FIND_WR_ANY); 37618c2ecf20Sopenharmony_ci if (wrcfile) { 37628c2ecf20Sopenharmony_ci filemap_write_and_wait(inode->i_mapping); 37638c2ecf20Sopenharmony_ci smb2_flush_file(xid, tcon, &wrcfile->fid); 37648c2ecf20Sopenharmony_ci cifsFileInfo_put(wrcfile); 37658c2ecf20Sopenharmony_ci } 37668c2ecf20Sopenharmony_ci 37678c2ecf20Sopenharmony_ci if (!(cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE)) { 37688c2ecf20Sopenharmony_ci if (whence == SEEK_HOLE) 37698c2ecf20Sopenharmony_ci offset = i_size_read(inode); 37708c2ecf20Sopenharmony_ci goto lseek_exit; 37718c2ecf20Sopenharmony_ci } 37728c2ecf20Sopenharmony_ci 37738c2ecf20Sopenharmony_ci in_data.file_offset = cpu_to_le64(offset); 37748c2ecf20Sopenharmony_ci in_data.length = cpu_to_le64(i_size_read(inode)); 37758c2ecf20Sopenharmony_ci 37768c2ecf20Sopenharmony_ci rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, 37778c2ecf20Sopenharmony_ci cfile->fid.volatile_fid, 37788c2ecf20Sopenharmony_ci FSCTL_QUERY_ALLOCATED_RANGES, 37798c2ecf20Sopenharmony_ci (char *)&in_data, sizeof(in_data), 37808c2ecf20Sopenharmony_ci sizeof(struct file_allocated_range_buffer), 37818c2ecf20Sopenharmony_ci (char **)&out_data, &out_data_len); 37828c2ecf20Sopenharmony_ci if (rc == -E2BIG) 37838c2ecf20Sopenharmony_ci rc = 0; 37848c2ecf20Sopenharmony_ci if (rc) 37858c2ecf20Sopenharmony_ci goto lseek_exit; 37868c2ecf20Sopenharmony_ci 37878c2ecf20Sopenharmony_ci if (whence == SEEK_HOLE && out_data_len == 0) 37888c2ecf20Sopenharmony_ci goto lseek_exit; 37898c2ecf20Sopenharmony_ci 37908c2ecf20Sopenharmony_ci if (whence == SEEK_DATA && out_data_len == 0) { 37918c2ecf20Sopenharmony_ci rc = -ENXIO; 37928c2ecf20Sopenharmony_ci goto lseek_exit; 37938c2ecf20Sopenharmony_ci } 37948c2ecf20Sopenharmony_ci 37958c2ecf20Sopenharmony_ci if (out_data_len < sizeof(struct file_allocated_range_buffer)) { 37968c2ecf20Sopenharmony_ci rc = -EINVAL; 37978c2ecf20Sopenharmony_ci goto lseek_exit; 37988c2ecf20Sopenharmony_ci } 37998c2ecf20Sopenharmony_ci if (whence == SEEK_DATA) { 38008c2ecf20Sopenharmony_ci offset = le64_to_cpu(out_data->file_offset); 38018c2ecf20Sopenharmony_ci goto lseek_exit; 38028c2ecf20Sopenharmony_ci } 38038c2ecf20Sopenharmony_ci if (offset < le64_to_cpu(out_data->file_offset)) 38048c2ecf20Sopenharmony_ci goto lseek_exit; 38058c2ecf20Sopenharmony_ci 38068c2ecf20Sopenharmony_ci offset = le64_to_cpu(out_data->file_offset) + le64_to_cpu(out_data->length); 38078c2ecf20Sopenharmony_ci 38088c2ecf20Sopenharmony_ci lseek_exit: 38098c2ecf20Sopenharmony_ci free_xid(xid); 38108c2ecf20Sopenharmony_ci kfree(out_data); 38118c2ecf20Sopenharmony_ci if (!rc) 38128c2ecf20Sopenharmony_ci return vfs_setpos(file, offset, inode->i_sb->s_maxbytes); 38138c2ecf20Sopenharmony_ci else 38148c2ecf20Sopenharmony_ci return rc; 38158c2ecf20Sopenharmony_ci} 38168c2ecf20Sopenharmony_ci 38178c2ecf20Sopenharmony_cistatic int smb3_fiemap(struct cifs_tcon *tcon, 38188c2ecf20Sopenharmony_ci struct cifsFileInfo *cfile, 38198c2ecf20Sopenharmony_ci struct fiemap_extent_info *fei, u64 start, u64 len) 38208c2ecf20Sopenharmony_ci{ 38218c2ecf20Sopenharmony_ci unsigned int xid; 38228c2ecf20Sopenharmony_ci struct file_allocated_range_buffer in_data, *out_data; 38238c2ecf20Sopenharmony_ci u32 out_data_len; 38248c2ecf20Sopenharmony_ci int i, num, rc, flags, last_blob; 38258c2ecf20Sopenharmony_ci u64 next; 38268c2ecf20Sopenharmony_ci 38278c2ecf20Sopenharmony_ci rc = fiemap_prep(d_inode(cfile->dentry), fei, start, &len, 0); 38288c2ecf20Sopenharmony_ci if (rc) 38298c2ecf20Sopenharmony_ci return rc; 38308c2ecf20Sopenharmony_ci 38318c2ecf20Sopenharmony_ci xid = get_xid(); 38328c2ecf20Sopenharmony_ci again: 38338c2ecf20Sopenharmony_ci in_data.file_offset = cpu_to_le64(start); 38348c2ecf20Sopenharmony_ci in_data.length = cpu_to_le64(len); 38358c2ecf20Sopenharmony_ci 38368c2ecf20Sopenharmony_ci rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, 38378c2ecf20Sopenharmony_ci cfile->fid.volatile_fid, 38388c2ecf20Sopenharmony_ci FSCTL_QUERY_ALLOCATED_RANGES, 38398c2ecf20Sopenharmony_ci (char *)&in_data, sizeof(in_data), 38408c2ecf20Sopenharmony_ci 1024 * sizeof(struct file_allocated_range_buffer), 38418c2ecf20Sopenharmony_ci (char **)&out_data, &out_data_len); 38428c2ecf20Sopenharmony_ci if (rc == -E2BIG) { 38438c2ecf20Sopenharmony_ci last_blob = 0; 38448c2ecf20Sopenharmony_ci rc = 0; 38458c2ecf20Sopenharmony_ci } else 38468c2ecf20Sopenharmony_ci last_blob = 1; 38478c2ecf20Sopenharmony_ci if (rc) 38488c2ecf20Sopenharmony_ci goto out; 38498c2ecf20Sopenharmony_ci 38508c2ecf20Sopenharmony_ci if (out_data_len && out_data_len < sizeof(struct file_allocated_range_buffer)) { 38518c2ecf20Sopenharmony_ci rc = -EINVAL; 38528c2ecf20Sopenharmony_ci goto out; 38538c2ecf20Sopenharmony_ci } 38548c2ecf20Sopenharmony_ci if (out_data_len % sizeof(struct file_allocated_range_buffer)) { 38558c2ecf20Sopenharmony_ci rc = -EINVAL; 38568c2ecf20Sopenharmony_ci goto out; 38578c2ecf20Sopenharmony_ci } 38588c2ecf20Sopenharmony_ci 38598c2ecf20Sopenharmony_ci num = out_data_len / sizeof(struct file_allocated_range_buffer); 38608c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 38618c2ecf20Sopenharmony_ci flags = 0; 38628c2ecf20Sopenharmony_ci if (i == num - 1 && last_blob) 38638c2ecf20Sopenharmony_ci flags |= FIEMAP_EXTENT_LAST; 38648c2ecf20Sopenharmony_ci 38658c2ecf20Sopenharmony_ci rc = fiemap_fill_next_extent(fei, 38668c2ecf20Sopenharmony_ci le64_to_cpu(out_data[i].file_offset), 38678c2ecf20Sopenharmony_ci le64_to_cpu(out_data[i].file_offset), 38688c2ecf20Sopenharmony_ci le64_to_cpu(out_data[i].length), 38698c2ecf20Sopenharmony_ci flags); 38708c2ecf20Sopenharmony_ci if (rc < 0) 38718c2ecf20Sopenharmony_ci goto out; 38728c2ecf20Sopenharmony_ci if (rc == 1) { 38738c2ecf20Sopenharmony_ci rc = 0; 38748c2ecf20Sopenharmony_ci goto out; 38758c2ecf20Sopenharmony_ci } 38768c2ecf20Sopenharmony_ci } 38778c2ecf20Sopenharmony_ci 38788c2ecf20Sopenharmony_ci if (!last_blob) { 38798c2ecf20Sopenharmony_ci next = le64_to_cpu(out_data[num - 1].file_offset) + 38808c2ecf20Sopenharmony_ci le64_to_cpu(out_data[num - 1].length); 38818c2ecf20Sopenharmony_ci len = len - (next - start); 38828c2ecf20Sopenharmony_ci start = next; 38838c2ecf20Sopenharmony_ci goto again; 38848c2ecf20Sopenharmony_ci } 38858c2ecf20Sopenharmony_ci 38868c2ecf20Sopenharmony_ci out: 38878c2ecf20Sopenharmony_ci free_xid(xid); 38888c2ecf20Sopenharmony_ci kfree(out_data); 38898c2ecf20Sopenharmony_ci return rc; 38908c2ecf20Sopenharmony_ci} 38918c2ecf20Sopenharmony_ci 38928c2ecf20Sopenharmony_cistatic long smb3_fallocate(struct file *file, struct cifs_tcon *tcon, int mode, 38938c2ecf20Sopenharmony_ci loff_t off, loff_t len) 38948c2ecf20Sopenharmony_ci{ 38958c2ecf20Sopenharmony_ci /* KEEP_SIZE already checked for by do_fallocate */ 38968c2ecf20Sopenharmony_ci if (mode & FALLOC_FL_PUNCH_HOLE) 38978c2ecf20Sopenharmony_ci return smb3_punch_hole(file, tcon, off, len); 38988c2ecf20Sopenharmony_ci else if (mode & FALLOC_FL_ZERO_RANGE) { 38998c2ecf20Sopenharmony_ci if (mode & FALLOC_FL_KEEP_SIZE) 39008c2ecf20Sopenharmony_ci return smb3_zero_range(file, tcon, off, len, true); 39018c2ecf20Sopenharmony_ci return smb3_zero_range(file, tcon, off, len, false); 39028c2ecf20Sopenharmony_ci } else if (mode == FALLOC_FL_KEEP_SIZE) 39038c2ecf20Sopenharmony_ci return smb3_simple_falloc(file, tcon, off, len, true); 39048c2ecf20Sopenharmony_ci else if (mode == 0) 39058c2ecf20Sopenharmony_ci return smb3_simple_falloc(file, tcon, off, len, false); 39068c2ecf20Sopenharmony_ci 39078c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 39088c2ecf20Sopenharmony_ci} 39098c2ecf20Sopenharmony_ci 39108c2ecf20Sopenharmony_cistatic void 39118c2ecf20Sopenharmony_cismb2_downgrade_oplock(struct TCP_Server_Info *server, 39128c2ecf20Sopenharmony_ci struct cifsInodeInfo *cinode, __u32 oplock, 39138c2ecf20Sopenharmony_ci unsigned int epoch, bool *purge_cache) 39148c2ecf20Sopenharmony_ci{ 39158c2ecf20Sopenharmony_ci server->ops->set_oplock_level(cinode, oplock, 0, NULL); 39168c2ecf20Sopenharmony_ci} 39178c2ecf20Sopenharmony_ci 39188c2ecf20Sopenharmony_cistatic void 39198c2ecf20Sopenharmony_cismb21_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, 39208c2ecf20Sopenharmony_ci unsigned int epoch, bool *purge_cache); 39218c2ecf20Sopenharmony_ci 39228c2ecf20Sopenharmony_cistatic void 39238c2ecf20Sopenharmony_cismb3_downgrade_oplock(struct TCP_Server_Info *server, 39248c2ecf20Sopenharmony_ci struct cifsInodeInfo *cinode, __u32 oplock, 39258c2ecf20Sopenharmony_ci unsigned int epoch, bool *purge_cache) 39268c2ecf20Sopenharmony_ci{ 39278c2ecf20Sopenharmony_ci unsigned int old_state = cinode->oplock; 39288c2ecf20Sopenharmony_ci unsigned int old_epoch = cinode->epoch; 39298c2ecf20Sopenharmony_ci unsigned int new_state; 39308c2ecf20Sopenharmony_ci 39318c2ecf20Sopenharmony_ci if (epoch > old_epoch) { 39328c2ecf20Sopenharmony_ci smb21_set_oplock_level(cinode, oplock, 0, NULL); 39338c2ecf20Sopenharmony_ci cinode->epoch = epoch; 39348c2ecf20Sopenharmony_ci } 39358c2ecf20Sopenharmony_ci 39368c2ecf20Sopenharmony_ci new_state = cinode->oplock; 39378c2ecf20Sopenharmony_ci *purge_cache = false; 39388c2ecf20Sopenharmony_ci 39398c2ecf20Sopenharmony_ci if ((old_state & CIFS_CACHE_READ_FLG) != 0 && 39408c2ecf20Sopenharmony_ci (new_state & CIFS_CACHE_READ_FLG) == 0) 39418c2ecf20Sopenharmony_ci *purge_cache = true; 39428c2ecf20Sopenharmony_ci else if (old_state == new_state && (epoch - old_epoch > 1)) 39438c2ecf20Sopenharmony_ci *purge_cache = true; 39448c2ecf20Sopenharmony_ci} 39458c2ecf20Sopenharmony_ci 39468c2ecf20Sopenharmony_cistatic void 39478c2ecf20Sopenharmony_cismb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, 39488c2ecf20Sopenharmony_ci unsigned int epoch, bool *purge_cache) 39498c2ecf20Sopenharmony_ci{ 39508c2ecf20Sopenharmony_ci oplock &= 0xFF; 39518c2ecf20Sopenharmony_ci if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE) 39528c2ecf20Sopenharmony_ci return; 39538c2ecf20Sopenharmony_ci if (oplock == SMB2_OPLOCK_LEVEL_BATCH) { 39548c2ecf20Sopenharmony_ci cinode->oplock = CIFS_CACHE_RHW_FLG; 39558c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Batch Oplock granted on inode %p\n", 39568c2ecf20Sopenharmony_ci &cinode->vfs_inode); 39578c2ecf20Sopenharmony_ci } else if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) { 39588c2ecf20Sopenharmony_ci cinode->oplock = CIFS_CACHE_RW_FLG; 39598c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Exclusive Oplock granted on inode %p\n", 39608c2ecf20Sopenharmony_ci &cinode->vfs_inode); 39618c2ecf20Sopenharmony_ci } else if (oplock == SMB2_OPLOCK_LEVEL_II) { 39628c2ecf20Sopenharmony_ci cinode->oplock = CIFS_CACHE_READ_FLG; 39638c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Level II Oplock granted on inode %p\n", 39648c2ecf20Sopenharmony_ci &cinode->vfs_inode); 39658c2ecf20Sopenharmony_ci } else 39668c2ecf20Sopenharmony_ci cinode->oplock = 0; 39678c2ecf20Sopenharmony_ci} 39688c2ecf20Sopenharmony_ci 39698c2ecf20Sopenharmony_cistatic void 39708c2ecf20Sopenharmony_cismb21_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, 39718c2ecf20Sopenharmony_ci unsigned int epoch, bool *purge_cache) 39728c2ecf20Sopenharmony_ci{ 39738c2ecf20Sopenharmony_ci char message[5] = {0}; 39748c2ecf20Sopenharmony_ci unsigned int new_oplock = 0; 39758c2ecf20Sopenharmony_ci 39768c2ecf20Sopenharmony_ci oplock &= 0xFF; 39778c2ecf20Sopenharmony_ci if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE) 39788c2ecf20Sopenharmony_ci return; 39798c2ecf20Sopenharmony_ci 39808c2ecf20Sopenharmony_ci /* Check if the server granted an oplock rather than a lease */ 39818c2ecf20Sopenharmony_ci if (oplock & SMB2_OPLOCK_LEVEL_EXCLUSIVE) 39828c2ecf20Sopenharmony_ci return smb2_set_oplock_level(cinode, oplock, epoch, 39838c2ecf20Sopenharmony_ci purge_cache); 39848c2ecf20Sopenharmony_ci 39858c2ecf20Sopenharmony_ci if (oplock & SMB2_LEASE_READ_CACHING_HE) { 39868c2ecf20Sopenharmony_ci new_oplock |= CIFS_CACHE_READ_FLG; 39878c2ecf20Sopenharmony_ci strcat(message, "R"); 39888c2ecf20Sopenharmony_ci } 39898c2ecf20Sopenharmony_ci if (oplock & SMB2_LEASE_HANDLE_CACHING_HE) { 39908c2ecf20Sopenharmony_ci new_oplock |= CIFS_CACHE_HANDLE_FLG; 39918c2ecf20Sopenharmony_ci strcat(message, "H"); 39928c2ecf20Sopenharmony_ci } 39938c2ecf20Sopenharmony_ci if (oplock & SMB2_LEASE_WRITE_CACHING_HE) { 39948c2ecf20Sopenharmony_ci new_oplock |= CIFS_CACHE_WRITE_FLG; 39958c2ecf20Sopenharmony_ci strcat(message, "W"); 39968c2ecf20Sopenharmony_ci } 39978c2ecf20Sopenharmony_ci if (!new_oplock) 39988c2ecf20Sopenharmony_ci strncpy(message, "None", sizeof(message)); 39998c2ecf20Sopenharmony_ci 40008c2ecf20Sopenharmony_ci cinode->oplock = new_oplock; 40018c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s Lease granted on inode %p\n", message, 40028c2ecf20Sopenharmony_ci &cinode->vfs_inode); 40038c2ecf20Sopenharmony_ci} 40048c2ecf20Sopenharmony_ci 40058c2ecf20Sopenharmony_cistatic void 40068c2ecf20Sopenharmony_cismb3_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, 40078c2ecf20Sopenharmony_ci unsigned int epoch, bool *purge_cache) 40088c2ecf20Sopenharmony_ci{ 40098c2ecf20Sopenharmony_ci unsigned int old_oplock = cinode->oplock; 40108c2ecf20Sopenharmony_ci 40118c2ecf20Sopenharmony_ci smb21_set_oplock_level(cinode, oplock, epoch, purge_cache); 40128c2ecf20Sopenharmony_ci 40138c2ecf20Sopenharmony_ci if (purge_cache) { 40148c2ecf20Sopenharmony_ci *purge_cache = false; 40158c2ecf20Sopenharmony_ci if (old_oplock == CIFS_CACHE_READ_FLG) { 40168c2ecf20Sopenharmony_ci if (cinode->oplock == CIFS_CACHE_READ_FLG && 40178c2ecf20Sopenharmony_ci (epoch - cinode->epoch > 0)) 40188c2ecf20Sopenharmony_ci *purge_cache = true; 40198c2ecf20Sopenharmony_ci else if (cinode->oplock == CIFS_CACHE_RH_FLG && 40208c2ecf20Sopenharmony_ci (epoch - cinode->epoch > 1)) 40218c2ecf20Sopenharmony_ci *purge_cache = true; 40228c2ecf20Sopenharmony_ci else if (cinode->oplock == CIFS_CACHE_RHW_FLG && 40238c2ecf20Sopenharmony_ci (epoch - cinode->epoch > 1)) 40248c2ecf20Sopenharmony_ci *purge_cache = true; 40258c2ecf20Sopenharmony_ci else if (cinode->oplock == 0 && 40268c2ecf20Sopenharmony_ci (epoch - cinode->epoch > 0)) 40278c2ecf20Sopenharmony_ci *purge_cache = true; 40288c2ecf20Sopenharmony_ci } else if (old_oplock == CIFS_CACHE_RH_FLG) { 40298c2ecf20Sopenharmony_ci if (cinode->oplock == CIFS_CACHE_RH_FLG && 40308c2ecf20Sopenharmony_ci (epoch - cinode->epoch > 0)) 40318c2ecf20Sopenharmony_ci *purge_cache = true; 40328c2ecf20Sopenharmony_ci else if (cinode->oplock == CIFS_CACHE_RHW_FLG && 40338c2ecf20Sopenharmony_ci (epoch - cinode->epoch > 1)) 40348c2ecf20Sopenharmony_ci *purge_cache = true; 40358c2ecf20Sopenharmony_ci } 40368c2ecf20Sopenharmony_ci cinode->epoch = epoch; 40378c2ecf20Sopenharmony_ci } 40388c2ecf20Sopenharmony_ci} 40398c2ecf20Sopenharmony_ci 40408c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY 40418c2ecf20Sopenharmony_cistatic bool 40428c2ecf20Sopenharmony_cismb2_is_read_op(__u32 oplock) 40438c2ecf20Sopenharmony_ci{ 40448c2ecf20Sopenharmony_ci return oplock == SMB2_OPLOCK_LEVEL_II; 40458c2ecf20Sopenharmony_ci} 40468c2ecf20Sopenharmony_ci#endif /* CIFS_ALLOW_INSECURE_LEGACY */ 40478c2ecf20Sopenharmony_ci 40488c2ecf20Sopenharmony_cistatic bool 40498c2ecf20Sopenharmony_cismb21_is_read_op(__u32 oplock) 40508c2ecf20Sopenharmony_ci{ 40518c2ecf20Sopenharmony_ci return (oplock & SMB2_LEASE_READ_CACHING_HE) && 40528c2ecf20Sopenharmony_ci !(oplock & SMB2_LEASE_WRITE_CACHING_HE); 40538c2ecf20Sopenharmony_ci} 40548c2ecf20Sopenharmony_ci 40558c2ecf20Sopenharmony_cistatic __le32 40568c2ecf20Sopenharmony_cimap_oplock_to_lease(u8 oplock) 40578c2ecf20Sopenharmony_ci{ 40588c2ecf20Sopenharmony_ci if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) 40598c2ecf20Sopenharmony_ci return SMB2_LEASE_WRITE_CACHING | SMB2_LEASE_READ_CACHING; 40608c2ecf20Sopenharmony_ci else if (oplock == SMB2_OPLOCK_LEVEL_II) 40618c2ecf20Sopenharmony_ci return SMB2_LEASE_READ_CACHING; 40628c2ecf20Sopenharmony_ci else if (oplock == SMB2_OPLOCK_LEVEL_BATCH) 40638c2ecf20Sopenharmony_ci return SMB2_LEASE_HANDLE_CACHING | SMB2_LEASE_READ_CACHING | 40648c2ecf20Sopenharmony_ci SMB2_LEASE_WRITE_CACHING; 40658c2ecf20Sopenharmony_ci return 0; 40668c2ecf20Sopenharmony_ci} 40678c2ecf20Sopenharmony_ci 40688c2ecf20Sopenharmony_cistatic char * 40698c2ecf20Sopenharmony_cismb2_create_lease_buf(u8 *lease_key, u8 oplock) 40708c2ecf20Sopenharmony_ci{ 40718c2ecf20Sopenharmony_ci struct create_lease *buf; 40728c2ecf20Sopenharmony_ci 40738c2ecf20Sopenharmony_ci buf = kzalloc(sizeof(struct create_lease), GFP_KERNEL); 40748c2ecf20Sopenharmony_ci if (!buf) 40758c2ecf20Sopenharmony_ci return NULL; 40768c2ecf20Sopenharmony_ci 40778c2ecf20Sopenharmony_ci memcpy(&buf->lcontext.LeaseKey, lease_key, SMB2_LEASE_KEY_SIZE); 40788c2ecf20Sopenharmony_ci buf->lcontext.LeaseState = map_oplock_to_lease(oplock); 40798c2ecf20Sopenharmony_ci 40808c2ecf20Sopenharmony_ci buf->ccontext.DataOffset = cpu_to_le16(offsetof 40818c2ecf20Sopenharmony_ci (struct create_lease, lcontext)); 40828c2ecf20Sopenharmony_ci buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context)); 40838c2ecf20Sopenharmony_ci buf->ccontext.NameOffset = cpu_to_le16(offsetof 40848c2ecf20Sopenharmony_ci (struct create_lease, Name)); 40858c2ecf20Sopenharmony_ci buf->ccontext.NameLength = cpu_to_le16(4); 40868c2ecf20Sopenharmony_ci /* SMB2_CREATE_REQUEST_LEASE is "RqLs" */ 40878c2ecf20Sopenharmony_ci buf->Name[0] = 'R'; 40888c2ecf20Sopenharmony_ci buf->Name[1] = 'q'; 40898c2ecf20Sopenharmony_ci buf->Name[2] = 'L'; 40908c2ecf20Sopenharmony_ci buf->Name[3] = 's'; 40918c2ecf20Sopenharmony_ci return (char *)buf; 40928c2ecf20Sopenharmony_ci} 40938c2ecf20Sopenharmony_ci 40948c2ecf20Sopenharmony_cistatic char * 40958c2ecf20Sopenharmony_cismb3_create_lease_buf(u8 *lease_key, u8 oplock) 40968c2ecf20Sopenharmony_ci{ 40978c2ecf20Sopenharmony_ci struct create_lease_v2 *buf; 40988c2ecf20Sopenharmony_ci 40998c2ecf20Sopenharmony_ci buf = kzalloc(sizeof(struct create_lease_v2), GFP_KERNEL); 41008c2ecf20Sopenharmony_ci if (!buf) 41018c2ecf20Sopenharmony_ci return NULL; 41028c2ecf20Sopenharmony_ci 41038c2ecf20Sopenharmony_ci memcpy(&buf->lcontext.LeaseKey, lease_key, SMB2_LEASE_KEY_SIZE); 41048c2ecf20Sopenharmony_ci buf->lcontext.LeaseState = map_oplock_to_lease(oplock); 41058c2ecf20Sopenharmony_ci 41068c2ecf20Sopenharmony_ci buf->ccontext.DataOffset = cpu_to_le16(offsetof 41078c2ecf20Sopenharmony_ci (struct create_lease_v2, lcontext)); 41088c2ecf20Sopenharmony_ci buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context_v2)); 41098c2ecf20Sopenharmony_ci buf->ccontext.NameOffset = cpu_to_le16(offsetof 41108c2ecf20Sopenharmony_ci (struct create_lease_v2, Name)); 41118c2ecf20Sopenharmony_ci buf->ccontext.NameLength = cpu_to_le16(4); 41128c2ecf20Sopenharmony_ci /* SMB2_CREATE_REQUEST_LEASE is "RqLs" */ 41138c2ecf20Sopenharmony_ci buf->Name[0] = 'R'; 41148c2ecf20Sopenharmony_ci buf->Name[1] = 'q'; 41158c2ecf20Sopenharmony_ci buf->Name[2] = 'L'; 41168c2ecf20Sopenharmony_ci buf->Name[3] = 's'; 41178c2ecf20Sopenharmony_ci return (char *)buf; 41188c2ecf20Sopenharmony_ci} 41198c2ecf20Sopenharmony_ci 41208c2ecf20Sopenharmony_cistatic __u8 41218c2ecf20Sopenharmony_cismb2_parse_lease_buf(void *buf, unsigned int *epoch, char *lease_key) 41228c2ecf20Sopenharmony_ci{ 41238c2ecf20Sopenharmony_ci struct create_lease *lc = (struct create_lease *)buf; 41248c2ecf20Sopenharmony_ci 41258c2ecf20Sopenharmony_ci *epoch = 0; /* not used */ 41268c2ecf20Sopenharmony_ci if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS) 41278c2ecf20Sopenharmony_ci return SMB2_OPLOCK_LEVEL_NOCHANGE; 41288c2ecf20Sopenharmony_ci return le32_to_cpu(lc->lcontext.LeaseState); 41298c2ecf20Sopenharmony_ci} 41308c2ecf20Sopenharmony_ci 41318c2ecf20Sopenharmony_cistatic __u8 41328c2ecf20Sopenharmony_cismb3_parse_lease_buf(void *buf, unsigned int *epoch, char *lease_key) 41338c2ecf20Sopenharmony_ci{ 41348c2ecf20Sopenharmony_ci struct create_lease_v2 *lc = (struct create_lease_v2 *)buf; 41358c2ecf20Sopenharmony_ci 41368c2ecf20Sopenharmony_ci *epoch = le16_to_cpu(lc->lcontext.Epoch); 41378c2ecf20Sopenharmony_ci if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS) 41388c2ecf20Sopenharmony_ci return SMB2_OPLOCK_LEVEL_NOCHANGE; 41398c2ecf20Sopenharmony_ci if (lease_key) 41408c2ecf20Sopenharmony_ci memcpy(lease_key, &lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); 41418c2ecf20Sopenharmony_ci return le32_to_cpu(lc->lcontext.LeaseState); 41428c2ecf20Sopenharmony_ci} 41438c2ecf20Sopenharmony_ci 41448c2ecf20Sopenharmony_cistatic unsigned int 41458c2ecf20Sopenharmony_cismb2_wp_retry_size(struct inode *inode) 41468c2ecf20Sopenharmony_ci{ 41478c2ecf20Sopenharmony_ci return min_t(unsigned int, CIFS_SB(inode->i_sb)->wsize, 41488c2ecf20Sopenharmony_ci SMB2_MAX_BUFFER_SIZE); 41498c2ecf20Sopenharmony_ci} 41508c2ecf20Sopenharmony_ci 41518c2ecf20Sopenharmony_cistatic bool 41528c2ecf20Sopenharmony_cismb2_dir_needs_close(struct cifsFileInfo *cfile) 41538c2ecf20Sopenharmony_ci{ 41548c2ecf20Sopenharmony_ci return !cfile->invalidHandle; 41558c2ecf20Sopenharmony_ci} 41568c2ecf20Sopenharmony_ci 41578c2ecf20Sopenharmony_cistatic void 41588c2ecf20Sopenharmony_cifill_transform_hdr(struct smb2_transform_hdr *tr_hdr, unsigned int orig_len, 41598c2ecf20Sopenharmony_ci struct smb_rqst *old_rq, __le16 cipher_type) 41608c2ecf20Sopenharmony_ci{ 41618c2ecf20Sopenharmony_ci struct smb2_sync_hdr *shdr = 41628c2ecf20Sopenharmony_ci (struct smb2_sync_hdr *)old_rq->rq_iov[0].iov_base; 41638c2ecf20Sopenharmony_ci 41648c2ecf20Sopenharmony_ci memset(tr_hdr, 0, sizeof(struct smb2_transform_hdr)); 41658c2ecf20Sopenharmony_ci tr_hdr->ProtocolId = SMB2_TRANSFORM_PROTO_NUM; 41668c2ecf20Sopenharmony_ci tr_hdr->OriginalMessageSize = cpu_to_le32(orig_len); 41678c2ecf20Sopenharmony_ci tr_hdr->Flags = cpu_to_le16(0x01); 41688c2ecf20Sopenharmony_ci if ((cipher_type == SMB2_ENCRYPTION_AES128_GCM) || 41698c2ecf20Sopenharmony_ci (cipher_type == SMB2_ENCRYPTION_AES256_GCM)) 41708c2ecf20Sopenharmony_ci get_random_bytes(&tr_hdr->Nonce, SMB3_AES_GCM_NONCE); 41718c2ecf20Sopenharmony_ci else 41728c2ecf20Sopenharmony_ci get_random_bytes(&tr_hdr->Nonce, SMB3_AES_CCM_NONCE); 41738c2ecf20Sopenharmony_ci memcpy(&tr_hdr->SessionId, &shdr->SessionId, 8); 41748c2ecf20Sopenharmony_ci} 41758c2ecf20Sopenharmony_ci 41768c2ecf20Sopenharmony_cistatic void *smb2_aead_req_alloc(struct crypto_aead *tfm, const struct smb_rqst *rqst, 41778c2ecf20Sopenharmony_ci int num_rqst, const u8 *sig, u8 **iv, 41788c2ecf20Sopenharmony_ci struct aead_request **req, struct scatterlist **sgl, 41798c2ecf20Sopenharmony_ci unsigned int *num_sgs) 41808c2ecf20Sopenharmony_ci{ 41818c2ecf20Sopenharmony_ci unsigned int req_size = sizeof(**req) + crypto_aead_reqsize(tfm); 41828c2ecf20Sopenharmony_ci unsigned int iv_size = crypto_aead_ivsize(tfm); 41838c2ecf20Sopenharmony_ci unsigned int len; 41848c2ecf20Sopenharmony_ci u8 *p; 41858c2ecf20Sopenharmony_ci 41868c2ecf20Sopenharmony_ci *num_sgs = cifs_get_num_sgs(rqst, num_rqst, sig); 41878c2ecf20Sopenharmony_ci 41888c2ecf20Sopenharmony_ci len = iv_size; 41898c2ecf20Sopenharmony_ci len += crypto_aead_alignmask(tfm) & ~(crypto_tfm_ctx_alignment() - 1); 41908c2ecf20Sopenharmony_ci len = ALIGN(len, crypto_tfm_ctx_alignment()); 41918c2ecf20Sopenharmony_ci len += req_size; 41928c2ecf20Sopenharmony_ci len = ALIGN(len, __alignof__(struct scatterlist)); 41938c2ecf20Sopenharmony_ci len += *num_sgs * sizeof(**sgl); 41948c2ecf20Sopenharmony_ci 41958c2ecf20Sopenharmony_ci p = kmalloc(len, GFP_ATOMIC); 41968c2ecf20Sopenharmony_ci if (!p) 41978c2ecf20Sopenharmony_ci return NULL; 41988c2ecf20Sopenharmony_ci 41998c2ecf20Sopenharmony_ci *iv = (u8 *)PTR_ALIGN(p, crypto_aead_alignmask(tfm) + 1); 42008c2ecf20Sopenharmony_ci *req = (struct aead_request *)PTR_ALIGN(*iv + iv_size, 42018c2ecf20Sopenharmony_ci crypto_tfm_ctx_alignment()); 42028c2ecf20Sopenharmony_ci *sgl = (struct scatterlist *)PTR_ALIGN((u8 *)*req + req_size, 42038c2ecf20Sopenharmony_ci __alignof__(struct scatterlist)); 42048c2ecf20Sopenharmony_ci return p; 42058c2ecf20Sopenharmony_ci} 42068c2ecf20Sopenharmony_ci 42078c2ecf20Sopenharmony_cistatic void *smb2_get_aead_req(struct crypto_aead *tfm, const struct smb_rqst *rqst, 42088c2ecf20Sopenharmony_ci int num_rqst, const u8 *sig, u8 **iv, 42098c2ecf20Sopenharmony_ci struct aead_request **req, struct scatterlist **sgl) 42108c2ecf20Sopenharmony_ci{ 42118c2ecf20Sopenharmony_ci unsigned int off, len, skip; 42128c2ecf20Sopenharmony_ci struct scatterlist *sg; 42138c2ecf20Sopenharmony_ci unsigned int num_sgs; 42148c2ecf20Sopenharmony_ci unsigned long addr; 42158c2ecf20Sopenharmony_ci int i, j; 42168c2ecf20Sopenharmony_ci void *p; 42178c2ecf20Sopenharmony_ci 42188c2ecf20Sopenharmony_ci p = smb2_aead_req_alloc(tfm, rqst, num_rqst, sig, iv, req, sgl, &num_sgs); 42198c2ecf20Sopenharmony_ci if (!p) 42208c2ecf20Sopenharmony_ci return NULL; 42218c2ecf20Sopenharmony_ci 42228c2ecf20Sopenharmony_ci sg_init_table(*sgl, num_sgs); 42238c2ecf20Sopenharmony_ci sg = *sgl; 42248c2ecf20Sopenharmony_ci 42258c2ecf20Sopenharmony_ci /* Assumes the first rqst has a transform header as the first iov. 42268c2ecf20Sopenharmony_ci * I.e. 42278c2ecf20Sopenharmony_ci * rqst[0].rq_iov[0] is transform header 42288c2ecf20Sopenharmony_ci * rqst[0].rq_iov[1+] data to be encrypted/decrypted 42298c2ecf20Sopenharmony_ci * rqst[1+].rq_iov[0+] data to be encrypted/decrypted 42308c2ecf20Sopenharmony_ci */ 42318c2ecf20Sopenharmony_ci for (i = 0; i < num_rqst; i++) { 42328c2ecf20Sopenharmony_ci /* 42338c2ecf20Sopenharmony_ci * The first rqst has a transform header where the 42348c2ecf20Sopenharmony_ci * first 20 bytes are not part of the encrypted blob. 42358c2ecf20Sopenharmony_ci */ 42368c2ecf20Sopenharmony_ci for (j = 0; j < rqst[i].rq_nvec; j++) { 42378c2ecf20Sopenharmony_ci struct kvec *iov = &rqst[i].rq_iov[j]; 42388c2ecf20Sopenharmony_ci 42398c2ecf20Sopenharmony_ci skip = (i == 0) && (j == 0) ? 20 : 0; 42408c2ecf20Sopenharmony_ci addr = (unsigned long)iov->iov_base + skip; 42418c2ecf20Sopenharmony_ci len = iov->iov_len - skip; 42428c2ecf20Sopenharmony_ci sg = cifs_sg_set_buf(sg, (void *)addr, len); 42438c2ecf20Sopenharmony_ci } 42448c2ecf20Sopenharmony_ci for (j = 0; j < rqst[i].rq_npages; j++) { 42458c2ecf20Sopenharmony_ci rqst_page_get_length(&rqst[i], j, &len, &off); 42468c2ecf20Sopenharmony_ci sg_set_page(sg++, rqst[i].rq_pages[j], len, off); 42478c2ecf20Sopenharmony_ci } 42488c2ecf20Sopenharmony_ci } 42498c2ecf20Sopenharmony_ci cifs_sg_set_buf(sg, sig, SMB2_SIGNATURE_SIZE); 42508c2ecf20Sopenharmony_ci 42518c2ecf20Sopenharmony_ci return p; 42528c2ecf20Sopenharmony_ci} 42538c2ecf20Sopenharmony_ci 42548c2ecf20Sopenharmony_cistatic int 42558c2ecf20Sopenharmony_cismb2_get_enc_key(struct TCP_Server_Info *server, __u64 ses_id, int enc, u8 *key) 42568c2ecf20Sopenharmony_ci{ 42578c2ecf20Sopenharmony_ci struct cifs_ses *ses; 42588c2ecf20Sopenharmony_ci u8 *ses_enc_key; 42598c2ecf20Sopenharmony_ci 42608c2ecf20Sopenharmony_ci spin_lock(&cifs_tcp_ses_lock); 42618c2ecf20Sopenharmony_ci list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { 42628c2ecf20Sopenharmony_ci list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { 42638c2ecf20Sopenharmony_ci if (ses->Suid == ses_id) { 42648c2ecf20Sopenharmony_ci ses_enc_key = enc ? ses->smb3encryptionkey : 42658c2ecf20Sopenharmony_ci ses->smb3decryptionkey; 42668c2ecf20Sopenharmony_ci memcpy(key, ses_enc_key, SMB3_ENC_DEC_KEY_SIZE); 42678c2ecf20Sopenharmony_ci spin_unlock(&cifs_tcp_ses_lock); 42688c2ecf20Sopenharmony_ci return 0; 42698c2ecf20Sopenharmony_ci } 42708c2ecf20Sopenharmony_ci } 42718c2ecf20Sopenharmony_ci } 42728c2ecf20Sopenharmony_ci spin_unlock(&cifs_tcp_ses_lock); 42738c2ecf20Sopenharmony_ci 42748c2ecf20Sopenharmony_ci return -EAGAIN; 42758c2ecf20Sopenharmony_ci} 42768c2ecf20Sopenharmony_ci/* 42778c2ecf20Sopenharmony_ci * Encrypt or decrypt @rqst message. @rqst[0] has the following format: 42788c2ecf20Sopenharmony_ci * iov[0] - transform header (associate data), 42798c2ecf20Sopenharmony_ci * iov[1-N] - SMB2 header and pages - data to encrypt. 42808c2ecf20Sopenharmony_ci * On success return encrypted data in iov[1-N] and pages, leave iov[0] 42818c2ecf20Sopenharmony_ci * untouched. 42828c2ecf20Sopenharmony_ci */ 42838c2ecf20Sopenharmony_cistatic int 42848c2ecf20Sopenharmony_cicrypt_message(struct TCP_Server_Info *server, int num_rqst, 42858c2ecf20Sopenharmony_ci struct smb_rqst *rqst, int enc) 42868c2ecf20Sopenharmony_ci{ 42878c2ecf20Sopenharmony_ci struct smb2_transform_hdr *tr_hdr = 42888c2ecf20Sopenharmony_ci (struct smb2_transform_hdr *)rqst[0].rq_iov[0].iov_base; 42898c2ecf20Sopenharmony_ci unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20; 42908c2ecf20Sopenharmony_ci int rc = 0; 42918c2ecf20Sopenharmony_ci struct scatterlist *sg; 42928c2ecf20Sopenharmony_ci u8 sign[SMB2_SIGNATURE_SIZE] = {}; 42938c2ecf20Sopenharmony_ci u8 key[SMB3_ENC_DEC_KEY_SIZE]; 42948c2ecf20Sopenharmony_ci struct aead_request *req; 42958c2ecf20Sopenharmony_ci u8 *iv; 42968c2ecf20Sopenharmony_ci DECLARE_CRYPTO_WAIT(wait); 42978c2ecf20Sopenharmony_ci struct crypto_aead *tfm; 42988c2ecf20Sopenharmony_ci unsigned int crypt_len = le32_to_cpu(tr_hdr->OriginalMessageSize); 42998c2ecf20Sopenharmony_ci void *creq; 43008c2ecf20Sopenharmony_ci 43018c2ecf20Sopenharmony_ci rc = smb2_get_enc_key(server, tr_hdr->SessionId, enc, key); 43028c2ecf20Sopenharmony_ci if (rc) { 43038c2ecf20Sopenharmony_ci cifs_server_dbg(VFS, "%s: Could not get %scryption key\n", __func__, 43048c2ecf20Sopenharmony_ci enc ? "en" : "de"); 43058c2ecf20Sopenharmony_ci return rc; 43068c2ecf20Sopenharmony_ci } 43078c2ecf20Sopenharmony_ci 43088c2ecf20Sopenharmony_ci rc = smb3_crypto_aead_allocate(server); 43098c2ecf20Sopenharmony_ci if (rc) { 43108c2ecf20Sopenharmony_ci cifs_server_dbg(VFS, "%s: crypto alloc failed\n", __func__); 43118c2ecf20Sopenharmony_ci return rc; 43128c2ecf20Sopenharmony_ci } 43138c2ecf20Sopenharmony_ci 43148c2ecf20Sopenharmony_ci tfm = enc ? server->secmech.ccmaesencrypt : 43158c2ecf20Sopenharmony_ci server->secmech.ccmaesdecrypt; 43168c2ecf20Sopenharmony_ci 43178c2ecf20Sopenharmony_ci if ((server->cipher_type == SMB2_ENCRYPTION_AES256_CCM) || 43188c2ecf20Sopenharmony_ci (server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) 43198c2ecf20Sopenharmony_ci rc = crypto_aead_setkey(tfm, key, SMB3_GCM256_CRYPTKEY_SIZE); 43208c2ecf20Sopenharmony_ci else 43218c2ecf20Sopenharmony_ci rc = crypto_aead_setkey(tfm, key, SMB3_GCM128_CRYPTKEY_SIZE); 43228c2ecf20Sopenharmony_ci 43238c2ecf20Sopenharmony_ci if (rc) { 43248c2ecf20Sopenharmony_ci cifs_server_dbg(VFS, "%s: Failed to set aead key %d\n", __func__, rc); 43258c2ecf20Sopenharmony_ci return rc; 43268c2ecf20Sopenharmony_ci } 43278c2ecf20Sopenharmony_ci 43288c2ecf20Sopenharmony_ci rc = crypto_aead_setauthsize(tfm, SMB2_SIGNATURE_SIZE); 43298c2ecf20Sopenharmony_ci if (rc) { 43308c2ecf20Sopenharmony_ci cifs_server_dbg(VFS, "%s: Failed to set authsize %d\n", __func__, rc); 43318c2ecf20Sopenharmony_ci return rc; 43328c2ecf20Sopenharmony_ci } 43338c2ecf20Sopenharmony_ci 43348c2ecf20Sopenharmony_ci creq = smb2_get_aead_req(tfm, rqst, num_rqst, sign, &iv, &req, &sg); 43358c2ecf20Sopenharmony_ci if (unlikely(!creq)) 43368c2ecf20Sopenharmony_ci return -ENOMEM; 43378c2ecf20Sopenharmony_ci 43388c2ecf20Sopenharmony_ci if (!enc) { 43398c2ecf20Sopenharmony_ci memcpy(sign, &tr_hdr->Signature, SMB2_SIGNATURE_SIZE); 43408c2ecf20Sopenharmony_ci crypt_len += SMB2_SIGNATURE_SIZE; 43418c2ecf20Sopenharmony_ci } 43428c2ecf20Sopenharmony_ci 43438c2ecf20Sopenharmony_ci if ((server->cipher_type == SMB2_ENCRYPTION_AES128_GCM) || 43448c2ecf20Sopenharmony_ci (server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) 43458c2ecf20Sopenharmony_ci memcpy(iv, (char *)tr_hdr->Nonce, SMB3_AES_GCM_NONCE); 43468c2ecf20Sopenharmony_ci else { 43478c2ecf20Sopenharmony_ci iv[0] = 3; 43488c2ecf20Sopenharmony_ci memcpy(iv + 1, (char *)tr_hdr->Nonce, SMB3_AES_CCM_NONCE); 43498c2ecf20Sopenharmony_ci } 43508c2ecf20Sopenharmony_ci 43518c2ecf20Sopenharmony_ci aead_request_set_tfm(req, tfm); 43528c2ecf20Sopenharmony_ci aead_request_set_crypt(req, sg, sg, crypt_len, iv); 43538c2ecf20Sopenharmony_ci aead_request_set_ad(req, assoc_data_len); 43548c2ecf20Sopenharmony_ci 43558c2ecf20Sopenharmony_ci aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, 43568c2ecf20Sopenharmony_ci crypto_req_done, &wait); 43578c2ecf20Sopenharmony_ci 43588c2ecf20Sopenharmony_ci rc = crypto_wait_req(enc ? crypto_aead_encrypt(req) 43598c2ecf20Sopenharmony_ci : crypto_aead_decrypt(req), &wait); 43608c2ecf20Sopenharmony_ci 43618c2ecf20Sopenharmony_ci if (!rc && enc) 43628c2ecf20Sopenharmony_ci memcpy(&tr_hdr->Signature, sign, SMB2_SIGNATURE_SIZE); 43638c2ecf20Sopenharmony_ci 43648c2ecf20Sopenharmony_ci kfree_sensitive(creq); 43658c2ecf20Sopenharmony_ci return rc; 43668c2ecf20Sopenharmony_ci} 43678c2ecf20Sopenharmony_ci 43688c2ecf20Sopenharmony_civoid 43698c2ecf20Sopenharmony_cismb3_free_compound_rqst(int num_rqst, struct smb_rqst *rqst) 43708c2ecf20Sopenharmony_ci{ 43718c2ecf20Sopenharmony_ci int i, j; 43728c2ecf20Sopenharmony_ci 43738c2ecf20Sopenharmony_ci for (i = 0; i < num_rqst; i++) { 43748c2ecf20Sopenharmony_ci if (rqst[i].rq_pages) { 43758c2ecf20Sopenharmony_ci for (j = rqst[i].rq_npages - 1; j >= 0; j--) 43768c2ecf20Sopenharmony_ci put_page(rqst[i].rq_pages[j]); 43778c2ecf20Sopenharmony_ci kfree(rqst[i].rq_pages); 43788c2ecf20Sopenharmony_ci } 43798c2ecf20Sopenharmony_ci } 43808c2ecf20Sopenharmony_ci} 43818c2ecf20Sopenharmony_ci 43828c2ecf20Sopenharmony_ci/* 43838c2ecf20Sopenharmony_ci * This function will initialize new_rq and encrypt the content. 43848c2ecf20Sopenharmony_ci * The first entry, new_rq[0], only contains a single iov which contains 43858c2ecf20Sopenharmony_ci * a smb2_transform_hdr and is pre-allocated by the caller. 43868c2ecf20Sopenharmony_ci * This function then populates new_rq[1+] with the content from olq_rq[0+]. 43878c2ecf20Sopenharmony_ci * 43888c2ecf20Sopenharmony_ci * The end result is an array of smb_rqst structures where the first structure 43898c2ecf20Sopenharmony_ci * only contains a single iov for the transform header which we then can pass 43908c2ecf20Sopenharmony_ci * to crypt_message(). 43918c2ecf20Sopenharmony_ci * 43928c2ecf20Sopenharmony_ci * new_rq[0].rq_iov[0] : smb2_transform_hdr pre-allocated by the caller 43938c2ecf20Sopenharmony_ci * new_rq[1+].rq_iov[*] == old_rq[0+].rq_iov[*] : SMB2/3 requests 43948c2ecf20Sopenharmony_ci */ 43958c2ecf20Sopenharmony_cistatic int 43968c2ecf20Sopenharmony_cismb3_init_transform_rq(struct TCP_Server_Info *server, int num_rqst, 43978c2ecf20Sopenharmony_ci struct smb_rqst *new_rq, struct smb_rqst *old_rq) 43988c2ecf20Sopenharmony_ci{ 43998c2ecf20Sopenharmony_ci struct page **pages; 44008c2ecf20Sopenharmony_ci struct smb2_transform_hdr *tr_hdr = new_rq[0].rq_iov[0].iov_base; 44018c2ecf20Sopenharmony_ci unsigned int npages; 44028c2ecf20Sopenharmony_ci unsigned int orig_len = 0; 44038c2ecf20Sopenharmony_ci int i, j; 44048c2ecf20Sopenharmony_ci int rc = -ENOMEM; 44058c2ecf20Sopenharmony_ci 44068c2ecf20Sopenharmony_ci for (i = 1; i < num_rqst; i++) { 44078c2ecf20Sopenharmony_ci npages = old_rq[i - 1].rq_npages; 44088c2ecf20Sopenharmony_ci pages = kmalloc_array(npages, sizeof(struct page *), 44098c2ecf20Sopenharmony_ci GFP_KERNEL); 44108c2ecf20Sopenharmony_ci if (!pages) 44118c2ecf20Sopenharmony_ci goto err_free; 44128c2ecf20Sopenharmony_ci 44138c2ecf20Sopenharmony_ci new_rq[i].rq_pages = pages; 44148c2ecf20Sopenharmony_ci new_rq[i].rq_npages = npages; 44158c2ecf20Sopenharmony_ci new_rq[i].rq_offset = old_rq[i - 1].rq_offset; 44168c2ecf20Sopenharmony_ci new_rq[i].rq_pagesz = old_rq[i - 1].rq_pagesz; 44178c2ecf20Sopenharmony_ci new_rq[i].rq_tailsz = old_rq[i - 1].rq_tailsz; 44188c2ecf20Sopenharmony_ci new_rq[i].rq_iov = old_rq[i - 1].rq_iov; 44198c2ecf20Sopenharmony_ci new_rq[i].rq_nvec = old_rq[i - 1].rq_nvec; 44208c2ecf20Sopenharmony_ci 44218c2ecf20Sopenharmony_ci orig_len += smb_rqst_len(server, &old_rq[i - 1]); 44228c2ecf20Sopenharmony_ci 44238c2ecf20Sopenharmony_ci for (j = 0; j < npages; j++) { 44248c2ecf20Sopenharmony_ci pages[j] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM); 44258c2ecf20Sopenharmony_ci if (!pages[j]) 44268c2ecf20Sopenharmony_ci goto err_free; 44278c2ecf20Sopenharmony_ci } 44288c2ecf20Sopenharmony_ci 44298c2ecf20Sopenharmony_ci /* copy pages form the old */ 44308c2ecf20Sopenharmony_ci for (j = 0; j < npages; j++) { 44318c2ecf20Sopenharmony_ci char *dst, *src; 44328c2ecf20Sopenharmony_ci unsigned int offset, len; 44338c2ecf20Sopenharmony_ci 44348c2ecf20Sopenharmony_ci rqst_page_get_length(&new_rq[i], j, &len, &offset); 44358c2ecf20Sopenharmony_ci 44368c2ecf20Sopenharmony_ci dst = (char *) kmap(new_rq[i].rq_pages[j]) + offset; 44378c2ecf20Sopenharmony_ci src = (char *) kmap(old_rq[i - 1].rq_pages[j]) + offset; 44388c2ecf20Sopenharmony_ci 44398c2ecf20Sopenharmony_ci memcpy(dst, src, len); 44408c2ecf20Sopenharmony_ci kunmap(new_rq[i].rq_pages[j]); 44418c2ecf20Sopenharmony_ci kunmap(old_rq[i - 1].rq_pages[j]); 44428c2ecf20Sopenharmony_ci } 44438c2ecf20Sopenharmony_ci } 44448c2ecf20Sopenharmony_ci 44458c2ecf20Sopenharmony_ci /* fill the 1st iov with a transform header */ 44468c2ecf20Sopenharmony_ci fill_transform_hdr(tr_hdr, orig_len, old_rq, server->cipher_type); 44478c2ecf20Sopenharmony_ci 44488c2ecf20Sopenharmony_ci rc = crypt_message(server, num_rqst, new_rq, 1); 44498c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Encrypt message returned %d\n", rc); 44508c2ecf20Sopenharmony_ci if (rc) 44518c2ecf20Sopenharmony_ci goto err_free; 44528c2ecf20Sopenharmony_ci 44538c2ecf20Sopenharmony_ci return rc; 44548c2ecf20Sopenharmony_ci 44558c2ecf20Sopenharmony_cierr_free: 44568c2ecf20Sopenharmony_ci smb3_free_compound_rqst(num_rqst - 1, &new_rq[1]); 44578c2ecf20Sopenharmony_ci return rc; 44588c2ecf20Sopenharmony_ci} 44598c2ecf20Sopenharmony_ci 44608c2ecf20Sopenharmony_cistatic int 44618c2ecf20Sopenharmony_cismb3_is_transform_hdr(void *buf) 44628c2ecf20Sopenharmony_ci{ 44638c2ecf20Sopenharmony_ci struct smb2_transform_hdr *trhdr = buf; 44648c2ecf20Sopenharmony_ci 44658c2ecf20Sopenharmony_ci return trhdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM; 44668c2ecf20Sopenharmony_ci} 44678c2ecf20Sopenharmony_ci 44688c2ecf20Sopenharmony_cistatic int 44698c2ecf20Sopenharmony_cidecrypt_raw_data(struct TCP_Server_Info *server, char *buf, 44708c2ecf20Sopenharmony_ci unsigned int buf_data_size, struct page **pages, 44718c2ecf20Sopenharmony_ci unsigned int npages, unsigned int page_data_size, 44728c2ecf20Sopenharmony_ci bool is_offloaded) 44738c2ecf20Sopenharmony_ci{ 44748c2ecf20Sopenharmony_ci struct kvec iov[2]; 44758c2ecf20Sopenharmony_ci struct smb_rqst rqst = {NULL}; 44768c2ecf20Sopenharmony_ci int rc; 44778c2ecf20Sopenharmony_ci 44788c2ecf20Sopenharmony_ci iov[0].iov_base = buf; 44798c2ecf20Sopenharmony_ci iov[0].iov_len = sizeof(struct smb2_transform_hdr); 44808c2ecf20Sopenharmony_ci iov[1].iov_base = buf + sizeof(struct smb2_transform_hdr); 44818c2ecf20Sopenharmony_ci iov[1].iov_len = buf_data_size; 44828c2ecf20Sopenharmony_ci 44838c2ecf20Sopenharmony_ci rqst.rq_iov = iov; 44848c2ecf20Sopenharmony_ci rqst.rq_nvec = 2; 44858c2ecf20Sopenharmony_ci rqst.rq_pages = pages; 44868c2ecf20Sopenharmony_ci rqst.rq_npages = npages; 44878c2ecf20Sopenharmony_ci rqst.rq_pagesz = PAGE_SIZE; 44888c2ecf20Sopenharmony_ci rqst.rq_tailsz = (page_data_size % PAGE_SIZE) ? : PAGE_SIZE; 44898c2ecf20Sopenharmony_ci 44908c2ecf20Sopenharmony_ci rc = crypt_message(server, 1, &rqst, 0); 44918c2ecf20Sopenharmony_ci cifs_dbg(FYI, "Decrypt message returned %d\n", rc); 44928c2ecf20Sopenharmony_ci 44938c2ecf20Sopenharmony_ci if (rc) 44948c2ecf20Sopenharmony_ci return rc; 44958c2ecf20Sopenharmony_ci 44968c2ecf20Sopenharmony_ci memmove(buf, iov[1].iov_base, buf_data_size); 44978c2ecf20Sopenharmony_ci 44988c2ecf20Sopenharmony_ci if (!is_offloaded) 44998c2ecf20Sopenharmony_ci server->total_read = buf_data_size + page_data_size; 45008c2ecf20Sopenharmony_ci 45018c2ecf20Sopenharmony_ci return rc; 45028c2ecf20Sopenharmony_ci} 45038c2ecf20Sopenharmony_ci 45048c2ecf20Sopenharmony_cistatic int 45058c2ecf20Sopenharmony_ciread_data_into_pages(struct TCP_Server_Info *server, struct page **pages, 45068c2ecf20Sopenharmony_ci unsigned int npages, unsigned int len) 45078c2ecf20Sopenharmony_ci{ 45088c2ecf20Sopenharmony_ci int i; 45098c2ecf20Sopenharmony_ci int length; 45108c2ecf20Sopenharmony_ci 45118c2ecf20Sopenharmony_ci for (i = 0; i < npages; i++) { 45128c2ecf20Sopenharmony_ci struct page *page = pages[i]; 45138c2ecf20Sopenharmony_ci size_t n; 45148c2ecf20Sopenharmony_ci 45158c2ecf20Sopenharmony_ci n = len; 45168c2ecf20Sopenharmony_ci if (len >= PAGE_SIZE) { 45178c2ecf20Sopenharmony_ci /* enough data to fill the page */ 45188c2ecf20Sopenharmony_ci n = PAGE_SIZE; 45198c2ecf20Sopenharmony_ci len -= n; 45208c2ecf20Sopenharmony_ci } else { 45218c2ecf20Sopenharmony_ci zero_user(page, len, PAGE_SIZE - len); 45228c2ecf20Sopenharmony_ci len = 0; 45238c2ecf20Sopenharmony_ci } 45248c2ecf20Sopenharmony_ci length = cifs_read_page_from_socket(server, page, 0, n); 45258c2ecf20Sopenharmony_ci if (length < 0) 45268c2ecf20Sopenharmony_ci return length; 45278c2ecf20Sopenharmony_ci server->total_read += length; 45288c2ecf20Sopenharmony_ci } 45298c2ecf20Sopenharmony_ci 45308c2ecf20Sopenharmony_ci return 0; 45318c2ecf20Sopenharmony_ci} 45328c2ecf20Sopenharmony_ci 45338c2ecf20Sopenharmony_cistatic int 45348c2ecf20Sopenharmony_ciinit_read_bvec(struct page **pages, unsigned int npages, unsigned int data_size, 45358c2ecf20Sopenharmony_ci unsigned int cur_off, struct bio_vec **page_vec) 45368c2ecf20Sopenharmony_ci{ 45378c2ecf20Sopenharmony_ci struct bio_vec *bvec; 45388c2ecf20Sopenharmony_ci int i; 45398c2ecf20Sopenharmony_ci 45408c2ecf20Sopenharmony_ci bvec = kcalloc(npages, sizeof(struct bio_vec), GFP_KERNEL); 45418c2ecf20Sopenharmony_ci if (!bvec) 45428c2ecf20Sopenharmony_ci return -ENOMEM; 45438c2ecf20Sopenharmony_ci 45448c2ecf20Sopenharmony_ci for (i = 0; i < npages; i++) { 45458c2ecf20Sopenharmony_ci bvec[i].bv_page = pages[i]; 45468c2ecf20Sopenharmony_ci bvec[i].bv_offset = (i == 0) ? cur_off : 0; 45478c2ecf20Sopenharmony_ci bvec[i].bv_len = min_t(unsigned int, PAGE_SIZE, data_size); 45488c2ecf20Sopenharmony_ci data_size -= bvec[i].bv_len; 45498c2ecf20Sopenharmony_ci } 45508c2ecf20Sopenharmony_ci 45518c2ecf20Sopenharmony_ci if (data_size != 0) { 45528c2ecf20Sopenharmony_ci cifs_dbg(VFS, "%s: something went wrong\n", __func__); 45538c2ecf20Sopenharmony_ci kfree(bvec); 45548c2ecf20Sopenharmony_ci return -EIO; 45558c2ecf20Sopenharmony_ci } 45568c2ecf20Sopenharmony_ci 45578c2ecf20Sopenharmony_ci *page_vec = bvec; 45588c2ecf20Sopenharmony_ci return 0; 45598c2ecf20Sopenharmony_ci} 45608c2ecf20Sopenharmony_ci 45618c2ecf20Sopenharmony_cistatic int 45628c2ecf20Sopenharmony_cihandle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid, 45638c2ecf20Sopenharmony_ci char *buf, unsigned int buf_len, struct page **pages, 45648c2ecf20Sopenharmony_ci unsigned int npages, unsigned int page_data_size, 45658c2ecf20Sopenharmony_ci bool is_offloaded) 45668c2ecf20Sopenharmony_ci{ 45678c2ecf20Sopenharmony_ci unsigned int data_offset; 45688c2ecf20Sopenharmony_ci unsigned int data_len; 45698c2ecf20Sopenharmony_ci unsigned int cur_off; 45708c2ecf20Sopenharmony_ci unsigned int cur_page_idx; 45718c2ecf20Sopenharmony_ci unsigned int pad_len; 45728c2ecf20Sopenharmony_ci struct cifs_readdata *rdata = mid->callback_data; 45738c2ecf20Sopenharmony_ci struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf; 45748c2ecf20Sopenharmony_ci struct bio_vec *bvec = NULL; 45758c2ecf20Sopenharmony_ci struct iov_iter iter; 45768c2ecf20Sopenharmony_ci struct kvec iov; 45778c2ecf20Sopenharmony_ci int length; 45788c2ecf20Sopenharmony_ci bool use_rdma_mr = false; 45798c2ecf20Sopenharmony_ci 45808c2ecf20Sopenharmony_ci if (shdr->Command != SMB2_READ) { 45818c2ecf20Sopenharmony_ci cifs_server_dbg(VFS, "only big read responses are supported\n"); 45828c2ecf20Sopenharmony_ci return -ENOTSUPP; 45838c2ecf20Sopenharmony_ci } 45848c2ecf20Sopenharmony_ci 45858c2ecf20Sopenharmony_ci if (server->ops->is_session_expired && 45868c2ecf20Sopenharmony_ci server->ops->is_session_expired(buf)) { 45878c2ecf20Sopenharmony_ci if (!is_offloaded) 45888c2ecf20Sopenharmony_ci cifs_reconnect(server); 45898c2ecf20Sopenharmony_ci return -1; 45908c2ecf20Sopenharmony_ci } 45918c2ecf20Sopenharmony_ci 45928c2ecf20Sopenharmony_ci if (server->ops->is_status_pending && 45938c2ecf20Sopenharmony_ci server->ops->is_status_pending(buf, server)) 45948c2ecf20Sopenharmony_ci return -1; 45958c2ecf20Sopenharmony_ci 45968c2ecf20Sopenharmony_ci /* set up first two iov to get credits */ 45978c2ecf20Sopenharmony_ci rdata->iov[0].iov_base = buf; 45988c2ecf20Sopenharmony_ci rdata->iov[0].iov_len = 0; 45998c2ecf20Sopenharmony_ci rdata->iov[1].iov_base = buf; 46008c2ecf20Sopenharmony_ci rdata->iov[1].iov_len = 46018c2ecf20Sopenharmony_ci min_t(unsigned int, buf_len, server->vals->read_rsp_size); 46028c2ecf20Sopenharmony_ci cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n", 46038c2ecf20Sopenharmony_ci rdata->iov[0].iov_base, rdata->iov[0].iov_len); 46048c2ecf20Sopenharmony_ci cifs_dbg(FYI, "1: iov_base=%p iov_len=%zu\n", 46058c2ecf20Sopenharmony_ci rdata->iov[1].iov_base, rdata->iov[1].iov_len); 46068c2ecf20Sopenharmony_ci 46078c2ecf20Sopenharmony_ci rdata->result = server->ops->map_error(buf, true); 46088c2ecf20Sopenharmony_ci if (rdata->result != 0) { 46098c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: server returned error %d\n", 46108c2ecf20Sopenharmony_ci __func__, rdata->result); 46118c2ecf20Sopenharmony_ci /* normal error on read response */ 46128c2ecf20Sopenharmony_ci if (is_offloaded) 46138c2ecf20Sopenharmony_ci mid->mid_state = MID_RESPONSE_RECEIVED; 46148c2ecf20Sopenharmony_ci else 46158c2ecf20Sopenharmony_ci dequeue_mid(mid, false); 46168c2ecf20Sopenharmony_ci return 0; 46178c2ecf20Sopenharmony_ci } 46188c2ecf20Sopenharmony_ci 46198c2ecf20Sopenharmony_ci data_offset = server->ops->read_data_offset(buf); 46208c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_SMB_DIRECT 46218c2ecf20Sopenharmony_ci use_rdma_mr = rdata->mr; 46228c2ecf20Sopenharmony_ci#endif 46238c2ecf20Sopenharmony_ci data_len = server->ops->read_data_length(buf, use_rdma_mr); 46248c2ecf20Sopenharmony_ci 46258c2ecf20Sopenharmony_ci if (data_offset < server->vals->read_rsp_size) { 46268c2ecf20Sopenharmony_ci /* 46278c2ecf20Sopenharmony_ci * win2k8 sometimes sends an offset of 0 when the read 46288c2ecf20Sopenharmony_ci * is beyond the EOF. Treat it as if the data starts just after 46298c2ecf20Sopenharmony_ci * the header. 46308c2ecf20Sopenharmony_ci */ 46318c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: data offset (%u) inside read response header\n", 46328c2ecf20Sopenharmony_ci __func__, data_offset); 46338c2ecf20Sopenharmony_ci data_offset = server->vals->read_rsp_size; 46348c2ecf20Sopenharmony_ci } else if (data_offset > MAX_CIFS_SMALL_BUFFER_SIZE) { 46358c2ecf20Sopenharmony_ci /* data_offset is beyond the end of smallbuf */ 46368c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: data offset (%u) beyond end of smallbuf\n", 46378c2ecf20Sopenharmony_ci __func__, data_offset); 46388c2ecf20Sopenharmony_ci rdata->result = -EIO; 46398c2ecf20Sopenharmony_ci if (is_offloaded) 46408c2ecf20Sopenharmony_ci mid->mid_state = MID_RESPONSE_MALFORMED; 46418c2ecf20Sopenharmony_ci else 46428c2ecf20Sopenharmony_ci dequeue_mid(mid, rdata->result); 46438c2ecf20Sopenharmony_ci return 0; 46448c2ecf20Sopenharmony_ci } 46458c2ecf20Sopenharmony_ci 46468c2ecf20Sopenharmony_ci pad_len = data_offset - server->vals->read_rsp_size; 46478c2ecf20Sopenharmony_ci 46488c2ecf20Sopenharmony_ci if (buf_len <= data_offset) { 46498c2ecf20Sopenharmony_ci /* read response payload is in pages */ 46508c2ecf20Sopenharmony_ci cur_page_idx = pad_len / PAGE_SIZE; 46518c2ecf20Sopenharmony_ci cur_off = pad_len % PAGE_SIZE; 46528c2ecf20Sopenharmony_ci 46538c2ecf20Sopenharmony_ci if (cur_page_idx != 0) { 46548c2ecf20Sopenharmony_ci /* data offset is beyond the 1st page of response */ 46558c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: data offset (%u) beyond 1st page of response\n", 46568c2ecf20Sopenharmony_ci __func__, data_offset); 46578c2ecf20Sopenharmony_ci rdata->result = -EIO; 46588c2ecf20Sopenharmony_ci if (is_offloaded) 46598c2ecf20Sopenharmony_ci mid->mid_state = MID_RESPONSE_MALFORMED; 46608c2ecf20Sopenharmony_ci else 46618c2ecf20Sopenharmony_ci dequeue_mid(mid, rdata->result); 46628c2ecf20Sopenharmony_ci return 0; 46638c2ecf20Sopenharmony_ci } 46648c2ecf20Sopenharmony_ci 46658c2ecf20Sopenharmony_ci if (data_len > page_data_size - pad_len) { 46668c2ecf20Sopenharmony_ci /* data_len is corrupt -- discard frame */ 46678c2ecf20Sopenharmony_ci rdata->result = -EIO; 46688c2ecf20Sopenharmony_ci if (is_offloaded) 46698c2ecf20Sopenharmony_ci mid->mid_state = MID_RESPONSE_MALFORMED; 46708c2ecf20Sopenharmony_ci else 46718c2ecf20Sopenharmony_ci dequeue_mid(mid, rdata->result); 46728c2ecf20Sopenharmony_ci return 0; 46738c2ecf20Sopenharmony_ci } 46748c2ecf20Sopenharmony_ci 46758c2ecf20Sopenharmony_ci rdata->result = init_read_bvec(pages, npages, page_data_size, 46768c2ecf20Sopenharmony_ci cur_off, &bvec); 46778c2ecf20Sopenharmony_ci if (rdata->result != 0) { 46788c2ecf20Sopenharmony_ci if (is_offloaded) 46798c2ecf20Sopenharmony_ci mid->mid_state = MID_RESPONSE_MALFORMED; 46808c2ecf20Sopenharmony_ci else 46818c2ecf20Sopenharmony_ci dequeue_mid(mid, rdata->result); 46828c2ecf20Sopenharmony_ci return 0; 46838c2ecf20Sopenharmony_ci } 46848c2ecf20Sopenharmony_ci 46858c2ecf20Sopenharmony_ci iov_iter_bvec(&iter, WRITE, bvec, npages, data_len); 46868c2ecf20Sopenharmony_ci } else if (buf_len >= data_offset + data_len) { 46878c2ecf20Sopenharmony_ci /* read response payload is in buf */ 46888c2ecf20Sopenharmony_ci WARN_ONCE(npages > 0, "read data can be either in buf or in pages"); 46898c2ecf20Sopenharmony_ci iov.iov_base = buf + data_offset; 46908c2ecf20Sopenharmony_ci iov.iov_len = data_len; 46918c2ecf20Sopenharmony_ci iov_iter_kvec(&iter, WRITE, &iov, 1, data_len); 46928c2ecf20Sopenharmony_ci } else { 46938c2ecf20Sopenharmony_ci /* read response payload cannot be in both buf and pages */ 46948c2ecf20Sopenharmony_ci WARN_ONCE(1, "buf can not contain only a part of read data"); 46958c2ecf20Sopenharmony_ci rdata->result = -EIO; 46968c2ecf20Sopenharmony_ci if (is_offloaded) 46978c2ecf20Sopenharmony_ci mid->mid_state = MID_RESPONSE_MALFORMED; 46988c2ecf20Sopenharmony_ci else 46998c2ecf20Sopenharmony_ci dequeue_mid(mid, rdata->result); 47008c2ecf20Sopenharmony_ci return 0; 47018c2ecf20Sopenharmony_ci } 47028c2ecf20Sopenharmony_ci 47038c2ecf20Sopenharmony_ci length = rdata->copy_into_pages(server, rdata, &iter); 47048c2ecf20Sopenharmony_ci 47058c2ecf20Sopenharmony_ci kfree(bvec); 47068c2ecf20Sopenharmony_ci 47078c2ecf20Sopenharmony_ci if (length < 0) 47088c2ecf20Sopenharmony_ci return length; 47098c2ecf20Sopenharmony_ci 47108c2ecf20Sopenharmony_ci if (is_offloaded) 47118c2ecf20Sopenharmony_ci mid->mid_state = MID_RESPONSE_RECEIVED; 47128c2ecf20Sopenharmony_ci else 47138c2ecf20Sopenharmony_ci dequeue_mid(mid, false); 47148c2ecf20Sopenharmony_ci return length; 47158c2ecf20Sopenharmony_ci} 47168c2ecf20Sopenharmony_ci 47178c2ecf20Sopenharmony_cistruct smb2_decrypt_work { 47188c2ecf20Sopenharmony_ci struct work_struct decrypt; 47198c2ecf20Sopenharmony_ci struct TCP_Server_Info *server; 47208c2ecf20Sopenharmony_ci struct page **ppages; 47218c2ecf20Sopenharmony_ci char *buf; 47228c2ecf20Sopenharmony_ci unsigned int npages; 47238c2ecf20Sopenharmony_ci unsigned int len; 47248c2ecf20Sopenharmony_ci}; 47258c2ecf20Sopenharmony_ci 47268c2ecf20Sopenharmony_ci 47278c2ecf20Sopenharmony_cistatic void smb2_decrypt_offload(struct work_struct *work) 47288c2ecf20Sopenharmony_ci{ 47298c2ecf20Sopenharmony_ci struct smb2_decrypt_work *dw = container_of(work, 47308c2ecf20Sopenharmony_ci struct smb2_decrypt_work, decrypt); 47318c2ecf20Sopenharmony_ci int i, rc; 47328c2ecf20Sopenharmony_ci struct mid_q_entry *mid; 47338c2ecf20Sopenharmony_ci 47348c2ecf20Sopenharmony_ci rc = decrypt_raw_data(dw->server, dw->buf, dw->server->vals->read_rsp_size, 47358c2ecf20Sopenharmony_ci dw->ppages, dw->npages, dw->len, true); 47368c2ecf20Sopenharmony_ci if (rc) { 47378c2ecf20Sopenharmony_ci cifs_dbg(VFS, "error decrypting rc=%d\n", rc); 47388c2ecf20Sopenharmony_ci goto free_pages; 47398c2ecf20Sopenharmony_ci } 47408c2ecf20Sopenharmony_ci 47418c2ecf20Sopenharmony_ci dw->server->lstrp = jiffies; 47428c2ecf20Sopenharmony_ci mid = smb2_find_dequeue_mid(dw->server, dw->buf); 47438c2ecf20Sopenharmony_ci if (mid == NULL) 47448c2ecf20Sopenharmony_ci cifs_dbg(FYI, "mid not found\n"); 47458c2ecf20Sopenharmony_ci else { 47468c2ecf20Sopenharmony_ci mid->decrypted = true; 47478c2ecf20Sopenharmony_ci rc = handle_read_data(dw->server, mid, dw->buf, 47488c2ecf20Sopenharmony_ci dw->server->vals->read_rsp_size, 47498c2ecf20Sopenharmony_ci dw->ppages, dw->npages, dw->len, 47508c2ecf20Sopenharmony_ci true); 47518c2ecf20Sopenharmony_ci if (rc >= 0) { 47528c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_STATS2 47538c2ecf20Sopenharmony_ci mid->when_received = jiffies; 47548c2ecf20Sopenharmony_ci#endif 47558c2ecf20Sopenharmony_ci mid->callback(mid); 47568c2ecf20Sopenharmony_ci } else { 47578c2ecf20Sopenharmony_ci spin_lock(&GlobalMid_Lock); 47588c2ecf20Sopenharmony_ci if (dw->server->tcpStatus == CifsNeedReconnect) { 47598c2ecf20Sopenharmony_ci mid->mid_state = MID_RETRY_NEEDED; 47608c2ecf20Sopenharmony_ci spin_unlock(&GlobalMid_Lock); 47618c2ecf20Sopenharmony_ci mid->callback(mid); 47628c2ecf20Sopenharmony_ci } else { 47638c2ecf20Sopenharmony_ci mid->mid_state = MID_REQUEST_SUBMITTED; 47648c2ecf20Sopenharmony_ci mid->mid_flags &= ~(MID_DELETED); 47658c2ecf20Sopenharmony_ci list_add_tail(&mid->qhead, 47668c2ecf20Sopenharmony_ci &dw->server->pending_mid_q); 47678c2ecf20Sopenharmony_ci spin_unlock(&GlobalMid_Lock); 47688c2ecf20Sopenharmony_ci } 47698c2ecf20Sopenharmony_ci } 47708c2ecf20Sopenharmony_ci cifs_mid_q_entry_release(mid); 47718c2ecf20Sopenharmony_ci } 47728c2ecf20Sopenharmony_ci 47738c2ecf20Sopenharmony_cifree_pages: 47748c2ecf20Sopenharmony_ci for (i = dw->npages-1; i >= 0; i--) 47758c2ecf20Sopenharmony_ci put_page(dw->ppages[i]); 47768c2ecf20Sopenharmony_ci 47778c2ecf20Sopenharmony_ci kfree(dw->ppages); 47788c2ecf20Sopenharmony_ci cifs_small_buf_release(dw->buf); 47798c2ecf20Sopenharmony_ci kfree(dw); 47808c2ecf20Sopenharmony_ci} 47818c2ecf20Sopenharmony_ci 47828c2ecf20Sopenharmony_ci 47838c2ecf20Sopenharmony_cistatic int 47848c2ecf20Sopenharmony_cireceive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid, 47858c2ecf20Sopenharmony_ci int *num_mids) 47868c2ecf20Sopenharmony_ci{ 47878c2ecf20Sopenharmony_ci char *buf = server->smallbuf; 47888c2ecf20Sopenharmony_ci struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)buf; 47898c2ecf20Sopenharmony_ci unsigned int npages; 47908c2ecf20Sopenharmony_ci struct page **pages; 47918c2ecf20Sopenharmony_ci unsigned int len; 47928c2ecf20Sopenharmony_ci unsigned int buflen = server->pdu_size; 47938c2ecf20Sopenharmony_ci int rc; 47948c2ecf20Sopenharmony_ci int i = 0; 47958c2ecf20Sopenharmony_ci struct smb2_decrypt_work *dw; 47968c2ecf20Sopenharmony_ci 47978c2ecf20Sopenharmony_ci *num_mids = 1; 47988c2ecf20Sopenharmony_ci len = min_t(unsigned int, buflen, server->vals->read_rsp_size + 47998c2ecf20Sopenharmony_ci sizeof(struct smb2_transform_hdr)) - HEADER_SIZE(server) + 1; 48008c2ecf20Sopenharmony_ci 48018c2ecf20Sopenharmony_ci rc = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1, len); 48028c2ecf20Sopenharmony_ci if (rc < 0) 48038c2ecf20Sopenharmony_ci return rc; 48048c2ecf20Sopenharmony_ci server->total_read += rc; 48058c2ecf20Sopenharmony_ci 48068c2ecf20Sopenharmony_ci len = le32_to_cpu(tr_hdr->OriginalMessageSize) - 48078c2ecf20Sopenharmony_ci server->vals->read_rsp_size; 48088c2ecf20Sopenharmony_ci npages = DIV_ROUND_UP(len, PAGE_SIZE); 48098c2ecf20Sopenharmony_ci 48108c2ecf20Sopenharmony_ci pages = kmalloc_array(npages, sizeof(struct page *), GFP_KERNEL); 48118c2ecf20Sopenharmony_ci if (!pages) { 48128c2ecf20Sopenharmony_ci rc = -ENOMEM; 48138c2ecf20Sopenharmony_ci goto discard_data; 48148c2ecf20Sopenharmony_ci } 48158c2ecf20Sopenharmony_ci 48168c2ecf20Sopenharmony_ci for (; i < npages; i++) { 48178c2ecf20Sopenharmony_ci pages[i] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM); 48188c2ecf20Sopenharmony_ci if (!pages[i]) { 48198c2ecf20Sopenharmony_ci rc = -ENOMEM; 48208c2ecf20Sopenharmony_ci goto discard_data; 48218c2ecf20Sopenharmony_ci } 48228c2ecf20Sopenharmony_ci } 48238c2ecf20Sopenharmony_ci 48248c2ecf20Sopenharmony_ci /* read read data into pages */ 48258c2ecf20Sopenharmony_ci rc = read_data_into_pages(server, pages, npages, len); 48268c2ecf20Sopenharmony_ci if (rc) 48278c2ecf20Sopenharmony_ci goto free_pages; 48288c2ecf20Sopenharmony_ci 48298c2ecf20Sopenharmony_ci rc = cifs_discard_remaining_data(server); 48308c2ecf20Sopenharmony_ci if (rc) 48318c2ecf20Sopenharmony_ci goto free_pages; 48328c2ecf20Sopenharmony_ci 48338c2ecf20Sopenharmony_ci /* 48348c2ecf20Sopenharmony_ci * For large reads, offload to different thread for better performance, 48358c2ecf20Sopenharmony_ci * use more cores decrypting which can be expensive 48368c2ecf20Sopenharmony_ci */ 48378c2ecf20Sopenharmony_ci 48388c2ecf20Sopenharmony_ci if ((server->min_offload) && (server->in_flight > 1) && 48398c2ecf20Sopenharmony_ci (server->pdu_size >= server->min_offload)) { 48408c2ecf20Sopenharmony_ci dw = kmalloc(sizeof(struct smb2_decrypt_work), GFP_KERNEL); 48418c2ecf20Sopenharmony_ci if (dw == NULL) 48428c2ecf20Sopenharmony_ci goto non_offloaded_decrypt; 48438c2ecf20Sopenharmony_ci 48448c2ecf20Sopenharmony_ci dw->buf = server->smallbuf; 48458c2ecf20Sopenharmony_ci server->smallbuf = (char *)cifs_small_buf_get(); 48468c2ecf20Sopenharmony_ci 48478c2ecf20Sopenharmony_ci INIT_WORK(&dw->decrypt, smb2_decrypt_offload); 48488c2ecf20Sopenharmony_ci 48498c2ecf20Sopenharmony_ci dw->npages = npages; 48508c2ecf20Sopenharmony_ci dw->server = server; 48518c2ecf20Sopenharmony_ci dw->ppages = pages; 48528c2ecf20Sopenharmony_ci dw->len = len; 48538c2ecf20Sopenharmony_ci queue_work(decrypt_wq, &dw->decrypt); 48548c2ecf20Sopenharmony_ci *num_mids = 0; /* worker thread takes care of finding mid */ 48558c2ecf20Sopenharmony_ci return -1; 48568c2ecf20Sopenharmony_ci } 48578c2ecf20Sopenharmony_ci 48588c2ecf20Sopenharmony_cinon_offloaded_decrypt: 48598c2ecf20Sopenharmony_ci rc = decrypt_raw_data(server, buf, server->vals->read_rsp_size, 48608c2ecf20Sopenharmony_ci pages, npages, len, false); 48618c2ecf20Sopenharmony_ci if (rc) 48628c2ecf20Sopenharmony_ci goto free_pages; 48638c2ecf20Sopenharmony_ci 48648c2ecf20Sopenharmony_ci *mid = smb2_find_mid(server, buf); 48658c2ecf20Sopenharmony_ci if (*mid == NULL) 48668c2ecf20Sopenharmony_ci cifs_dbg(FYI, "mid not found\n"); 48678c2ecf20Sopenharmony_ci else { 48688c2ecf20Sopenharmony_ci cifs_dbg(FYI, "mid found\n"); 48698c2ecf20Sopenharmony_ci (*mid)->decrypted = true; 48708c2ecf20Sopenharmony_ci rc = handle_read_data(server, *mid, buf, 48718c2ecf20Sopenharmony_ci server->vals->read_rsp_size, 48728c2ecf20Sopenharmony_ci pages, npages, len, false); 48738c2ecf20Sopenharmony_ci } 48748c2ecf20Sopenharmony_ci 48758c2ecf20Sopenharmony_cifree_pages: 48768c2ecf20Sopenharmony_ci for (i = i - 1; i >= 0; i--) 48778c2ecf20Sopenharmony_ci put_page(pages[i]); 48788c2ecf20Sopenharmony_ci kfree(pages); 48798c2ecf20Sopenharmony_ci return rc; 48808c2ecf20Sopenharmony_cidiscard_data: 48818c2ecf20Sopenharmony_ci cifs_discard_remaining_data(server); 48828c2ecf20Sopenharmony_ci goto free_pages; 48838c2ecf20Sopenharmony_ci} 48848c2ecf20Sopenharmony_ci 48858c2ecf20Sopenharmony_cistatic int 48868c2ecf20Sopenharmony_cireceive_encrypted_standard(struct TCP_Server_Info *server, 48878c2ecf20Sopenharmony_ci struct mid_q_entry **mids, char **bufs, 48888c2ecf20Sopenharmony_ci int *num_mids) 48898c2ecf20Sopenharmony_ci{ 48908c2ecf20Sopenharmony_ci int ret, length; 48918c2ecf20Sopenharmony_ci char *buf = server->smallbuf; 48928c2ecf20Sopenharmony_ci struct smb2_sync_hdr *shdr; 48938c2ecf20Sopenharmony_ci unsigned int pdu_length = server->pdu_size; 48948c2ecf20Sopenharmony_ci unsigned int buf_size; 48958c2ecf20Sopenharmony_ci struct mid_q_entry *mid_entry; 48968c2ecf20Sopenharmony_ci int next_is_large; 48978c2ecf20Sopenharmony_ci char *next_buffer = NULL; 48988c2ecf20Sopenharmony_ci 48998c2ecf20Sopenharmony_ci *num_mids = 0; 49008c2ecf20Sopenharmony_ci 49018c2ecf20Sopenharmony_ci /* switch to large buffer if too big for a small one */ 49028c2ecf20Sopenharmony_ci if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE) { 49038c2ecf20Sopenharmony_ci server->large_buf = true; 49048c2ecf20Sopenharmony_ci memcpy(server->bigbuf, buf, server->total_read); 49058c2ecf20Sopenharmony_ci buf = server->bigbuf; 49068c2ecf20Sopenharmony_ci } 49078c2ecf20Sopenharmony_ci 49088c2ecf20Sopenharmony_ci /* now read the rest */ 49098c2ecf20Sopenharmony_ci length = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1, 49108c2ecf20Sopenharmony_ci pdu_length - HEADER_SIZE(server) + 1); 49118c2ecf20Sopenharmony_ci if (length < 0) 49128c2ecf20Sopenharmony_ci return length; 49138c2ecf20Sopenharmony_ci server->total_read += length; 49148c2ecf20Sopenharmony_ci 49158c2ecf20Sopenharmony_ci buf_size = pdu_length - sizeof(struct smb2_transform_hdr); 49168c2ecf20Sopenharmony_ci length = decrypt_raw_data(server, buf, buf_size, NULL, 0, 0, false); 49178c2ecf20Sopenharmony_ci if (length) 49188c2ecf20Sopenharmony_ci return length; 49198c2ecf20Sopenharmony_ci 49208c2ecf20Sopenharmony_ci next_is_large = server->large_buf; 49218c2ecf20Sopenharmony_cione_more: 49228c2ecf20Sopenharmony_ci shdr = (struct smb2_sync_hdr *)buf; 49238c2ecf20Sopenharmony_ci if (shdr->NextCommand) { 49248c2ecf20Sopenharmony_ci if (next_is_large) 49258c2ecf20Sopenharmony_ci next_buffer = (char *)cifs_buf_get(); 49268c2ecf20Sopenharmony_ci else 49278c2ecf20Sopenharmony_ci next_buffer = (char *)cifs_small_buf_get(); 49288c2ecf20Sopenharmony_ci memcpy(next_buffer, 49298c2ecf20Sopenharmony_ci buf + le32_to_cpu(shdr->NextCommand), 49308c2ecf20Sopenharmony_ci pdu_length - le32_to_cpu(shdr->NextCommand)); 49318c2ecf20Sopenharmony_ci } 49328c2ecf20Sopenharmony_ci 49338c2ecf20Sopenharmony_ci mid_entry = smb2_find_mid(server, buf); 49348c2ecf20Sopenharmony_ci if (mid_entry == NULL) 49358c2ecf20Sopenharmony_ci cifs_dbg(FYI, "mid not found\n"); 49368c2ecf20Sopenharmony_ci else { 49378c2ecf20Sopenharmony_ci cifs_dbg(FYI, "mid found\n"); 49388c2ecf20Sopenharmony_ci mid_entry->decrypted = true; 49398c2ecf20Sopenharmony_ci mid_entry->resp_buf_size = server->pdu_size; 49408c2ecf20Sopenharmony_ci } 49418c2ecf20Sopenharmony_ci 49428c2ecf20Sopenharmony_ci if (*num_mids >= MAX_COMPOUND) { 49438c2ecf20Sopenharmony_ci cifs_server_dbg(VFS, "too many PDUs in compound\n"); 49448c2ecf20Sopenharmony_ci return -1; 49458c2ecf20Sopenharmony_ci } 49468c2ecf20Sopenharmony_ci bufs[*num_mids] = buf; 49478c2ecf20Sopenharmony_ci mids[(*num_mids)++] = mid_entry; 49488c2ecf20Sopenharmony_ci 49498c2ecf20Sopenharmony_ci if (mid_entry && mid_entry->handle) 49508c2ecf20Sopenharmony_ci ret = mid_entry->handle(server, mid_entry); 49518c2ecf20Sopenharmony_ci else 49528c2ecf20Sopenharmony_ci ret = cifs_handle_standard(server, mid_entry); 49538c2ecf20Sopenharmony_ci 49548c2ecf20Sopenharmony_ci if (ret == 0 && shdr->NextCommand) { 49558c2ecf20Sopenharmony_ci pdu_length -= le32_to_cpu(shdr->NextCommand); 49568c2ecf20Sopenharmony_ci server->large_buf = next_is_large; 49578c2ecf20Sopenharmony_ci if (next_is_large) 49588c2ecf20Sopenharmony_ci server->bigbuf = buf = next_buffer; 49598c2ecf20Sopenharmony_ci else 49608c2ecf20Sopenharmony_ci server->smallbuf = buf = next_buffer; 49618c2ecf20Sopenharmony_ci goto one_more; 49628c2ecf20Sopenharmony_ci } else if (ret != 0) { 49638c2ecf20Sopenharmony_ci /* 49648c2ecf20Sopenharmony_ci * ret != 0 here means that we didn't get to handle_mid() thus 49658c2ecf20Sopenharmony_ci * server->smallbuf and server->bigbuf are still valid. We need 49668c2ecf20Sopenharmony_ci * to free next_buffer because it is not going to be used 49678c2ecf20Sopenharmony_ci * anywhere. 49688c2ecf20Sopenharmony_ci */ 49698c2ecf20Sopenharmony_ci if (next_is_large) 49708c2ecf20Sopenharmony_ci free_rsp_buf(CIFS_LARGE_BUFFER, next_buffer); 49718c2ecf20Sopenharmony_ci else 49728c2ecf20Sopenharmony_ci free_rsp_buf(CIFS_SMALL_BUFFER, next_buffer); 49738c2ecf20Sopenharmony_ci } 49748c2ecf20Sopenharmony_ci 49758c2ecf20Sopenharmony_ci return ret; 49768c2ecf20Sopenharmony_ci} 49778c2ecf20Sopenharmony_ci 49788c2ecf20Sopenharmony_cistatic int 49798c2ecf20Sopenharmony_cismb3_receive_transform(struct TCP_Server_Info *server, 49808c2ecf20Sopenharmony_ci struct mid_q_entry **mids, char **bufs, int *num_mids) 49818c2ecf20Sopenharmony_ci{ 49828c2ecf20Sopenharmony_ci char *buf = server->smallbuf; 49838c2ecf20Sopenharmony_ci unsigned int pdu_length = server->pdu_size; 49848c2ecf20Sopenharmony_ci struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)buf; 49858c2ecf20Sopenharmony_ci unsigned int orig_len = le32_to_cpu(tr_hdr->OriginalMessageSize); 49868c2ecf20Sopenharmony_ci 49878c2ecf20Sopenharmony_ci if (pdu_length < sizeof(struct smb2_transform_hdr) + 49888c2ecf20Sopenharmony_ci sizeof(struct smb2_sync_hdr)) { 49898c2ecf20Sopenharmony_ci cifs_server_dbg(VFS, "Transform message is too small (%u)\n", 49908c2ecf20Sopenharmony_ci pdu_length); 49918c2ecf20Sopenharmony_ci cifs_reconnect(server); 49928c2ecf20Sopenharmony_ci return -ECONNABORTED; 49938c2ecf20Sopenharmony_ci } 49948c2ecf20Sopenharmony_ci 49958c2ecf20Sopenharmony_ci if (pdu_length < orig_len + sizeof(struct smb2_transform_hdr)) { 49968c2ecf20Sopenharmony_ci cifs_server_dbg(VFS, "Transform message is broken\n"); 49978c2ecf20Sopenharmony_ci cifs_reconnect(server); 49988c2ecf20Sopenharmony_ci return -ECONNABORTED; 49998c2ecf20Sopenharmony_ci } 50008c2ecf20Sopenharmony_ci 50018c2ecf20Sopenharmony_ci /* TODO: add support for compounds containing READ. */ 50028c2ecf20Sopenharmony_ci if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server)) { 50038c2ecf20Sopenharmony_ci return receive_encrypted_read(server, &mids[0], num_mids); 50048c2ecf20Sopenharmony_ci } 50058c2ecf20Sopenharmony_ci 50068c2ecf20Sopenharmony_ci return receive_encrypted_standard(server, mids, bufs, num_mids); 50078c2ecf20Sopenharmony_ci} 50088c2ecf20Sopenharmony_ci 50098c2ecf20Sopenharmony_ciint 50108c2ecf20Sopenharmony_cismb3_handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid) 50118c2ecf20Sopenharmony_ci{ 50128c2ecf20Sopenharmony_ci char *buf = server->large_buf ? server->bigbuf : server->smallbuf; 50138c2ecf20Sopenharmony_ci 50148c2ecf20Sopenharmony_ci return handle_read_data(server, mid, buf, server->pdu_size, 50158c2ecf20Sopenharmony_ci NULL, 0, 0, false); 50168c2ecf20Sopenharmony_ci} 50178c2ecf20Sopenharmony_ci 50188c2ecf20Sopenharmony_cistatic int 50198c2ecf20Sopenharmony_cismb2_next_header(char *buf) 50208c2ecf20Sopenharmony_ci{ 50218c2ecf20Sopenharmony_ci struct smb2_sync_hdr *hdr = (struct smb2_sync_hdr *)buf; 50228c2ecf20Sopenharmony_ci struct smb2_transform_hdr *t_hdr = (struct smb2_transform_hdr *)buf; 50238c2ecf20Sopenharmony_ci 50248c2ecf20Sopenharmony_ci if (hdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) 50258c2ecf20Sopenharmony_ci return sizeof(struct smb2_transform_hdr) + 50268c2ecf20Sopenharmony_ci le32_to_cpu(t_hdr->OriginalMessageSize); 50278c2ecf20Sopenharmony_ci 50288c2ecf20Sopenharmony_ci return le32_to_cpu(hdr->NextCommand); 50298c2ecf20Sopenharmony_ci} 50308c2ecf20Sopenharmony_ci 50318c2ecf20Sopenharmony_cistatic int 50328c2ecf20Sopenharmony_cismb2_make_node(unsigned int xid, struct inode *inode, 50338c2ecf20Sopenharmony_ci struct dentry *dentry, struct cifs_tcon *tcon, 50348c2ecf20Sopenharmony_ci char *full_path, umode_t mode, dev_t dev) 50358c2ecf20Sopenharmony_ci{ 50368c2ecf20Sopenharmony_ci struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); 50378c2ecf20Sopenharmony_ci int rc = -EPERM; 50388c2ecf20Sopenharmony_ci FILE_ALL_INFO *buf = NULL; 50398c2ecf20Sopenharmony_ci struct cifs_io_parms io_parms = {0}; 50408c2ecf20Sopenharmony_ci __u32 oplock = 0; 50418c2ecf20Sopenharmony_ci struct cifs_fid fid; 50428c2ecf20Sopenharmony_ci struct cifs_open_parms oparms; 50438c2ecf20Sopenharmony_ci unsigned int bytes_written; 50448c2ecf20Sopenharmony_ci struct win_dev *pdev; 50458c2ecf20Sopenharmony_ci struct kvec iov[2]; 50468c2ecf20Sopenharmony_ci 50478c2ecf20Sopenharmony_ci /* 50488c2ecf20Sopenharmony_ci * Check if mounted with mount parm 'sfu' mount parm. 50498c2ecf20Sopenharmony_ci * SFU emulation should work with all servers, but only 50508c2ecf20Sopenharmony_ci * supports block and char device (no socket & fifo), 50518c2ecf20Sopenharmony_ci * and was used by default in earlier versions of Windows 50528c2ecf20Sopenharmony_ci */ 50538c2ecf20Sopenharmony_ci if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)) 50548c2ecf20Sopenharmony_ci goto out; 50558c2ecf20Sopenharmony_ci 50568c2ecf20Sopenharmony_ci /* 50578c2ecf20Sopenharmony_ci * TODO: Add ability to create instead via reparse point. Windows (e.g. 50588c2ecf20Sopenharmony_ci * their current NFS server) uses this approach to expose special files 50598c2ecf20Sopenharmony_ci * over SMB2/SMB3 and Samba will do this with SMB3.1.1 POSIX Extensions 50608c2ecf20Sopenharmony_ci */ 50618c2ecf20Sopenharmony_ci 50628c2ecf20Sopenharmony_ci if (!S_ISCHR(mode) && !S_ISBLK(mode)) 50638c2ecf20Sopenharmony_ci goto out; 50648c2ecf20Sopenharmony_ci 50658c2ecf20Sopenharmony_ci cifs_dbg(FYI, "sfu compat create special file\n"); 50668c2ecf20Sopenharmony_ci 50678c2ecf20Sopenharmony_ci buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL); 50688c2ecf20Sopenharmony_ci if (buf == NULL) { 50698c2ecf20Sopenharmony_ci rc = -ENOMEM; 50708c2ecf20Sopenharmony_ci goto out; 50718c2ecf20Sopenharmony_ci } 50728c2ecf20Sopenharmony_ci 50738c2ecf20Sopenharmony_ci oparms.tcon = tcon; 50748c2ecf20Sopenharmony_ci oparms.cifs_sb = cifs_sb; 50758c2ecf20Sopenharmony_ci oparms.desired_access = GENERIC_WRITE; 50768c2ecf20Sopenharmony_ci oparms.create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR | 50778c2ecf20Sopenharmony_ci CREATE_OPTION_SPECIAL); 50788c2ecf20Sopenharmony_ci oparms.disposition = FILE_CREATE; 50798c2ecf20Sopenharmony_ci oparms.path = full_path; 50808c2ecf20Sopenharmony_ci oparms.fid = &fid; 50818c2ecf20Sopenharmony_ci oparms.reconnect = false; 50828c2ecf20Sopenharmony_ci 50838c2ecf20Sopenharmony_ci if (tcon->ses->server->oplocks) 50848c2ecf20Sopenharmony_ci oplock = REQ_OPLOCK; 50858c2ecf20Sopenharmony_ci else 50868c2ecf20Sopenharmony_ci oplock = 0; 50878c2ecf20Sopenharmony_ci rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, buf); 50888c2ecf20Sopenharmony_ci if (rc) 50898c2ecf20Sopenharmony_ci goto out; 50908c2ecf20Sopenharmony_ci 50918c2ecf20Sopenharmony_ci /* 50928c2ecf20Sopenharmony_ci * BB Do not bother to decode buf since no local inode yet to put 50938c2ecf20Sopenharmony_ci * timestamps in, but we can reuse it safely. 50948c2ecf20Sopenharmony_ci */ 50958c2ecf20Sopenharmony_ci 50968c2ecf20Sopenharmony_ci pdev = (struct win_dev *)buf; 50978c2ecf20Sopenharmony_ci io_parms.pid = current->tgid; 50988c2ecf20Sopenharmony_ci io_parms.tcon = tcon; 50998c2ecf20Sopenharmony_ci io_parms.offset = 0; 51008c2ecf20Sopenharmony_ci io_parms.length = sizeof(struct win_dev); 51018c2ecf20Sopenharmony_ci iov[1].iov_base = buf; 51028c2ecf20Sopenharmony_ci iov[1].iov_len = sizeof(struct win_dev); 51038c2ecf20Sopenharmony_ci if (S_ISCHR(mode)) { 51048c2ecf20Sopenharmony_ci memcpy(pdev->type, "IntxCHR", 8); 51058c2ecf20Sopenharmony_ci pdev->major = cpu_to_le64(MAJOR(dev)); 51068c2ecf20Sopenharmony_ci pdev->minor = cpu_to_le64(MINOR(dev)); 51078c2ecf20Sopenharmony_ci rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms, 51088c2ecf20Sopenharmony_ci &bytes_written, iov, 1); 51098c2ecf20Sopenharmony_ci } else if (S_ISBLK(mode)) { 51108c2ecf20Sopenharmony_ci memcpy(pdev->type, "IntxBLK", 8); 51118c2ecf20Sopenharmony_ci pdev->major = cpu_to_le64(MAJOR(dev)); 51128c2ecf20Sopenharmony_ci pdev->minor = cpu_to_le64(MINOR(dev)); 51138c2ecf20Sopenharmony_ci rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms, 51148c2ecf20Sopenharmony_ci &bytes_written, iov, 1); 51158c2ecf20Sopenharmony_ci } 51168c2ecf20Sopenharmony_ci tcon->ses->server->ops->close(xid, tcon, &fid); 51178c2ecf20Sopenharmony_ci d_drop(dentry); 51188c2ecf20Sopenharmony_ci 51198c2ecf20Sopenharmony_ci /* FIXME: add code here to set EAs */ 51208c2ecf20Sopenharmony_ciout: 51218c2ecf20Sopenharmony_ci kfree(buf); 51228c2ecf20Sopenharmony_ci return rc; 51238c2ecf20Sopenharmony_ci} 51248c2ecf20Sopenharmony_ci 51258c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY 51268c2ecf20Sopenharmony_cistruct smb_version_operations smb20_operations = { 51278c2ecf20Sopenharmony_ci .compare_fids = smb2_compare_fids, 51288c2ecf20Sopenharmony_ci .setup_request = smb2_setup_request, 51298c2ecf20Sopenharmony_ci .setup_async_request = smb2_setup_async_request, 51308c2ecf20Sopenharmony_ci .check_receive = smb2_check_receive, 51318c2ecf20Sopenharmony_ci .add_credits = smb2_add_credits, 51328c2ecf20Sopenharmony_ci .set_credits = smb2_set_credits, 51338c2ecf20Sopenharmony_ci .get_credits_field = smb2_get_credits_field, 51348c2ecf20Sopenharmony_ci .get_credits = smb2_get_credits, 51358c2ecf20Sopenharmony_ci .wait_mtu_credits = cifs_wait_mtu_credits, 51368c2ecf20Sopenharmony_ci .get_next_mid = smb2_get_next_mid, 51378c2ecf20Sopenharmony_ci .revert_current_mid = smb2_revert_current_mid, 51388c2ecf20Sopenharmony_ci .read_data_offset = smb2_read_data_offset, 51398c2ecf20Sopenharmony_ci .read_data_length = smb2_read_data_length, 51408c2ecf20Sopenharmony_ci .map_error = map_smb2_to_linux_error, 51418c2ecf20Sopenharmony_ci .find_mid = smb2_find_mid, 51428c2ecf20Sopenharmony_ci .check_message = smb2_check_message, 51438c2ecf20Sopenharmony_ci .dump_detail = smb2_dump_detail, 51448c2ecf20Sopenharmony_ci .clear_stats = smb2_clear_stats, 51458c2ecf20Sopenharmony_ci .print_stats = smb2_print_stats, 51468c2ecf20Sopenharmony_ci .is_oplock_break = smb2_is_valid_oplock_break, 51478c2ecf20Sopenharmony_ci .handle_cancelled_mid = smb2_handle_cancelled_mid, 51488c2ecf20Sopenharmony_ci .downgrade_oplock = smb2_downgrade_oplock, 51498c2ecf20Sopenharmony_ci .need_neg = smb2_need_neg, 51508c2ecf20Sopenharmony_ci .negotiate = smb2_negotiate, 51518c2ecf20Sopenharmony_ci .negotiate_wsize = smb2_negotiate_wsize, 51528c2ecf20Sopenharmony_ci .negotiate_rsize = smb2_negotiate_rsize, 51538c2ecf20Sopenharmony_ci .sess_setup = SMB2_sess_setup, 51548c2ecf20Sopenharmony_ci .logoff = SMB2_logoff, 51558c2ecf20Sopenharmony_ci .tree_connect = SMB2_tcon, 51568c2ecf20Sopenharmony_ci .tree_disconnect = SMB2_tdis, 51578c2ecf20Sopenharmony_ci .qfs_tcon = smb2_qfs_tcon, 51588c2ecf20Sopenharmony_ci .is_path_accessible = smb2_is_path_accessible, 51598c2ecf20Sopenharmony_ci .can_echo = smb2_can_echo, 51608c2ecf20Sopenharmony_ci .echo = SMB2_echo, 51618c2ecf20Sopenharmony_ci .query_path_info = smb2_query_path_info, 51628c2ecf20Sopenharmony_ci .get_srv_inum = smb2_get_srv_inum, 51638c2ecf20Sopenharmony_ci .query_file_info = smb2_query_file_info, 51648c2ecf20Sopenharmony_ci .set_path_size = smb2_set_path_size, 51658c2ecf20Sopenharmony_ci .set_file_size = smb2_set_file_size, 51668c2ecf20Sopenharmony_ci .set_file_info = smb2_set_file_info, 51678c2ecf20Sopenharmony_ci .set_compression = smb2_set_compression, 51688c2ecf20Sopenharmony_ci .mkdir = smb2_mkdir, 51698c2ecf20Sopenharmony_ci .mkdir_setinfo = smb2_mkdir_setinfo, 51708c2ecf20Sopenharmony_ci .rmdir = smb2_rmdir, 51718c2ecf20Sopenharmony_ci .unlink = smb2_unlink, 51728c2ecf20Sopenharmony_ci .rename = smb2_rename_path, 51738c2ecf20Sopenharmony_ci .create_hardlink = smb2_create_hardlink, 51748c2ecf20Sopenharmony_ci .query_symlink = smb2_query_symlink, 51758c2ecf20Sopenharmony_ci .query_mf_symlink = smb3_query_mf_symlink, 51768c2ecf20Sopenharmony_ci .create_mf_symlink = smb3_create_mf_symlink, 51778c2ecf20Sopenharmony_ci .open = smb2_open_file, 51788c2ecf20Sopenharmony_ci .set_fid = smb2_set_fid, 51798c2ecf20Sopenharmony_ci .close = smb2_close_file, 51808c2ecf20Sopenharmony_ci .flush = smb2_flush_file, 51818c2ecf20Sopenharmony_ci .async_readv = smb2_async_readv, 51828c2ecf20Sopenharmony_ci .async_writev = smb2_async_writev, 51838c2ecf20Sopenharmony_ci .sync_read = smb2_sync_read, 51848c2ecf20Sopenharmony_ci .sync_write = smb2_sync_write, 51858c2ecf20Sopenharmony_ci .query_dir_first = smb2_query_dir_first, 51868c2ecf20Sopenharmony_ci .query_dir_next = smb2_query_dir_next, 51878c2ecf20Sopenharmony_ci .close_dir = smb2_close_dir, 51888c2ecf20Sopenharmony_ci .calc_smb_size = smb2_calc_size, 51898c2ecf20Sopenharmony_ci .is_status_pending = smb2_is_status_pending, 51908c2ecf20Sopenharmony_ci .is_session_expired = smb2_is_session_expired, 51918c2ecf20Sopenharmony_ci .oplock_response = smb2_oplock_response, 51928c2ecf20Sopenharmony_ci .queryfs = smb2_queryfs, 51938c2ecf20Sopenharmony_ci .mand_lock = smb2_mand_lock, 51948c2ecf20Sopenharmony_ci .mand_unlock_range = smb2_unlock_range, 51958c2ecf20Sopenharmony_ci .push_mand_locks = smb2_push_mandatory_locks, 51968c2ecf20Sopenharmony_ci .get_lease_key = smb2_get_lease_key, 51978c2ecf20Sopenharmony_ci .set_lease_key = smb2_set_lease_key, 51988c2ecf20Sopenharmony_ci .new_lease_key = smb2_new_lease_key, 51998c2ecf20Sopenharmony_ci .calc_signature = smb2_calc_signature, 52008c2ecf20Sopenharmony_ci .is_read_op = smb2_is_read_op, 52018c2ecf20Sopenharmony_ci .set_oplock_level = smb2_set_oplock_level, 52028c2ecf20Sopenharmony_ci .create_lease_buf = smb2_create_lease_buf, 52038c2ecf20Sopenharmony_ci .parse_lease_buf = smb2_parse_lease_buf, 52048c2ecf20Sopenharmony_ci .copychunk_range = smb2_copychunk_range, 52058c2ecf20Sopenharmony_ci .wp_retry_size = smb2_wp_retry_size, 52068c2ecf20Sopenharmony_ci .dir_needs_close = smb2_dir_needs_close, 52078c2ecf20Sopenharmony_ci .get_dfs_refer = smb2_get_dfs_refer, 52088c2ecf20Sopenharmony_ci .select_sectype = smb2_select_sectype, 52098c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_XATTR 52108c2ecf20Sopenharmony_ci .query_all_EAs = smb2_query_eas, 52118c2ecf20Sopenharmony_ci .set_EA = smb2_set_ea, 52128c2ecf20Sopenharmony_ci#endif /* CIFS_XATTR */ 52138c2ecf20Sopenharmony_ci .get_acl = get_smb2_acl, 52148c2ecf20Sopenharmony_ci .get_acl_by_fid = get_smb2_acl_by_fid, 52158c2ecf20Sopenharmony_ci .set_acl = set_smb2_acl, 52168c2ecf20Sopenharmony_ci .next_header = smb2_next_header, 52178c2ecf20Sopenharmony_ci .ioctl_query_info = smb2_ioctl_query_info, 52188c2ecf20Sopenharmony_ci .make_node = smb2_make_node, 52198c2ecf20Sopenharmony_ci .fiemap = smb3_fiemap, 52208c2ecf20Sopenharmony_ci .llseek = smb3_llseek, 52218c2ecf20Sopenharmony_ci .is_status_io_timeout = smb2_is_status_io_timeout, 52228c2ecf20Sopenharmony_ci}; 52238c2ecf20Sopenharmony_ci#endif /* CIFS_ALLOW_INSECURE_LEGACY */ 52248c2ecf20Sopenharmony_ci 52258c2ecf20Sopenharmony_cistruct smb_version_operations smb21_operations = { 52268c2ecf20Sopenharmony_ci .compare_fids = smb2_compare_fids, 52278c2ecf20Sopenharmony_ci .setup_request = smb2_setup_request, 52288c2ecf20Sopenharmony_ci .setup_async_request = smb2_setup_async_request, 52298c2ecf20Sopenharmony_ci .check_receive = smb2_check_receive, 52308c2ecf20Sopenharmony_ci .add_credits = smb2_add_credits, 52318c2ecf20Sopenharmony_ci .set_credits = smb2_set_credits, 52328c2ecf20Sopenharmony_ci .get_credits_field = smb2_get_credits_field, 52338c2ecf20Sopenharmony_ci .get_credits = smb2_get_credits, 52348c2ecf20Sopenharmony_ci .wait_mtu_credits = smb2_wait_mtu_credits, 52358c2ecf20Sopenharmony_ci .adjust_credits = smb2_adjust_credits, 52368c2ecf20Sopenharmony_ci .get_next_mid = smb2_get_next_mid, 52378c2ecf20Sopenharmony_ci .revert_current_mid = smb2_revert_current_mid, 52388c2ecf20Sopenharmony_ci .read_data_offset = smb2_read_data_offset, 52398c2ecf20Sopenharmony_ci .read_data_length = smb2_read_data_length, 52408c2ecf20Sopenharmony_ci .map_error = map_smb2_to_linux_error, 52418c2ecf20Sopenharmony_ci .find_mid = smb2_find_mid, 52428c2ecf20Sopenharmony_ci .check_message = smb2_check_message, 52438c2ecf20Sopenharmony_ci .dump_detail = smb2_dump_detail, 52448c2ecf20Sopenharmony_ci .clear_stats = smb2_clear_stats, 52458c2ecf20Sopenharmony_ci .print_stats = smb2_print_stats, 52468c2ecf20Sopenharmony_ci .is_oplock_break = smb2_is_valid_oplock_break, 52478c2ecf20Sopenharmony_ci .handle_cancelled_mid = smb2_handle_cancelled_mid, 52488c2ecf20Sopenharmony_ci .downgrade_oplock = smb2_downgrade_oplock, 52498c2ecf20Sopenharmony_ci .need_neg = smb2_need_neg, 52508c2ecf20Sopenharmony_ci .negotiate = smb2_negotiate, 52518c2ecf20Sopenharmony_ci .negotiate_wsize = smb2_negotiate_wsize, 52528c2ecf20Sopenharmony_ci .negotiate_rsize = smb2_negotiate_rsize, 52538c2ecf20Sopenharmony_ci .sess_setup = SMB2_sess_setup, 52548c2ecf20Sopenharmony_ci .logoff = SMB2_logoff, 52558c2ecf20Sopenharmony_ci .tree_connect = SMB2_tcon, 52568c2ecf20Sopenharmony_ci .tree_disconnect = SMB2_tdis, 52578c2ecf20Sopenharmony_ci .qfs_tcon = smb2_qfs_tcon, 52588c2ecf20Sopenharmony_ci .is_path_accessible = smb2_is_path_accessible, 52598c2ecf20Sopenharmony_ci .can_echo = smb2_can_echo, 52608c2ecf20Sopenharmony_ci .echo = SMB2_echo, 52618c2ecf20Sopenharmony_ci .query_path_info = smb2_query_path_info, 52628c2ecf20Sopenharmony_ci .get_srv_inum = smb2_get_srv_inum, 52638c2ecf20Sopenharmony_ci .query_file_info = smb2_query_file_info, 52648c2ecf20Sopenharmony_ci .set_path_size = smb2_set_path_size, 52658c2ecf20Sopenharmony_ci .set_file_size = smb2_set_file_size, 52668c2ecf20Sopenharmony_ci .set_file_info = smb2_set_file_info, 52678c2ecf20Sopenharmony_ci .set_compression = smb2_set_compression, 52688c2ecf20Sopenharmony_ci .mkdir = smb2_mkdir, 52698c2ecf20Sopenharmony_ci .mkdir_setinfo = smb2_mkdir_setinfo, 52708c2ecf20Sopenharmony_ci .rmdir = smb2_rmdir, 52718c2ecf20Sopenharmony_ci .unlink = smb2_unlink, 52728c2ecf20Sopenharmony_ci .rename = smb2_rename_path, 52738c2ecf20Sopenharmony_ci .create_hardlink = smb2_create_hardlink, 52748c2ecf20Sopenharmony_ci .query_symlink = smb2_query_symlink, 52758c2ecf20Sopenharmony_ci .query_mf_symlink = smb3_query_mf_symlink, 52768c2ecf20Sopenharmony_ci .create_mf_symlink = smb3_create_mf_symlink, 52778c2ecf20Sopenharmony_ci .open = smb2_open_file, 52788c2ecf20Sopenharmony_ci .set_fid = smb2_set_fid, 52798c2ecf20Sopenharmony_ci .close = smb2_close_file, 52808c2ecf20Sopenharmony_ci .flush = smb2_flush_file, 52818c2ecf20Sopenharmony_ci .async_readv = smb2_async_readv, 52828c2ecf20Sopenharmony_ci .async_writev = smb2_async_writev, 52838c2ecf20Sopenharmony_ci .sync_read = smb2_sync_read, 52848c2ecf20Sopenharmony_ci .sync_write = smb2_sync_write, 52858c2ecf20Sopenharmony_ci .query_dir_first = smb2_query_dir_first, 52868c2ecf20Sopenharmony_ci .query_dir_next = smb2_query_dir_next, 52878c2ecf20Sopenharmony_ci .close_dir = smb2_close_dir, 52888c2ecf20Sopenharmony_ci .calc_smb_size = smb2_calc_size, 52898c2ecf20Sopenharmony_ci .is_status_pending = smb2_is_status_pending, 52908c2ecf20Sopenharmony_ci .is_session_expired = smb2_is_session_expired, 52918c2ecf20Sopenharmony_ci .oplock_response = smb2_oplock_response, 52928c2ecf20Sopenharmony_ci .queryfs = smb2_queryfs, 52938c2ecf20Sopenharmony_ci .mand_lock = smb2_mand_lock, 52948c2ecf20Sopenharmony_ci .mand_unlock_range = smb2_unlock_range, 52958c2ecf20Sopenharmony_ci .push_mand_locks = smb2_push_mandatory_locks, 52968c2ecf20Sopenharmony_ci .get_lease_key = smb2_get_lease_key, 52978c2ecf20Sopenharmony_ci .set_lease_key = smb2_set_lease_key, 52988c2ecf20Sopenharmony_ci .new_lease_key = smb2_new_lease_key, 52998c2ecf20Sopenharmony_ci .calc_signature = smb2_calc_signature, 53008c2ecf20Sopenharmony_ci .is_read_op = smb21_is_read_op, 53018c2ecf20Sopenharmony_ci .set_oplock_level = smb21_set_oplock_level, 53028c2ecf20Sopenharmony_ci .create_lease_buf = smb2_create_lease_buf, 53038c2ecf20Sopenharmony_ci .parse_lease_buf = smb2_parse_lease_buf, 53048c2ecf20Sopenharmony_ci .copychunk_range = smb2_copychunk_range, 53058c2ecf20Sopenharmony_ci .wp_retry_size = smb2_wp_retry_size, 53068c2ecf20Sopenharmony_ci .dir_needs_close = smb2_dir_needs_close, 53078c2ecf20Sopenharmony_ci .enum_snapshots = smb3_enum_snapshots, 53088c2ecf20Sopenharmony_ci .notify = smb3_notify, 53098c2ecf20Sopenharmony_ci .get_dfs_refer = smb2_get_dfs_refer, 53108c2ecf20Sopenharmony_ci .select_sectype = smb2_select_sectype, 53118c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_XATTR 53128c2ecf20Sopenharmony_ci .query_all_EAs = smb2_query_eas, 53138c2ecf20Sopenharmony_ci .set_EA = smb2_set_ea, 53148c2ecf20Sopenharmony_ci#endif /* CIFS_XATTR */ 53158c2ecf20Sopenharmony_ci .get_acl = get_smb2_acl, 53168c2ecf20Sopenharmony_ci .get_acl_by_fid = get_smb2_acl_by_fid, 53178c2ecf20Sopenharmony_ci .set_acl = set_smb2_acl, 53188c2ecf20Sopenharmony_ci .next_header = smb2_next_header, 53198c2ecf20Sopenharmony_ci .ioctl_query_info = smb2_ioctl_query_info, 53208c2ecf20Sopenharmony_ci .make_node = smb2_make_node, 53218c2ecf20Sopenharmony_ci .fiemap = smb3_fiemap, 53228c2ecf20Sopenharmony_ci .llseek = smb3_llseek, 53238c2ecf20Sopenharmony_ci .is_status_io_timeout = smb2_is_status_io_timeout, 53248c2ecf20Sopenharmony_ci}; 53258c2ecf20Sopenharmony_ci 53268c2ecf20Sopenharmony_cistruct smb_version_operations smb30_operations = { 53278c2ecf20Sopenharmony_ci .compare_fids = smb2_compare_fids, 53288c2ecf20Sopenharmony_ci .setup_request = smb2_setup_request, 53298c2ecf20Sopenharmony_ci .setup_async_request = smb2_setup_async_request, 53308c2ecf20Sopenharmony_ci .check_receive = smb2_check_receive, 53318c2ecf20Sopenharmony_ci .add_credits = smb2_add_credits, 53328c2ecf20Sopenharmony_ci .set_credits = smb2_set_credits, 53338c2ecf20Sopenharmony_ci .get_credits_field = smb2_get_credits_field, 53348c2ecf20Sopenharmony_ci .get_credits = smb2_get_credits, 53358c2ecf20Sopenharmony_ci .wait_mtu_credits = smb2_wait_mtu_credits, 53368c2ecf20Sopenharmony_ci .adjust_credits = smb2_adjust_credits, 53378c2ecf20Sopenharmony_ci .get_next_mid = smb2_get_next_mid, 53388c2ecf20Sopenharmony_ci .revert_current_mid = smb2_revert_current_mid, 53398c2ecf20Sopenharmony_ci .read_data_offset = smb2_read_data_offset, 53408c2ecf20Sopenharmony_ci .read_data_length = smb2_read_data_length, 53418c2ecf20Sopenharmony_ci .map_error = map_smb2_to_linux_error, 53428c2ecf20Sopenharmony_ci .find_mid = smb2_find_mid, 53438c2ecf20Sopenharmony_ci .check_message = smb2_check_message, 53448c2ecf20Sopenharmony_ci .dump_detail = smb2_dump_detail, 53458c2ecf20Sopenharmony_ci .clear_stats = smb2_clear_stats, 53468c2ecf20Sopenharmony_ci .print_stats = smb2_print_stats, 53478c2ecf20Sopenharmony_ci .dump_share_caps = smb2_dump_share_caps, 53488c2ecf20Sopenharmony_ci .is_oplock_break = smb2_is_valid_oplock_break, 53498c2ecf20Sopenharmony_ci .handle_cancelled_mid = smb2_handle_cancelled_mid, 53508c2ecf20Sopenharmony_ci .downgrade_oplock = smb3_downgrade_oplock, 53518c2ecf20Sopenharmony_ci .need_neg = smb2_need_neg, 53528c2ecf20Sopenharmony_ci .negotiate = smb2_negotiate, 53538c2ecf20Sopenharmony_ci .negotiate_wsize = smb3_negotiate_wsize, 53548c2ecf20Sopenharmony_ci .negotiate_rsize = smb3_negotiate_rsize, 53558c2ecf20Sopenharmony_ci .sess_setup = SMB2_sess_setup, 53568c2ecf20Sopenharmony_ci .logoff = SMB2_logoff, 53578c2ecf20Sopenharmony_ci .tree_connect = SMB2_tcon, 53588c2ecf20Sopenharmony_ci .tree_disconnect = SMB2_tdis, 53598c2ecf20Sopenharmony_ci .qfs_tcon = smb3_qfs_tcon, 53608c2ecf20Sopenharmony_ci .is_path_accessible = smb2_is_path_accessible, 53618c2ecf20Sopenharmony_ci .can_echo = smb2_can_echo, 53628c2ecf20Sopenharmony_ci .echo = SMB2_echo, 53638c2ecf20Sopenharmony_ci .query_path_info = smb2_query_path_info, 53648c2ecf20Sopenharmony_ci /* WSL tags introduced long after smb2.1, enable for SMB3, 3.11 only */ 53658c2ecf20Sopenharmony_ci .query_reparse_tag = smb2_query_reparse_tag, 53668c2ecf20Sopenharmony_ci .get_srv_inum = smb2_get_srv_inum, 53678c2ecf20Sopenharmony_ci .query_file_info = smb2_query_file_info, 53688c2ecf20Sopenharmony_ci .set_path_size = smb2_set_path_size, 53698c2ecf20Sopenharmony_ci .set_file_size = smb2_set_file_size, 53708c2ecf20Sopenharmony_ci .set_file_info = smb2_set_file_info, 53718c2ecf20Sopenharmony_ci .set_compression = smb2_set_compression, 53728c2ecf20Sopenharmony_ci .mkdir = smb2_mkdir, 53738c2ecf20Sopenharmony_ci .mkdir_setinfo = smb2_mkdir_setinfo, 53748c2ecf20Sopenharmony_ci .rmdir = smb2_rmdir, 53758c2ecf20Sopenharmony_ci .unlink = smb2_unlink, 53768c2ecf20Sopenharmony_ci .rename = smb2_rename_path, 53778c2ecf20Sopenharmony_ci .create_hardlink = smb2_create_hardlink, 53788c2ecf20Sopenharmony_ci .query_symlink = smb2_query_symlink, 53798c2ecf20Sopenharmony_ci .query_mf_symlink = smb3_query_mf_symlink, 53808c2ecf20Sopenharmony_ci .create_mf_symlink = smb3_create_mf_symlink, 53818c2ecf20Sopenharmony_ci .open = smb2_open_file, 53828c2ecf20Sopenharmony_ci .set_fid = smb2_set_fid, 53838c2ecf20Sopenharmony_ci .close = smb2_close_file, 53848c2ecf20Sopenharmony_ci .close_getattr = smb2_close_getattr, 53858c2ecf20Sopenharmony_ci .flush = smb2_flush_file, 53868c2ecf20Sopenharmony_ci .async_readv = smb2_async_readv, 53878c2ecf20Sopenharmony_ci .async_writev = smb2_async_writev, 53888c2ecf20Sopenharmony_ci .sync_read = smb2_sync_read, 53898c2ecf20Sopenharmony_ci .sync_write = smb2_sync_write, 53908c2ecf20Sopenharmony_ci .query_dir_first = smb2_query_dir_first, 53918c2ecf20Sopenharmony_ci .query_dir_next = smb2_query_dir_next, 53928c2ecf20Sopenharmony_ci .close_dir = smb2_close_dir, 53938c2ecf20Sopenharmony_ci .calc_smb_size = smb2_calc_size, 53948c2ecf20Sopenharmony_ci .is_status_pending = smb2_is_status_pending, 53958c2ecf20Sopenharmony_ci .is_session_expired = smb2_is_session_expired, 53968c2ecf20Sopenharmony_ci .oplock_response = smb2_oplock_response, 53978c2ecf20Sopenharmony_ci .queryfs = smb2_queryfs, 53988c2ecf20Sopenharmony_ci .mand_lock = smb2_mand_lock, 53998c2ecf20Sopenharmony_ci .mand_unlock_range = smb2_unlock_range, 54008c2ecf20Sopenharmony_ci .push_mand_locks = smb2_push_mandatory_locks, 54018c2ecf20Sopenharmony_ci .get_lease_key = smb2_get_lease_key, 54028c2ecf20Sopenharmony_ci .set_lease_key = smb2_set_lease_key, 54038c2ecf20Sopenharmony_ci .new_lease_key = smb2_new_lease_key, 54048c2ecf20Sopenharmony_ci .generate_signingkey = generate_smb30signingkey, 54058c2ecf20Sopenharmony_ci .calc_signature = smb3_calc_signature, 54068c2ecf20Sopenharmony_ci .set_integrity = smb3_set_integrity, 54078c2ecf20Sopenharmony_ci .is_read_op = smb21_is_read_op, 54088c2ecf20Sopenharmony_ci .set_oplock_level = smb3_set_oplock_level, 54098c2ecf20Sopenharmony_ci .create_lease_buf = smb3_create_lease_buf, 54108c2ecf20Sopenharmony_ci .parse_lease_buf = smb3_parse_lease_buf, 54118c2ecf20Sopenharmony_ci .copychunk_range = smb2_copychunk_range, 54128c2ecf20Sopenharmony_ci .duplicate_extents = smb2_duplicate_extents, 54138c2ecf20Sopenharmony_ci .validate_negotiate = smb3_validate_negotiate, 54148c2ecf20Sopenharmony_ci .wp_retry_size = smb2_wp_retry_size, 54158c2ecf20Sopenharmony_ci .dir_needs_close = smb2_dir_needs_close, 54168c2ecf20Sopenharmony_ci .fallocate = smb3_fallocate, 54178c2ecf20Sopenharmony_ci .enum_snapshots = smb3_enum_snapshots, 54188c2ecf20Sopenharmony_ci .notify = smb3_notify, 54198c2ecf20Sopenharmony_ci .init_transform_rq = smb3_init_transform_rq, 54208c2ecf20Sopenharmony_ci .is_transform_hdr = smb3_is_transform_hdr, 54218c2ecf20Sopenharmony_ci .receive_transform = smb3_receive_transform, 54228c2ecf20Sopenharmony_ci .get_dfs_refer = smb2_get_dfs_refer, 54238c2ecf20Sopenharmony_ci .select_sectype = smb2_select_sectype, 54248c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_XATTR 54258c2ecf20Sopenharmony_ci .query_all_EAs = smb2_query_eas, 54268c2ecf20Sopenharmony_ci .set_EA = smb2_set_ea, 54278c2ecf20Sopenharmony_ci#endif /* CIFS_XATTR */ 54288c2ecf20Sopenharmony_ci .get_acl = get_smb2_acl, 54298c2ecf20Sopenharmony_ci .get_acl_by_fid = get_smb2_acl_by_fid, 54308c2ecf20Sopenharmony_ci .set_acl = set_smb2_acl, 54318c2ecf20Sopenharmony_ci .next_header = smb2_next_header, 54328c2ecf20Sopenharmony_ci .ioctl_query_info = smb2_ioctl_query_info, 54338c2ecf20Sopenharmony_ci .make_node = smb2_make_node, 54348c2ecf20Sopenharmony_ci .fiemap = smb3_fiemap, 54358c2ecf20Sopenharmony_ci .llseek = smb3_llseek, 54368c2ecf20Sopenharmony_ci .is_status_io_timeout = smb2_is_status_io_timeout, 54378c2ecf20Sopenharmony_ci}; 54388c2ecf20Sopenharmony_ci 54398c2ecf20Sopenharmony_cistruct smb_version_operations smb311_operations = { 54408c2ecf20Sopenharmony_ci .compare_fids = smb2_compare_fids, 54418c2ecf20Sopenharmony_ci .setup_request = smb2_setup_request, 54428c2ecf20Sopenharmony_ci .setup_async_request = smb2_setup_async_request, 54438c2ecf20Sopenharmony_ci .check_receive = smb2_check_receive, 54448c2ecf20Sopenharmony_ci .add_credits = smb2_add_credits, 54458c2ecf20Sopenharmony_ci .set_credits = smb2_set_credits, 54468c2ecf20Sopenharmony_ci .get_credits_field = smb2_get_credits_field, 54478c2ecf20Sopenharmony_ci .get_credits = smb2_get_credits, 54488c2ecf20Sopenharmony_ci .wait_mtu_credits = smb2_wait_mtu_credits, 54498c2ecf20Sopenharmony_ci .adjust_credits = smb2_adjust_credits, 54508c2ecf20Sopenharmony_ci .get_next_mid = smb2_get_next_mid, 54518c2ecf20Sopenharmony_ci .revert_current_mid = smb2_revert_current_mid, 54528c2ecf20Sopenharmony_ci .read_data_offset = smb2_read_data_offset, 54538c2ecf20Sopenharmony_ci .read_data_length = smb2_read_data_length, 54548c2ecf20Sopenharmony_ci .map_error = map_smb2_to_linux_error, 54558c2ecf20Sopenharmony_ci .find_mid = smb2_find_mid, 54568c2ecf20Sopenharmony_ci .check_message = smb2_check_message, 54578c2ecf20Sopenharmony_ci .dump_detail = smb2_dump_detail, 54588c2ecf20Sopenharmony_ci .clear_stats = smb2_clear_stats, 54598c2ecf20Sopenharmony_ci .print_stats = smb2_print_stats, 54608c2ecf20Sopenharmony_ci .dump_share_caps = smb2_dump_share_caps, 54618c2ecf20Sopenharmony_ci .is_oplock_break = smb2_is_valid_oplock_break, 54628c2ecf20Sopenharmony_ci .handle_cancelled_mid = smb2_handle_cancelled_mid, 54638c2ecf20Sopenharmony_ci .downgrade_oplock = smb3_downgrade_oplock, 54648c2ecf20Sopenharmony_ci .need_neg = smb2_need_neg, 54658c2ecf20Sopenharmony_ci .negotiate = smb2_negotiate, 54668c2ecf20Sopenharmony_ci .negotiate_wsize = smb3_negotiate_wsize, 54678c2ecf20Sopenharmony_ci .negotiate_rsize = smb3_negotiate_rsize, 54688c2ecf20Sopenharmony_ci .sess_setup = SMB2_sess_setup, 54698c2ecf20Sopenharmony_ci .logoff = SMB2_logoff, 54708c2ecf20Sopenharmony_ci .tree_connect = SMB2_tcon, 54718c2ecf20Sopenharmony_ci .tree_disconnect = SMB2_tdis, 54728c2ecf20Sopenharmony_ci .qfs_tcon = smb3_qfs_tcon, 54738c2ecf20Sopenharmony_ci .is_path_accessible = smb2_is_path_accessible, 54748c2ecf20Sopenharmony_ci .can_echo = smb2_can_echo, 54758c2ecf20Sopenharmony_ci .echo = SMB2_echo, 54768c2ecf20Sopenharmony_ci .query_path_info = smb2_query_path_info, 54778c2ecf20Sopenharmony_ci .query_reparse_tag = smb2_query_reparse_tag, 54788c2ecf20Sopenharmony_ci .get_srv_inum = smb2_get_srv_inum, 54798c2ecf20Sopenharmony_ci .query_file_info = smb2_query_file_info, 54808c2ecf20Sopenharmony_ci .set_path_size = smb2_set_path_size, 54818c2ecf20Sopenharmony_ci .set_file_size = smb2_set_file_size, 54828c2ecf20Sopenharmony_ci .set_file_info = smb2_set_file_info, 54838c2ecf20Sopenharmony_ci .set_compression = smb2_set_compression, 54848c2ecf20Sopenharmony_ci .mkdir = smb2_mkdir, 54858c2ecf20Sopenharmony_ci .mkdir_setinfo = smb2_mkdir_setinfo, 54868c2ecf20Sopenharmony_ci .posix_mkdir = smb311_posix_mkdir, 54878c2ecf20Sopenharmony_ci .rmdir = smb2_rmdir, 54888c2ecf20Sopenharmony_ci .unlink = smb2_unlink, 54898c2ecf20Sopenharmony_ci .rename = smb2_rename_path, 54908c2ecf20Sopenharmony_ci .create_hardlink = smb2_create_hardlink, 54918c2ecf20Sopenharmony_ci .query_symlink = smb2_query_symlink, 54928c2ecf20Sopenharmony_ci .query_mf_symlink = smb3_query_mf_symlink, 54938c2ecf20Sopenharmony_ci .create_mf_symlink = smb3_create_mf_symlink, 54948c2ecf20Sopenharmony_ci .open = smb2_open_file, 54958c2ecf20Sopenharmony_ci .set_fid = smb2_set_fid, 54968c2ecf20Sopenharmony_ci .close = smb2_close_file, 54978c2ecf20Sopenharmony_ci .close_getattr = smb2_close_getattr, 54988c2ecf20Sopenharmony_ci .flush = smb2_flush_file, 54998c2ecf20Sopenharmony_ci .async_readv = smb2_async_readv, 55008c2ecf20Sopenharmony_ci .async_writev = smb2_async_writev, 55018c2ecf20Sopenharmony_ci .sync_read = smb2_sync_read, 55028c2ecf20Sopenharmony_ci .sync_write = smb2_sync_write, 55038c2ecf20Sopenharmony_ci .query_dir_first = smb2_query_dir_first, 55048c2ecf20Sopenharmony_ci .query_dir_next = smb2_query_dir_next, 55058c2ecf20Sopenharmony_ci .close_dir = smb2_close_dir, 55068c2ecf20Sopenharmony_ci .calc_smb_size = smb2_calc_size, 55078c2ecf20Sopenharmony_ci .is_status_pending = smb2_is_status_pending, 55088c2ecf20Sopenharmony_ci .is_session_expired = smb2_is_session_expired, 55098c2ecf20Sopenharmony_ci .oplock_response = smb2_oplock_response, 55108c2ecf20Sopenharmony_ci .queryfs = smb311_queryfs, 55118c2ecf20Sopenharmony_ci .mand_lock = smb2_mand_lock, 55128c2ecf20Sopenharmony_ci .mand_unlock_range = smb2_unlock_range, 55138c2ecf20Sopenharmony_ci .push_mand_locks = smb2_push_mandatory_locks, 55148c2ecf20Sopenharmony_ci .get_lease_key = smb2_get_lease_key, 55158c2ecf20Sopenharmony_ci .set_lease_key = smb2_set_lease_key, 55168c2ecf20Sopenharmony_ci .new_lease_key = smb2_new_lease_key, 55178c2ecf20Sopenharmony_ci .generate_signingkey = generate_smb311signingkey, 55188c2ecf20Sopenharmony_ci .calc_signature = smb3_calc_signature, 55198c2ecf20Sopenharmony_ci .set_integrity = smb3_set_integrity, 55208c2ecf20Sopenharmony_ci .is_read_op = smb21_is_read_op, 55218c2ecf20Sopenharmony_ci .set_oplock_level = smb3_set_oplock_level, 55228c2ecf20Sopenharmony_ci .create_lease_buf = smb3_create_lease_buf, 55238c2ecf20Sopenharmony_ci .parse_lease_buf = smb3_parse_lease_buf, 55248c2ecf20Sopenharmony_ci .copychunk_range = smb2_copychunk_range, 55258c2ecf20Sopenharmony_ci .duplicate_extents = smb2_duplicate_extents, 55268c2ecf20Sopenharmony_ci/* .validate_negotiate = smb3_validate_negotiate, */ /* not used in 3.11 */ 55278c2ecf20Sopenharmony_ci .wp_retry_size = smb2_wp_retry_size, 55288c2ecf20Sopenharmony_ci .dir_needs_close = smb2_dir_needs_close, 55298c2ecf20Sopenharmony_ci .fallocate = smb3_fallocate, 55308c2ecf20Sopenharmony_ci .enum_snapshots = smb3_enum_snapshots, 55318c2ecf20Sopenharmony_ci .notify = smb3_notify, 55328c2ecf20Sopenharmony_ci .init_transform_rq = smb3_init_transform_rq, 55338c2ecf20Sopenharmony_ci .is_transform_hdr = smb3_is_transform_hdr, 55348c2ecf20Sopenharmony_ci .receive_transform = smb3_receive_transform, 55358c2ecf20Sopenharmony_ci .get_dfs_refer = smb2_get_dfs_refer, 55368c2ecf20Sopenharmony_ci .select_sectype = smb2_select_sectype, 55378c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_XATTR 55388c2ecf20Sopenharmony_ci .query_all_EAs = smb2_query_eas, 55398c2ecf20Sopenharmony_ci .set_EA = smb2_set_ea, 55408c2ecf20Sopenharmony_ci#endif /* CIFS_XATTR */ 55418c2ecf20Sopenharmony_ci .get_acl = get_smb2_acl, 55428c2ecf20Sopenharmony_ci .get_acl_by_fid = get_smb2_acl_by_fid, 55438c2ecf20Sopenharmony_ci .set_acl = set_smb2_acl, 55448c2ecf20Sopenharmony_ci .next_header = smb2_next_header, 55458c2ecf20Sopenharmony_ci .ioctl_query_info = smb2_ioctl_query_info, 55468c2ecf20Sopenharmony_ci .make_node = smb2_make_node, 55478c2ecf20Sopenharmony_ci .fiemap = smb3_fiemap, 55488c2ecf20Sopenharmony_ci .llseek = smb3_llseek, 55498c2ecf20Sopenharmony_ci .is_status_io_timeout = smb2_is_status_io_timeout, 55508c2ecf20Sopenharmony_ci}; 55518c2ecf20Sopenharmony_ci 55528c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY 55538c2ecf20Sopenharmony_cistruct smb_version_values smb20_values = { 55548c2ecf20Sopenharmony_ci .version_string = SMB20_VERSION_STRING, 55558c2ecf20Sopenharmony_ci .protocol_id = SMB20_PROT_ID, 55568c2ecf20Sopenharmony_ci .req_capabilities = 0, /* MBZ */ 55578c2ecf20Sopenharmony_ci .large_lock_type = 0, 55588c2ecf20Sopenharmony_ci .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK, 55598c2ecf20Sopenharmony_ci .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK, 55608c2ecf20Sopenharmony_ci .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, 55618c2ecf20Sopenharmony_ci .header_size = sizeof(struct smb2_sync_hdr), 55628c2ecf20Sopenharmony_ci .header_preamble_size = 0, 55638c2ecf20Sopenharmony_ci .max_header_size = MAX_SMB2_HDR_SIZE, 55648c2ecf20Sopenharmony_ci .read_rsp_size = sizeof(struct smb2_read_rsp), 55658c2ecf20Sopenharmony_ci .lock_cmd = SMB2_LOCK, 55668c2ecf20Sopenharmony_ci .cap_unix = 0, 55678c2ecf20Sopenharmony_ci .cap_nt_find = SMB2_NT_FIND, 55688c2ecf20Sopenharmony_ci .cap_large_files = SMB2_LARGE_FILES, 55698c2ecf20Sopenharmony_ci .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, 55708c2ecf20Sopenharmony_ci .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, 55718c2ecf20Sopenharmony_ci .create_lease_size = sizeof(struct create_lease), 55728c2ecf20Sopenharmony_ci}; 55738c2ecf20Sopenharmony_ci#endif /* ALLOW_INSECURE_LEGACY */ 55748c2ecf20Sopenharmony_ci 55758c2ecf20Sopenharmony_cistruct smb_version_values smb21_values = { 55768c2ecf20Sopenharmony_ci .version_string = SMB21_VERSION_STRING, 55778c2ecf20Sopenharmony_ci .protocol_id = SMB21_PROT_ID, 55788c2ecf20Sopenharmony_ci .req_capabilities = 0, /* MBZ on negotiate req until SMB3 dialect */ 55798c2ecf20Sopenharmony_ci .large_lock_type = 0, 55808c2ecf20Sopenharmony_ci .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK, 55818c2ecf20Sopenharmony_ci .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK, 55828c2ecf20Sopenharmony_ci .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, 55838c2ecf20Sopenharmony_ci .header_size = sizeof(struct smb2_sync_hdr), 55848c2ecf20Sopenharmony_ci .header_preamble_size = 0, 55858c2ecf20Sopenharmony_ci .max_header_size = MAX_SMB2_HDR_SIZE, 55868c2ecf20Sopenharmony_ci .read_rsp_size = sizeof(struct smb2_read_rsp), 55878c2ecf20Sopenharmony_ci .lock_cmd = SMB2_LOCK, 55888c2ecf20Sopenharmony_ci .cap_unix = 0, 55898c2ecf20Sopenharmony_ci .cap_nt_find = SMB2_NT_FIND, 55908c2ecf20Sopenharmony_ci .cap_large_files = SMB2_LARGE_FILES, 55918c2ecf20Sopenharmony_ci .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, 55928c2ecf20Sopenharmony_ci .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, 55938c2ecf20Sopenharmony_ci .create_lease_size = sizeof(struct create_lease), 55948c2ecf20Sopenharmony_ci}; 55958c2ecf20Sopenharmony_ci 55968c2ecf20Sopenharmony_cistruct smb_version_values smb3any_values = { 55978c2ecf20Sopenharmony_ci .version_string = SMB3ANY_VERSION_STRING, 55988c2ecf20Sopenharmony_ci .protocol_id = SMB302_PROT_ID, /* doesn't matter, send protocol array */ 55998c2ecf20Sopenharmony_ci .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION | SMB2_GLOBAL_CAP_DIRECTORY_LEASING, 56008c2ecf20Sopenharmony_ci .large_lock_type = 0, 56018c2ecf20Sopenharmony_ci .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK, 56028c2ecf20Sopenharmony_ci .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK, 56038c2ecf20Sopenharmony_ci .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, 56048c2ecf20Sopenharmony_ci .header_size = sizeof(struct smb2_sync_hdr), 56058c2ecf20Sopenharmony_ci .header_preamble_size = 0, 56068c2ecf20Sopenharmony_ci .max_header_size = MAX_SMB2_HDR_SIZE, 56078c2ecf20Sopenharmony_ci .read_rsp_size = sizeof(struct smb2_read_rsp), 56088c2ecf20Sopenharmony_ci .lock_cmd = SMB2_LOCK, 56098c2ecf20Sopenharmony_ci .cap_unix = 0, 56108c2ecf20Sopenharmony_ci .cap_nt_find = SMB2_NT_FIND, 56118c2ecf20Sopenharmony_ci .cap_large_files = SMB2_LARGE_FILES, 56128c2ecf20Sopenharmony_ci .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, 56138c2ecf20Sopenharmony_ci .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, 56148c2ecf20Sopenharmony_ci .create_lease_size = sizeof(struct create_lease_v2), 56158c2ecf20Sopenharmony_ci}; 56168c2ecf20Sopenharmony_ci 56178c2ecf20Sopenharmony_cistruct smb_version_values smbdefault_values = { 56188c2ecf20Sopenharmony_ci .version_string = SMBDEFAULT_VERSION_STRING, 56198c2ecf20Sopenharmony_ci .protocol_id = SMB302_PROT_ID, /* doesn't matter, send protocol array */ 56208c2ecf20Sopenharmony_ci .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION | SMB2_GLOBAL_CAP_DIRECTORY_LEASING, 56218c2ecf20Sopenharmony_ci .large_lock_type = 0, 56228c2ecf20Sopenharmony_ci .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK, 56238c2ecf20Sopenharmony_ci .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK, 56248c2ecf20Sopenharmony_ci .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, 56258c2ecf20Sopenharmony_ci .header_size = sizeof(struct smb2_sync_hdr), 56268c2ecf20Sopenharmony_ci .header_preamble_size = 0, 56278c2ecf20Sopenharmony_ci .max_header_size = MAX_SMB2_HDR_SIZE, 56288c2ecf20Sopenharmony_ci .read_rsp_size = sizeof(struct smb2_read_rsp), 56298c2ecf20Sopenharmony_ci .lock_cmd = SMB2_LOCK, 56308c2ecf20Sopenharmony_ci .cap_unix = 0, 56318c2ecf20Sopenharmony_ci .cap_nt_find = SMB2_NT_FIND, 56328c2ecf20Sopenharmony_ci .cap_large_files = SMB2_LARGE_FILES, 56338c2ecf20Sopenharmony_ci .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, 56348c2ecf20Sopenharmony_ci .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, 56358c2ecf20Sopenharmony_ci .create_lease_size = sizeof(struct create_lease_v2), 56368c2ecf20Sopenharmony_ci}; 56378c2ecf20Sopenharmony_ci 56388c2ecf20Sopenharmony_cistruct smb_version_values smb30_values = { 56398c2ecf20Sopenharmony_ci .version_string = SMB30_VERSION_STRING, 56408c2ecf20Sopenharmony_ci .protocol_id = SMB30_PROT_ID, 56418c2ecf20Sopenharmony_ci .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION | SMB2_GLOBAL_CAP_DIRECTORY_LEASING, 56428c2ecf20Sopenharmony_ci .large_lock_type = 0, 56438c2ecf20Sopenharmony_ci .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK, 56448c2ecf20Sopenharmony_ci .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK, 56458c2ecf20Sopenharmony_ci .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, 56468c2ecf20Sopenharmony_ci .header_size = sizeof(struct smb2_sync_hdr), 56478c2ecf20Sopenharmony_ci .header_preamble_size = 0, 56488c2ecf20Sopenharmony_ci .max_header_size = MAX_SMB2_HDR_SIZE, 56498c2ecf20Sopenharmony_ci .read_rsp_size = sizeof(struct smb2_read_rsp), 56508c2ecf20Sopenharmony_ci .lock_cmd = SMB2_LOCK, 56518c2ecf20Sopenharmony_ci .cap_unix = 0, 56528c2ecf20Sopenharmony_ci .cap_nt_find = SMB2_NT_FIND, 56538c2ecf20Sopenharmony_ci .cap_large_files = SMB2_LARGE_FILES, 56548c2ecf20Sopenharmony_ci .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, 56558c2ecf20Sopenharmony_ci .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, 56568c2ecf20Sopenharmony_ci .create_lease_size = sizeof(struct create_lease_v2), 56578c2ecf20Sopenharmony_ci}; 56588c2ecf20Sopenharmony_ci 56598c2ecf20Sopenharmony_cistruct smb_version_values smb302_values = { 56608c2ecf20Sopenharmony_ci .version_string = SMB302_VERSION_STRING, 56618c2ecf20Sopenharmony_ci .protocol_id = SMB302_PROT_ID, 56628c2ecf20Sopenharmony_ci .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION | SMB2_GLOBAL_CAP_DIRECTORY_LEASING, 56638c2ecf20Sopenharmony_ci .large_lock_type = 0, 56648c2ecf20Sopenharmony_ci .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK, 56658c2ecf20Sopenharmony_ci .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK, 56668c2ecf20Sopenharmony_ci .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, 56678c2ecf20Sopenharmony_ci .header_size = sizeof(struct smb2_sync_hdr), 56688c2ecf20Sopenharmony_ci .header_preamble_size = 0, 56698c2ecf20Sopenharmony_ci .max_header_size = MAX_SMB2_HDR_SIZE, 56708c2ecf20Sopenharmony_ci .read_rsp_size = sizeof(struct smb2_read_rsp), 56718c2ecf20Sopenharmony_ci .lock_cmd = SMB2_LOCK, 56728c2ecf20Sopenharmony_ci .cap_unix = 0, 56738c2ecf20Sopenharmony_ci .cap_nt_find = SMB2_NT_FIND, 56748c2ecf20Sopenharmony_ci .cap_large_files = SMB2_LARGE_FILES, 56758c2ecf20Sopenharmony_ci .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, 56768c2ecf20Sopenharmony_ci .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, 56778c2ecf20Sopenharmony_ci .create_lease_size = sizeof(struct create_lease_v2), 56788c2ecf20Sopenharmony_ci}; 56798c2ecf20Sopenharmony_ci 56808c2ecf20Sopenharmony_cistruct smb_version_values smb311_values = { 56818c2ecf20Sopenharmony_ci .version_string = SMB311_VERSION_STRING, 56828c2ecf20Sopenharmony_ci .protocol_id = SMB311_PROT_ID, 56838c2ecf20Sopenharmony_ci .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION | SMB2_GLOBAL_CAP_DIRECTORY_LEASING, 56848c2ecf20Sopenharmony_ci .large_lock_type = 0, 56858c2ecf20Sopenharmony_ci .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK, 56868c2ecf20Sopenharmony_ci .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK, 56878c2ecf20Sopenharmony_ci .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, 56888c2ecf20Sopenharmony_ci .header_size = sizeof(struct smb2_sync_hdr), 56898c2ecf20Sopenharmony_ci .header_preamble_size = 0, 56908c2ecf20Sopenharmony_ci .max_header_size = MAX_SMB2_HDR_SIZE, 56918c2ecf20Sopenharmony_ci .read_rsp_size = sizeof(struct smb2_read_rsp), 56928c2ecf20Sopenharmony_ci .lock_cmd = SMB2_LOCK, 56938c2ecf20Sopenharmony_ci .cap_unix = 0, 56948c2ecf20Sopenharmony_ci .cap_nt_find = SMB2_NT_FIND, 56958c2ecf20Sopenharmony_ci .cap_large_files = SMB2_LARGE_FILES, 56968c2ecf20Sopenharmony_ci .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, 56978c2ecf20Sopenharmony_ci .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, 56988c2ecf20Sopenharmony_ci .create_lease_size = sizeof(struct create_lease_v2), 56998c2ecf20Sopenharmony_ci}; 5700