162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * SMB2 version specific operations 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2012, Jeff Layton <jlayton@redhat.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/pagemap.h> 962306a36Sopenharmony_ci#include <linux/vfs.h> 1062306a36Sopenharmony_ci#include <linux/falloc.h> 1162306a36Sopenharmony_ci#include <linux/scatterlist.h> 1262306a36Sopenharmony_ci#include <linux/uuid.h> 1362306a36Sopenharmony_ci#include <linux/sort.h> 1462306a36Sopenharmony_ci#include <crypto/aead.h> 1562306a36Sopenharmony_ci#include <linux/fiemap.h> 1662306a36Sopenharmony_ci#include <uapi/linux/magic.h> 1762306a36Sopenharmony_ci#include "cifsfs.h" 1862306a36Sopenharmony_ci#include "cifsglob.h" 1962306a36Sopenharmony_ci#include "smb2pdu.h" 2062306a36Sopenharmony_ci#include "smb2proto.h" 2162306a36Sopenharmony_ci#include "cifsproto.h" 2262306a36Sopenharmony_ci#include "cifs_debug.h" 2362306a36Sopenharmony_ci#include "cifs_unicode.h" 2462306a36Sopenharmony_ci#include "smb2status.h" 2562306a36Sopenharmony_ci#include "smb2glob.h" 2662306a36Sopenharmony_ci#include "cifs_ioctl.h" 2762306a36Sopenharmony_ci#include "smbdirect.h" 2862306a36Sopenharmony_ci#include "fscache.h" 2962306a36Sopenharmony_ci#include "fs_context.h" 3062306a36Sopenharmony_ci#include "cached_dir.h" 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* Change credits for different ops and return the total number of credits */ 3362306a36Sopenharmony_cistatic int 3462306a36Sopenharmony_cichange_conf(struct TCP_Server_Info *server) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci server->credits += server->echo_credits + server->oplock_credits; 3762306a36Sopenharmony_ci if (server->credits > server->max_credits) 3862306a36Sopenharmony_ci server->credits = server->max_credits; 3962306a36Sopenharmony_ci server->oplock_credits = server->echo_credits = 0; 4062306a36Sopenharmony_ci switch (server->credits) { 4162306a36Sopenharmony_ci case 0: 4262306a36Sopenharmony_ci return 0; 4362306a36Sopenharmony_ci case 1: 4462306a36Sopenharmony_ci server->echoes = false; 4562306a36Sopenharmony_ci server->oplocks = false; 4662306a36Sopenharmony_ci break; 4762306a36Sopenharmony_ci case 2: 4862306a36Sopenharmony_ci server->echoes = true; 4962306a36Sopenharmony_ci server->oplocks = false; 5062306a36Sopenharmony_ci server->echo_credits = 1; 5162306a36Sopenharmony_ci break; 5262306a36Sopenharmony_ci default: 5362306a36Sopenharmony_ci server->echoes = true; 5462306a36Sopenharmony_ci if (enable_oplocks) { 5562306a36Sopenharmony_ci server->oplocks = true; 5662306a36Sopenharmony_ci server->oplock_credits = 1; 5762306a36Sopenharmony_ci } else 5862306a36Sopenharmony_ci server->oplocks = false; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci server->echo_credits = 1; 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci server->credits -= server->echo_credits + server->oplock_credits; 6362306a36Sopenharmony_ci return server->credits + server->echo_credits + server->oplock_credits; 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic void 6762306a36Sopenharmony_cismb2_add_credits(struct TCP_Server_Info *server, 6862306a36Sopenharmony_ci const struct cifs_credits *credits, const int optype) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci int *val, rc = -1; 7162306a36Sopenharmony_ci int scredits, in_flight; 7262306a36Sopenharmony_ci unsigned int add = credits->value; 7362306a36Sopenharmony_ci unsigned int instance = credits->instance; 7462306a36Sopenharmony_ci bool reconnect_detected = false; 7562306a36Sopenharmony_ci bool reconnect_with_invalid_credits = false; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci spin_lock(&server->req_lock); 7862306a36Sopenharmony_ci val = server->ops->get_credits_field(server, optype); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci /* eg found case where write overlapping reconnect messed up credits */ 8162306a36Sopenharmony_ci if (((optype & CIFS_OP_MASK) == CIFS_NEG_OP) && (*val != 0)) 8262306a36Sopenharmony_ci reconnect_with_invalid_credits = true; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci if ((instance == 0) || (instance == server->reconnect_instance)) 8562306a36Sopenharmony_ci *val += add; 8662306a36Sopenharmony_ci else 8762306a36Sopenharmony_ci reconnect_detected = true; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (*val > 65000) { 9062306a36Sopenharmony_ci *val = 65000; /* Don't get near 64K credits, avoid srv bugs */ 9162306a36Sopenharmony_ci pr_warn_once("server overflowed SMB3 credits\n"); 9262306a36Sopenharmony_ci trace_smb3_overflow_credits(server->CurrentMid, 9362306a36Sopenharmony_ci server->conn_id, server->hostname, *val, 9462306a36Sopenharmony_ci add, server->in_flight); 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci WARN_ON_ONCE(server->in_flight == 0); 9762306a36Sopenharmony_ci server->in_flight--; 9862306a36Sopenharmony_ci if (server->in_flight == 0 && 9962306a36Sopenharmony_ci ((optype & CIFS_OP_MASK) != CIFS_NEG_OP) && 10062306a36Sopenharmony_ci ((optype & CIFS_OP_MASK) != CIFS_SESS_OP)) 10162306a36Sopenharmony_ci rc = change_conf(server); 10262306a36Sopenharmony_ci /* 10362306a36Sopenharmony_ci * Sometimes server returns 0 credits on oplock break ack - we need to 10462306a36Sopenharmony_ci * rebalance credits in this case. 10562306a36Sopenharmony_ci */ 10662306a36Sopenharmony_ci else if (server->in_flight > 0 && server->oplock_credits == 0 && 10762306a36Sopenharmony_ci server->oplocks) { 10862306a36Sopenharmony_ci if (server->credits > 1) { 10962306a36Sopenharmony_ci server->credits--; 11062306a36Sopenharmony_ci server->oplock_credits++; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci } else if ((server->in_flight > 0) && (server->oplock_credits > 3) && 11362306a36Sopenharmony_ci ((optype & CIFS_OP_MASK) == CIFS_OBREAK_OP)) 11462306a36Sopenharmony_ci /* if now have too many oplock credits, rebalance so don't starve normal ops */ 11562306a36Sopenharmony_ci change_conf(server); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci scredits = *val; 11862306a36Sopenharmony_ci in_flight = server->in_flight; 11962306a36Sopenharmony_ci spin_unlock(&server->req_lock); 12062306a36Sopenharmony_ci wake_up(&server->request_q); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (reconnect_detected) { 12362306a36Sopenharmony_ci trace_smb3_reconnect_detected(server->CurrentMid, 12462306a36Sopenharmony_ci server->conn_id, server->hostname, scredits, add, in_flight); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci cifs_dbg(FYI, "trying to put %d credits from the old server instance %d\n", 12762306a36Sopenharmony_ci add, instance); 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (reconnect_with_invalid_credits) { 13162306a36Sopenharmony_ci trace_smb3_reconnect_with_invalid_credits(server->CurrentMid, 13262306a36Sopenharmony_ci server->conn_id, server->hostname, scredits, add, in_flight); 13362306a36Sopenharmony_ci cifs_dbg(FYI, "Negotiate operation when server credits is non-zero. Optype: %d, server credits: %d, credits added: %d\n", 13462306a36Sopenharmony_ci optype, scredits, add); 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci spin_lock(&server->srv_lock); 13862306a36Sopenharmony_ci if (server->tcpStatus == CifsNeedReconnect 13962306a36Sopenharmony_ci || server->tcpStatus == CifsExiting) { 14062306a36Sopenharmony_ci spin_unlock(&server->srv_lock); 14162306a36Sopenharmony_ci return; 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci spin_unlock(&server->srv_lock); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci switch (rc) { 14662306a36Sopenharmony_ci case -1: 14762306a36Sopenharmony_ci /* change_conf hasn't been executed */ 14862306a36Sopenharmony_ci break; 14962306a36Sopenharmony_ci case 0: 15062306a36Sopenharmony_ci cifs_server_dbg(VFS, "Possible client or server bug - zero credits\n"); 15162306a36Sopenharmony_ci break; 15262306a36Sopenharmony_ci case 1: 15362306a36Sopenharmony_ci cifs_server_dbg(VFS, "disabling echoes and oplocks\n"); 15462306a36Sopenharmony_ci break; 15562306a36Sopenharmony_ci case 2: 15662306a36Sopenharmony_ci cifs_dbg(FYI, "disabling oplocks\n"); 15762306a36Sopenharmony_ci break; 15862306a36Sopenharmony_ci default: 15962306a36Sopenharmony_ci /* change_conf rebalanced credits for different types */ 16062306a36Sopenharmony_ci break; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci trace_smb3_add_credits(server->CurrentMid, 16462306a36Sopenharmony_ci server->conn_id, server->hostname, scredits, add, in_flight); 16562306a36Sopenharmony_ci cifs_dbg(FYI, "%s: added %u credits total=%d\n", __func__, add, scredits); 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic void 16962306a36Sopenharmony_cismb2_set_credits(struct TCP_Server_Info *server, const int val) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci int scredits, in_flight; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci spin_lock(&server->req_lock); 17462306a36Sopenharmony_ci server->credits = val; 17562306a36Sopenharmony_ci if (val == 1) { 17662306a36Sopenharmony_ci server->reconnect_instance++; 17762306a36Sopenharmony_ci /* 17862306a36Sopenharmony_ci * ChannelSequence updated for all channels in primary channel so that consistent 17962306a36Sopenharmony_ci * across SMB3 requests sent on any channel. See MS-SMB2 3.2.4.1 and 3.2.7.1 18062306a36Sopenharmony_ci */ 18162306a36Sopenharmony_ci if (SERVER_IS_CHAN(server)) 18262306a36Sopenharmony_ci server->primary_server->channel_sequence_num++; 18362306a36Sopenharmony_ci else 18462306a36Sopenharmony_ci server->channel_sequence_num++; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci scredits = server->credits; 18762306a36Sopenharmony_ci in_flight = server->in_flight; 18862306a36Sopenharmony_ci spin_unlock(&server->req_lock); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci trace_smb3_set_credits(server->CurrentMid, 19162306a36Sopenharmony_ci server->conn_id, server->hostname, scredits, val, in_flight); 19262306a36Sopenharmony_ci cifs_dbg(FYI, "%s: set %u credits\n", __func__, val); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* don't log while holding the lock */ 19562306a36Sopenharmony_ci if (val == 1) 19662306a36Sopenharmony_ci cifs_dbg(FYI, "set credits to 1 due to smb2 reconnect\n"); 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic int * 20062306a36Sopenharmony_cismb2_get_credits_field(struct TCP_Server_Info *server, const int optype) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci switch (optype) { 20362306a36Sopenharmony_ci case CIFS_ECHO_OP: 20462306a36Sopenharmony_ci return &server->echo_credits; 20562306a36Sopenharmony_ci case CIFS_OBREAK_OP: 20662306a36Sopenharmony_ci return &server->oplock_credits; 20762306a36Sopenharmony_ci default: 20862306a36Sopenharmony_ci return &server->credits; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic unsigned int 21362306a36Sopenharmony_cismb2_get_credits(struct mid_q_entry *mid) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci return mid->credits_received; 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic int 21962306a36Sopenharmony_cismb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size, 22062306a36Sopenharmony_ci unsigned int *num, struct cifs_credits *credits) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci int rc = 0; 22362306a36Sopenharmony_ci unsigned int scredits, in_flight; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci spin_lock(&server->req_lock); 22662306a36Sopenharmony_ci while (1) { 22762306a36Sopenharmony_ci spin_unlock(&server->req_lock); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci spin_lock(&server->srv_lock); 23062306a36Sopenharmony_ci if (server->tcpStatus == CifsExiting) { 23162306a36Sopenharmony_ci spin_unlock(&server->srv_lock); 23262306a36Sopenharmony_ci return -ENOENT; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci spin_unlock(&server->srv_lock); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci spin_lock(&server->req_lock); 23762306a36Sopenharmony_ci if (server->credits <= 0) { 23862306a36Sopenharmony_ci spin_unlock(&server->req_lock); 23962306a36Sopenharmony_ci cifs_num_waiters_inc(server); 24062306a36Sopenharmony_ci rc = wait_event_killable(server->request_q, 24162306a36Sopenharmony_ci has_credits(server, &server->credits, 1)); 24262306a36Sopenharmony_ci cifs_num_waiters_dec(server); 24362306a36Sopenharmony_ci if (rc) 24462306a36Sopenharmony_ci return rc; 24562306a36Sopenharmony_ci spin_lock(&server->req_lock); 24662306a36Sopenharmony_ci } else { 24762306a36Sopenharmony_ci scredits = server->credits; 24862306a36Sopenharmony_ci /* can deadlock with reopen */ 24962306a36Sopenharmony_ci if (scredits <= 8) { 25062306a36Sopenharmony_ci *num = SMB2_MAX_BUFFER_SIZE; 25162306a36Sopenharmony_ci credits->value = 0; 25262306a36Sopenharmony_ci credits->instance = 0; 25362306a36Sopenharmony_ci break; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* leave some credits for reopen and other ops */ 25762306a36Sopenharmony_ci scredits -= 8; 25862306a36Sopenharmony_ci *num = min_t(unsigned int, size, 25962306a36Sopenharmony_ci scredits * SMB2_MAX_BUFFER_SIZE); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci credits->value = 26262306a36Sopenharmony_ci DIV_ROUND_UP(*num, SMB2_MAX_BUFFER_SIZE); 26362306a36Sopenharmony_ci credits->instance = server->reconnect_instance; 26462306a36Sopenharmony_ci server->credits -= credits->value; 26562306a36Sopenharmony_ci server->in_flight++; 26662306a36Sopenharmony_ci if (server->in_flight > server->max_in_flight) 26762306a36Sopenharmony_ci server->max_in_flight = server->in_flight; 26862306a36Sopenharmony_ci break; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci scredits = server->credits; 27262306a36Sopenharmony_ci in_flight = server->in_flight; 27362306a36Sopenharmony_ci spin_unlock(&server->req_lock); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci trace_smb3_wait_credits(server->CurrentMid, 27662306a36Sopenharmony_ci server->conn_id, server->hostname, scredits, -(credits->value), in_flight); 27762306a36Sopenharmony_ci cifs_dbg(FYI, "%s: removed %u credits total=%d\n", 27862306a36Sopenharmony_ci __func__, credits->value, scredits); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci return rc; 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic int 28462306a36Sopenharmony_cismb2_adjust_credits(struct TCP_Server_Info *server, 28562306a36Sopenharmony_ci struct cifs_credits *credits, 28662306a36Sopenharmony_ci const unsigned int payload_size) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci int new_val = DIV_ROUND_UP(payload_size, SMB2_MAX_BUFFER_SIZE); 28962306a36Sopenharmony_ci int scredits, in_flight; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (!credits->value || credits->value == new_val) 29262306a36Sopenharmony_ci return 0; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (credits->value < new_val) { 29562306a36Sopenharmony_ci trace_smb3_too_many_credits(server->CurrentMid, 29662306a36Sopenharmony_ci server->conn_id, server->hostname, 0, credits->value - new_val, 0); 29762306a36Sopenharmony_ci cifs_server_dbg(VFS, "request has less credits (%d) than required (%d)", 29862306a36Sopenharmony_ci credits->value, new_val); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci return -EOPNOTSUPP; 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci spin_lock(&server->req_lock); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (server->reconnect_instance != credits->instance) { 30662306a36Sopenharmony_ci scredits = server->credits; 30762306a36Sopenharmony_ci in_flight = server->in_flight; 30862306a36Sopenharmony_ci spin_unlock(&server->req_lock); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci trace_smb3_reconnect_detected(server->CurrentMid, 31162306a36Sopenharmony_ci server->conn_id, server->hostname, scredits, 31262306a36Sopenharmony_ci credits->value - new_val, in_flight); 31362306a36Sopenharmony_ci cifs_server_dbg(VFS, "trying to return %d credits to old session\n", 31462306a36Sopenharmony_ci credits->value - new_val); 31562306a36Sopenharmony_ci return -EAGAIN; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci server->credits += credits->value - new_val; 31962306a36Sopenharmony_ci scredits = server->credits; 32062306a36Sopenharmony_ci in_flight = server->in_flight; 32162306a36Sopenharmony_ci spin_unlock(&server->req_lock); 32262306a36Sopenharmony_ci wake_up(&server->request_q); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci trace_smb3_adj_credits(server->CurrentMid, 32562306a36Sopenharmony_ci server->conn_id, server->hostname, scredits, 32662306a36Sopenharmony_ci credits->value - new_val, in_flight); 32762306a36Sopenharmony_ci cifs_dbg(FYI, "%s: adjust added %u credits total=%d\n", 32862306a36Sopenharmony_ci __func__, credits->value - new_val, scredits); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci credits->value = new_val; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci return 0; 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic __u64 33662306a36Sopenharmony_cismb2_get_next_mid(struct TCP_Server_Info *server) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci __u64 mid; 33962306a36Sopenharmony_ci /* for SMB2 we need the current value */ 34062306a36Sopenharmony_ci spin_lock(&server->mid_lock); 34162306a36Sopenharmony_ci mid = server->CurrentMid++; 34262306a36Sopenharmony_ci spin_unlock(&server->mid_lock); 34362306a36Sopenharmony_ci return mid; 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic void 34762306a36Sopenharmony_cismb2_revert_current_mid(struct TCP_Server_Info *server, const unsigned int val) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci spin_lock(&server->mid_lock); 35062306a36Sopenharmony_ci if (server->CurrentMid >= val) 35162306a36Sopenharmony_ci server->CurrentMid -= val; 35262306a36Sopenharmony_ci spin_unlock(&server->mid_lock); 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic struct mid_q_entry * 35662306a36Sopenharmony_ci__smb2_find_mid(struct TCP_Server_Info *server, char *buf, bool dequeue) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci struct mid_q_entry *mid; 35962306a36Sopenharmony_ci struct smb2_hdr *shdr = (struct smb2_hdr *)buf; 36062306a36Sopenharmony_ci __u64 wire_mid = le64_to_cpu(shdr->MessageId); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (shdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) { 36362306a36Sopenharmony_ci cifs_server_dbg(VFS, "Encrypted frame parsing not supported yet\n"); 36462306a36Sopenharmony_ci return NULL; 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci spin_lock(&server->mid_lock); 36862306a36Sopenharmony_ci list_for_each_entry(mid, &server->pending_mid_q, qhead) { 36962306a36Sopenharmony_ci if ((mid->mid == wire_mid) && 37062306a36Sopenharmony_ci (mid->mid_state == MID_REQUEST_SUBMITTED) && 37162306a36Sopenharmony_ci (mid->command == shdr->Command)) { 37262306a36Sopenharmony_ci kref_get(&mid->refcount); 37362306a36Sopenharmony_ci if (dequeue) { 37462306a36Sopenharmony_ci list_del_init(&mid->qhead); 37562306a36Sopenharmony_ci mid->mid_flags |= MID_DELETED; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci spin_unlock(&server->mid_lock); 37862306a36Sopenharmony_ci return mid; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci spin_unlock(&server->mid_lock); 38262306a36Sopenharmony_ci return NULL; 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic struct mid_q_entry * 38662306a36Sopenharmony_cismb2_find_mid(struct TCP_Server_Info *server, char *buf) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci return __smb2_find_mid(server, buf, false); 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic struct mid_q_entry * 39262306a36Sopenharmony_cismb2_find_dequeue_mid(struct TCP_Server_Info *server, char *buf) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci return __smb2_find_mid(server, buf, true); 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cistatic void 39862306a36Sopenharmony_cismb2_dump_detail(void *buf, struct TCP_Server_Info *server) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci#ifdef CONFIG_CIFS_DEBUG2 40162306a36Sopenharmony_ci struct smb2_hdr *shdr = (struct smb2_hdr *)buf; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci cifs_server_dbg(VFS, "Cmd: %d Err: 0x%x Flags: 0x%x Mid: %llu Pid: %d\n", 40462306a36Sopenharmony_ci shdr->Command, shdr->Status, shdr->Flags, shdr->MessageId, 40562306a36Sopenharmony_ci shdr->Id.SyncId.ProcessId); 40662306a36Sopenharmony_ci if (!server->ops->check_message(buf, server->total_read, server)) { 40762306a36Sopenharmony_ci cifs_server_dbg(VFS, "smb buf %p len %u\n", buf, 40862306a36Sopenharmony_ci server->ops->calc_smb_size(buf)); 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci#endif 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_cistatic bool 41462306a36Sopenharmony_cismb2_need_neg(struct TCP_Server_Info *server) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci return server->max_read == 0; 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic int 42062306a36Sopenharmony_cismb2_negotiate(const unsigned int xid, 42162306a36Sopenharmony_ci struct cifs_ses *ses, 42262306a36Sopenharmony_ci struct TCP_Server_Info *server) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci int rc; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci spin_lock(&server->mid_lock); 42762306a36Sopenharmony_ci server->CurrentMid = 0; 42862306a36Sopenharmony_ci spin_unlock(&server->mid_lock); 42962306a36Sopenharmony_ci rc = SMB2_negotiate(xid, ses, server); 43062306a36Sopenharmony_ci /* BB we probably don't need to retry with modern servers */ 43162306a36Sopenharmony_ci if (rc == -EAGAIN) 43262306a36Sopenharmony_ci rc = -EHOSTDOWN; 43362306a36Sopenharmony_ci return rc; 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_cistatic unsigned int 43762306a36Sopenharmony_cismb2_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci struct TCP_Server_Info *server = tcon->ses->server; 44062306a36Sopenharmony_ci unsigned int wsize; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* start with specified wsize, or default */ 44362306a36Sopenharmony_ci wsize = ctx->wsize ? ctx->wsize : CIFS_DEFAULT_IOSIZE; 44462306a36Sopenharmony_ci wsize = min_t(unsigned int, wsize, server->max_write); 44562306a36Sopenharmony_ci if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) 44662306a36Sopenharmony_ci wsize = min_t(unsigned int, wsize, SMB2_MAX_BUFFER_SIZE); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci return wsize; 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cistatic unsigned int 45262306a36Sopenharmony_cismb3_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci struct TCP_Server_Info *server = tcon->ses->server; 45562306a36Sopenharmony_ci unsigned int wsize; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci /* start with specified wsize, or default */ 45862306a36Sopenharmony_ci wsize = ctx->wsize ? ctx->wsize : SMB3_DEFAULT_IOSIZE; 45962306a36Sopenharmony_ci wsize = min_t(unsigned int, wsize, server->max_write); 46062306a36Sopenharmony_ci#ifdef CONFIG_CIFS_SMB_DIRECT 46162306a36Sopenharmony_ci if (server->rdma) { 46262306a36Sopenharmony_ci if (server->sign) 46362306a36Sopenharmony_ci /* 46462306a36Sopenharmony_ci * Account for SMB2 data transfer packet header and 46562306a36Sopenharmony_ci * possible encryption header 46662306a36Sopenharmony_ci */ 46762306a36Sopenharmony_ci wsize = min_t(unsigned int, 46862306a36Sopenharmony_ci wsize, 46962306a36Sopenharmony_ci server->smbd_conn->max_fragmented_send_size - 47062306a36Sopenharmony_ci SMB2_READWRITE_PDU_HEADER_SIZE - 47162306a36Sopenharmony_ci sizeof(struct smb2_transform_hdr)); 47262306a36Sopenharmony_ci else 47362306a36Sopenharmony_ci wsize = min_t(unsigned int, 47462306a36Sopenharmony_ci wsize, server->smbd_conn->max_readwrite_size); 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci#endif 47762306a36Sopenharmony_ci if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) 47862306a36Sopenharmony_ci wsize = min_t(unsigned int, wsize, SMB2_MAX_BUFFER_SIZE); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci return wsize; 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cistatic unsigned int 48462306a36Sopenharmony_cismb2_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci struct TCP_Server_Info *server = tcon->ses->server; 48762306a36Sopenharmony_ci unsigned int rsize; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci /* start with specified rsize, or default */ 49062306a36Sopenharmony_ci rsize = ctx->rsize ? ctx->rsize : CIFS_DEFAULT_IOSIZE; 49162306a36Sopenharmony_ci rsize = min_t(unsigned int, rsize, server->max_read); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) 49462306a36Sopenharmony_ci rsize = min_t(unsigned int, rsize, SMB2_MAX_BUFFER_SIZE); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci return rsize; 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cistatic unsigned int 50062306a36Sopenharmony_cismb3_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci struct TCP_Server_Info *server = tcon->ses->server; 50362306a36Sopenharmony_ci unsigned int rsize; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci /* start with specified rsize, or default */ 50662306a36Sopenharmony_ci rsize = ctx->rsize ? ctx->rsize : SMB3_DEFAULT_IOSIZE; 50762306a36Sopenharmony_ci rsize = min_t(unsigned int, rsize, server->max_read); 50862306a36Sopenharmony_ci#ifdef CONFIG_CIFS_SMB_DIRECT 50962306a36Sopenharmony_ci if (server->rdma) { 51062306a36Sopenharmony_ci if (server->sign) 51162306a36Sopenharmony_ci /* 51262306a36Sopenharmony_ci * Account for SMB2 data transfer packet header and 51362306a36Sopenharmony_ci * possible encryption header 51462306a36Sopenharmony_ci */ 51562306a36Sopenharmony_ci rsize = min_t(unsigned int, 51662306a36Sopenharmony_ci rsize, 51762306a36Sopenharmony_ci server->smbd_conn->max_fragmented_recv_size - 51862306a36Sopenharmony_ci SMB2_READWRITE_PDU_HEADER_SIZE - 51962306a36Sopenharmony_ci sizeof(struct smb2_transform_hdr)); 52062306a36Sopenharmony_ci else 52162306a36Sopenharmony_ci rsize = min_t(unsigned int, 52262306a36Sopenharmony_ci rsize, server->smbd_conn->max_readwrite_size); 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci#endif 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) 52762306a36Sopenharmony_ci rsize = min_t(unsigned int, rsize, SMB2_MAX_BUFFER_SIZE); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci return rsize; 53062306a36Sopenharmony_ci} 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci/* 53362306a36Sopenharmony_ci * compare two interfaces a and b 53462306a36Sopenharmony_ci * return 0 if everything matches. 53562306a36Sopenharmony_ci * return 1 if a is rdma capable, or rss capable, or has higher link speed 53662306a36Sopenharmony_ci * return -1 otherwise. 53762306a36Sopenharmony_ci */ 53862306a36Sopenharmony_cistatic int 53962306a36Sopenharmony_ciiface_cmp(struct cifs_server_iface *a, struct cifs_server_iface *b) 54062306a36Sopenharmony_ci{ 54162306a36Sopenharmony_ci int cmp_ret = 0; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci WARN_ON(!a || !b); 54462306a36Sopenharmony_ci if (a->rdma_capable == b->rdma_capable) { 54562306a36Sopenharmony_ci if (a->rss_capable == b->rss_capable) { 54662306a36Sopenharmony_ci if (a->speed == b->speed) { 54762306a36Sopenharmony_ci cmp_ret = cifs_ipaddr_cmp((struct sockaddr *) &a->sockaddr, 54862306a36Sopenharmony_ci (struct sockaddr *) &b->sockaddr); 54962306a36Sopenharmony_ci if (!cmp_ret) 55062306a36Sopenharmony_ci return 0; 55162306a36Sopenharmony_ci else if (cmp_ret > 0) 55262306a36Sopenharmony_ci return 1; 55362306a36Sopenharmony_ci else 55462306a36Sopenharmony_ci return -1; 55562306a36Sopenharmony_ci } else if (a->speed > b->speed) 55662306a36Sopenharmony_ci return 1; 55762306a36Sopenharmony_ci else 55862306a36Sopenharmony_ci return -1; 55962306a36Sopenharmony_ci } else if (a->rss_capable > b->rss_capable) 56062306a36Sopenharmony_ci return 1; 56162306a36Sopenharmony_ci else 56262306a36Sopenharmony_ci return -1; 56362306a36Sopenharmony_ci } else if (a->rdma_capable > b->rdma_capable) 56462306a36Sopenharmony_ci return 1; 56562306a36Sopenharmony_ci else 56662306a36Sopenharmony_ci return -1; 56762306a36Sopenharmony_ci} 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_cistatic int 57062306a36Sopenharmony_ciparse_server_interfaces(struct network_interface_info_ioctl_rsp *buf, 57162306a36Sopenharmony_ci size_t buf_len, struct cifs_ses *ses, bool in_mount) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci struct network_interface_info_ioctl_rsp *p; 57462306a36Sopenharmony_ci struct sockaddr_in *addr4; 57562306a36Sopenharmony_ci struct sockaddr_in6 *addr6; 57662306a36Sopenharmony_ci struct iface_info_ipv4 *p4; 57762306a36Sopenharmony_ci struct iface_info_ipv6 *p6; 57862306a36Sopenharmony_ci struct cifs_server_iface *info = NULL, *iface = NULL, *niface = NULL; 57962306a36Sopenharmony_ci struct cifs_server_iface tmp_iface; 58062306a36Sopenharmony_ci ssize_t bytes_left; 58162306a36Sopenharmony_ci size_t next = 0; 58262306a36Sopenharmony_ci int nb_iface = 0; 58362306a36Sopenharmony_ci int rc = 0, ret = 0; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci bytes_left = buf_len; 58662306a36Sopenharmony_ci p = buf; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci spin_lock(&ses->iface_lock); 58962306a36Sopenharmony_ci /* do not query too frequently, this time with lock held */ 59062306a36Sopenharmony_ci if (ses->iface_last_update && 59162306a36Sopenharmony_ci time_before(jiffies, ses->iface_last_update + 59262306a36Sopenharmony_ci (SMB_INTERFACE_POLL_INTERVAL * HZ))) { 59362306a36Sopenharmony_ci spin_unlock(&ses->iface_lock); 59462306a36Sopenharmony_ci return 0; 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci /* 59862306a36Sopenharmony_ci * Go through iface_list and mark them as inactive 59962306a36Sopenharmony_ci */ 60062306a36Sopenharmony_ci list_for_each_entry_safe(iface, niface, &ses->iface_list, 60162306a36Sopenharmony_ci iface_head) 60262306a36Sopenharmony_ci iface->is_active = 0; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci spin_unlock(&ses->iface_lock); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci /* 60762306a36Sopenharmony_ci * Samba server e.g. can return an empty interface list in some cases, 60862306a36Sopenharmony_ci * which would only be a problem if we were requesting multichannel 60962306a36Sopenharmony_ci */ 61062306a36Sopenharmony_ci if (bytes_left == 0) { 61162306a36Sopenharmony_ci /* avoid spamming logs every 10 minutes, so log only in mount */ 61262306a36Sopenharmony_ci if ((ses->chan_max > 1) && in_mount) 61362306a36Sopenharmony_ci cifs_dbg(VFS, 61462306a36Sopenharmony_ci "multichannel not available\n" 61562306a36Sopenharmony_ci "Empty network interface list returned by server %s\n", 61662306a36Sopenharmony_ci ses->server->hostname); 61762306a36Sopenharmony_ci rc = -EOPNOTSUPP; 61862306a36Sopenharmony_ci ses->iface_last_update = jiffies; 61962306a36Sopenharmony_ci goto out; 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci while (bytes_left >= (ssize_t)sizeof(*p)) { 62362306a36Sopenharmony_ci memset(&tmp_iface, 0, sizeof(tmp_iface)); 62462306a36Sopenharmony_ci tmp_iface.speed = le64_to_cpu(p->LinkSpeed); 62562306a36Sopenharmony_ci tmp_iface.rdma_capable = le32_to_cpu(p->Capability & RDMA_CAPABLE) ? 1 : 0; 62662306a36Sopenharmony_ci tmp_iface.rss_capable = le32_to_cpu(p->Capability & RSS_CAPABLE) ? 1 : 0; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci switch (p->Family) { 62962306a36Sopenharmony_ci /* 63062306a36Sopenharmony_ci * The kernel and wire socket structures have the same 63162306a36Sopenharmony_ci * layout and use network byte order but make the 63262306a36Sopenharmony_ci * conversion explicit in case either one changes. 63362306a36Sopenharmony_ci */ 63462306a36Sopenharmony_ci case INTERNETWORK: 63562306a36Sopenharmony_ci addr4 = (struct sockaddr_in *)&tmp_iface.sockaddr; 63662306a36Sopenharmony_ci p4 = (struct iface_info_ipv4 *)p->Buffer; 63762306a36Sopenharmony_ci addr4->sin_family = AF_INET; 63862306a36Sopenharmony_ci memcpy(&addr4->sin_addr, &p4->IPv4Address, 4); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci /* [MS-SMB2] 2.2.32.5.1.1 Clients MUST ignore these */ 64162306a36Sopenharmony_ci addr4->sin_port = cpu_to_be16(CIFS_PORT); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci cifs_dbg(FYI, "%s: ipv4 %pI4\n", __func__, 64462306a36Sopenharmony_ci &addr4->sin_addr); 64562306a36Sopenharmony_ci break; 64662306a36Sopenharmony_ci case INTERNETWORKV6: 64762306a36Sopenharmony_ci addr6 = (struct sockaddr_in6 *)&tmp_iface.sockaddr; 64862306a36Sopenharmony_ci p6 = (struct iface_info_ipv6 *)p->Buffer; 64962306a36Sopenharmony_ci addr6->sin6_family = AF_INET6; 65062306a36Sopenharmony_ci memcpy(&addr6->sin6_addr, &p6->IPv6Address, 16); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci /* [MS-SMB2] 2.2.32.5.1.2 Clients MUST ignore these */ 65362306a36Sopenharmony_ci addr6->sin6_flowinfo = 0; 65462306a36Sopenharmony_ci addr6->sin6_scope_id = 0; 65562306a36Sopenharmony_ci addr6->sin6_port = cpu_to_be16(CIFS_PORT); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci cifs_dbg(FYI, "%s: ipv6 %pI6\n", __func__, 65862306a36Sopenharmony_ci &addr6->sin6_addr); 65962306a36Sopenharmony_ci break; 66062306a36Sopenharmony_ci default: 66162306a36Sopenharmony_ci cifs_dbg(VFS, 66262306a36Sopenharmony_ci "%s: skipping unsupported socket family\n", 66362306a36Sopenharmony_ci __func__); 66462306a36Sopenharmony_ci goto next_iface; 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci /* 66862306a36Sopenharmony_ci * The iface_list is assumed to be sorted by speed. 66962306a36Sopenharmony_ci * Check if the new interface exists in that list. 67062306a36Sopenharmony_ci * NEVER change iface. it could be in use. 67162306a36Sopenharmony_ci * Add a new one instead 67262306a36Sopenharmony_ci */ 67362306a36Sopenharmony_ci spin_lock(&ses->iface_lock); 67462306a36Sopenharmony_ci list_for_each_entry_safe(iface, niface, &ses->iface_list, 67562306a36Sopenharmony_ci iface_head) { 67662306a36Sopenharmony_ci ret = iface_cmp(iface, &tmp_iface); 67762306a36Sopenharmony_ci if (!ret) { 67862306a36Sopenharmony_ci iface->is_active = 1; 67962306a36Sopenharmony_ci spin_unlock(&ses->iface_lock); 68062306a36Sopenharmony_ci goto next_iface; 68162306a36Sopenharmony_ci } else if (ret < 0) { 68262306a36Sopenharmony_ci /* all remaining ifaces are slower */ 68362306a36Sopenharmony_ci kref_get(&iface->refcount); 68462306a36Sopenharmony_ci break; 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci spin_unlock(&ses->iface_lock); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci /* no match. insert the entry in the list */ 69062306a36Sopenharmony_ci info = kmalloc(sizeof(struct cifs_server_iface), 69162306a36Sopenharmony_ci GFP_KERNEL); 69262306a36Sopenharmony_ci if (!info) { 69362306a36Sopenharmony_ci rc = -ENOMEM; 69462306a36Sopenharmony_ci goto out; 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci memcpy(info, &tmp_iface, sizeof(tmp_iface)); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci /* add this new entry to the list */ 69962306a36Sopenharmony_ci kref_init(&info->refcount); 70062306a36Sopenharmony_ci info->is_active = 1; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci cifs_dbg(FYI, "%s: adding iface %zu\n", __func__, ses->iface_count); 70362306a36Sopenharmony_ci cifs_dbg(FYI, "%s: speed %zu bps\n", __func__, info->speed); 70462306a36Sopenharmony_ci cifs_dbg(FYI, "%s: capabilities 0x%08x\n", __func__, 70562306a36Sopenharmony_ci le32_to_cpu(p->Capability)); 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci spin_lock(&ses->iface_lock); 70862306a36Sopenharmony_ci if (!list_entry_is_head(iface, &ses->iface_list, iface_head)) { 70962306a36Sopenharmony_ci list_add_tail(&info->iface_head, &iface->iface_head); 71062306a36Sopenharmony_ci kref_put(&iface->refcount, release_iface); 71162306a36Sopenharmony_ci } else 71262306a36Sopenharmony_ci list_add_tail(&info->iface_head, &ses->iface_list); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci ses->iface_count++; 71562306a36Sopenharmony_ci spin_unlock(&ses->iface_lock); 71662306a36Sopenharmony_cinext_iface: 71762306a36Sopenharmony_ci nb_iface++; 71862306a36Sopenharmony_ci next = le32_to_cpu(p->Next); 71962306a36Sopenharmony_ci if (!next) { 72062306a36Sopenharmony_ci bytes_left -= sizeof(*p); 72162306a36Sopenharmony_ci break; 72262306a36Sopenharmony_ci } 72362306a36Sopenharmony_ci p = (struct network_interface_info_ioctl_rsp *)((u8 *)p+next); 72462306a36Sopenharmony_ci bytes_left -= next; 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci if (!nb_iface) { 72862306a36Sopenharmony_ci cifs_dbg(VFS, "%s: malformed interface info\n", __func__); 72962306a36Sopenharmony_ci rc = -EINVAL; 73062306a36Sopenharmony_ci goto out; 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci /* Azure rounds the buffer size up 8, to a 16 byte boundary */ 73462306a36Sopenharmony_ci if ((bytes_left > 8) || p->Next) 73562306a36Sopenharmony_ci cifs_dbg(VFS, "%s: incomplete interface info\n", __func__); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci ses->iface_last_update = jiffies; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ciout: 74062306a36Sopenharmony_ci /* 74162306a36Sopenharmony_ci * Go through the list again and put the inactive entries 74262306a36Sopenharmony_ci */ 74362306a36Sopenharmony_ci spin_lock(&ses->iface_lock); 74462306a36Sopenharmony_ci list_for_each_entry_safe(iface, niface, &ses->iface_list, 74562306a36Sopenharmony_ci iface_head) { 74662306a36Sopenharmony_ci if (!iface->is_active) { 74762306a36Sopenharmony_ci list_del(&iface->iface_head); 74862306a36Sopenharmony_ci kref_put(&iface->refcount, release_iface); 74962306a36Sopenharmony_ci ses->iface_count--; 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci } 75262306a36Sopenharmony_ci spin_unlock(&ses->iface_lock); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci return rc; 75562306a36Sopenharmony_ci} 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ciint 75862306a36Sopenharmony_ciSMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon, bool in_mount) 75962306a36Sopenharmony_ci{ 76062306a36Sopenharmony_ci int rc; 76162306a36Sopenharmony_ci unsigned int ret_data_len = 0; 76262306a36Sopenharmony_ci struct network_interface_info_ioctl_rsp *out_buf = NULL; 76362306a36Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 76462306a36Sopenharmony_ci struct TCP_Server_Info *pserver; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci /* do not query too frequently */ 76762306a36Sopenharmony_ci if (ses->iface_last_update && 76862306a36Sopenharmony_ci time_before(jiffies, ses->iface_last_update + 76962306a36Sopenharmony_ci (SMB_INTERFACE_POLL_INTERVAL * HZ))) 77062306a36Sopenharmony_ci return 0; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, 77362306a36Sopenharmony_ci FSCTL_QUERY_NETWORK_INTERFACE_INFO, 77462306a36Sopenharmony_ci NULL /* no data input */, 0 /* no data input */, 77562306a36Sopenharmony_ci CIFSMaxBufSize, (char **)&out_buf, &ret_data_len); 77662306a36Sopenharmony_ci if (rc == -EOPNOTSUPP) { 77762306a36Sopenharmony_ci cifs_dbg(FYI, 77862306a36Sopenharmony_ci "server does not support query network interfaces\n"); 77962306a36Sopenharmony_ci ret_data_len = 0; 78062306a36Sopenharmony_ci } else if (rc != 0) { 78162306a36Sopenharmony_ci cifs_tcon_dbg(VFS, "error %d on ioctl to get interface list\n", rc); 78262306a36Sopenharmony_ci goto out; 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci rc = parse_server_interfaces(out_buf, ret_data_len, ses, in_mount); 78662306a36Sopenharmony_ci if (rc) 78762306a36Sopenharmony_ci goto out; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci /* check if iface is still active */ 79062306a36Sopenharmony_ci spin_lock(&ses->chan_lock); 79162306a36Sopenharmony_ci pserver = ses->chans[0].server; 79262306a36Sopenharmony_ci if (pserver && !cifs_chan_is_iface_active(ses, pserver)) { 79362306a36Sopenharmony_ci spin_unlock(&ses->chan_lock); 79462306a36Sopenharmony_ci cifs_chan_update_iface(ses, pserver); 79562306a36Sopenharmony_ci spin_lock(&ses->chan_lock); 79662306a36Sopenharmony_ci } 79762306a36Sopenharmony_ci spin_unlock(&ses->chan_lock); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ciout: 80062306a36Sopenharmony_ci kfree(out_buf); 80162306a36Sopenharmony_ci return rc; 80262306a36Sopenharmony_ci} 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_cistatic void 80562306a36Sopenharmony_cismb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon, 80662306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb) 80762306a36Sopenharmony_ci{ 80862306a36Sopenharmony_ci int rc; 80962306a36Sopenharmony_ci __le16 srch_path = 0; /* Null - open root of share */ 81062306a36Sopenharmony_ci u8 oplock = SMB2_OPLOCK_LEVEL_NONE; 81162306a36Sopenharmony_ci struct cifs_open_parms oparms; 81262306a36Sopenharmony_ci struct cifs_fid fid; 81362306a36Sopenharmony_ci struct cached_fid *cfid = NULL; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci oparms = (struct cifs_open_parms) { 81662306a36Sopenharmony_ci .tcon = tcon, 81762306a36Sopenharmony_ci .path = "", 81862306a36Sopenharmony_ci .desired_access = FILE_READ_ATTRIBUTES, 81962306a36Sopenharmony_ci .disposition = FILE_OPEN, 82062306a36Sopenharmony_ci .create_options = cifs_create_options(cifs_sb, 0), 82162306a36Sopenharmony_ci .fid = &fid, 82262306a36Sopenharmony_ci }; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci rc = open_cached_dir(xid, tcon, "", cifs_sb, false, &cfid); 82562306a36Sopenharmony_ci if (rc == 0) 82662306a36Sopenharmony_ci memcpy(&fid, &cfid->fid, sizeof(struct cifs_fid)); 82762306a36Sopenharmony_ci else 82862306a36Sopenharmony_ci rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL, 82962306a36Sopenharmony_ci NULL, NULL); 83062306a36Sopenharmony_ci if (rc) 83162306a36Sopenharmony_ci return; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci SMB3_request_interfaces(xid, tcon, true /* called during mount */); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, 83662306a36Sopenharmony_ci FS_ATTRIBUTE_INFORMATION); 83762306a36Sopenharmony_ci SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, 83862306a36Sopenharmony_ci FS_DEVICE_INFORMATION); 83962306a36Sopenharmony_ci SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, 84062306a36Sopenharmony_ci FS_VOLUME_INFORMATION); 84162306a36Sopenharmony_ci SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, 84262306a36Sopenharmony_ci FS_SECTOR_SIZE_INFORMATION); /* SMB3 specific */ 84362306a36Sopenharmony_ci if (cfid == NULL) 84462306a36Sopenharmony_ci SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); 84562306a36Sopenharmony_ci else 84662306a36Sopenharmony_ci close_cached_dir(cfid); 84762306a36Sopenharmony_ci} 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_cistatic void 85062306a36Sopenharmony_cismb2_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon, 85162306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb) 85262306a36Sopenharmony_ci{ 85362306a36Sopenharmony_ci int rc; 85462306a36Sopenharmony_ci __le16 srch_path = 0; /* Null - open root of share */ 85562306a36Sopenharmony_ci u8 oplock = SMB2_OPLOCK_LEVEL_NONE; 85662306a36Sopenharmony_ci struct cifs_open_parms oparms; 85762306a36Sopenharmony_ci struct cifs_fid fid; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci oparms = (struct cifs_open_parms) { 86062306a36Sopenharmony_ci .tcon = tcon, 86162306a36Sopenharmony_ci .path = "", 86262306a36Sopenharmony_ci .desired_access = FILE_READ_ATTRIBUTES, 86362306a36Sopenharmony_ci .disposition = FILE_OPEN, 86462306a36Sopenharmony_ci .create_options = cifs_create_options(cifs_sb, 0), 86562306a36Sopenharmony_ci .fid = &fid, 86662306a36Sopenharmony_ci }; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL, 86962306a36Sopenharmony_ci NULL, NULL); 87062306a36Sopenharmony_ci if (rc) 87162306a36Sopenharmony_ci return; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, 87462306a36Sopenharmony_ci FS_ATTRIBUTE_INFORMATION); 87562306a36Sopenharmony_ci SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, 87662306a36Sopenharmony_ci FS_DEVICE_INFORMATION); 87762306a36Sopenharmony_ci SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); 87862306a36Sopenharmony_ci} 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_cistatic int 88162306a36Sopenharmony_cismb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon, 88262306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb, const char *full_path) 88362306a36Sopenharmony_ci{ 88462306a36Sopenharmony_ci __le16 *utf16_path; 88562306a36Sopenharmony_ci __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; 88662306a36Sopenharmony_ci int err_buftype = CIFS_NO_BUFFER; 88762306a36Sopenharmony_ci struct cifs_open_parms oparms; 88862306a36Sopenharmony_ci struct kvec err_iov = {}; 88962306a36Sopenharmony_ci struct cifs_fid fid; 89062306a36Sopenharmony_ci struct cached_fid *cfid; 89162306a36Sopenharmony_ci bool islink; 89262306a36Sopenharmony_ci int rc, rc2; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci rc = open_cached_dir(xid, tcon, full_path, cifs_sb, true, &cfid); 89562306a36Sopenharmony_ci if (!rc) { 89662306a36Sopenharmony_ci if (cfid->has_lease) { 89762306a36Sopenharmony_ci close_cached_dir(cfid); 89862306a36Sopenharmony_ci return 0; 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci close_cached_dir(cfid); 90162306a36Sopenharmony_ci } 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); 90462306a36Sopenharmony_ci if (!utf16_path) 90562306a36Sopenharmony_ci return -ENOMEM; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci oparms = (struct cifs_open_parms) { 90862306a36Sopenharmony_ci .tcon = tcon, 90962306a36Sopenharmony_ci .path = full_path, 91062306a36Sopenharmony_ci .desired_access = FILE_READ_ATTRIBUTES, 91162306a36Sopenharmony_ci .disposition = FILE_OPEN, 91262306a36Sopenharmony_ci .create_options = cifs_create_options(cifs_sb, 0), 91362306a36Sopenharmony_ci .fid = &fid, 91462306a36Sopenharmony_ci }; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, 91762306a36Sopenharmony_ci &err_iov, &err_buftype); 91862306a36Sopenharmony_ci if (rc) { 91962306a36Sopenharmony_ci struct smb2_hdr *hdr = err_iov.iov_base; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci if (unlikely(!hdr || err_buftype == CIFS_NO_BUFFER)) 92262306a36Sopenharmony_ci goto out; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci if (rc != -EREMOTE && hdr->Status == STATUS_OBJECT_NAME_INVALID) { 92562306a36Sopenharmony_ci rc2 = cifs_inval_name_dfs_link_error(xid, tcon, cifs_sb, 92662306a36Sopenharmony_ci full_path, &islink); 92762306a36Sopenharmony_ci if (rc2) { 92862306a36Sopenharmony_ci rc = rc2; 92962306a36Sopenharmony_ci goto out; 93062306a36Sopenharmony_ci } 93162306a36Sopenharmony_ci if (islink) 93262306a36Sopenharmony_ci rc = -EREMOTE; 93362306a36Sopenharmony_ci } 93462306a36Sopenharmony_ci if (rc == -EREMOTE && IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) && cifs_sb && 93562306a36Sopenharmony_ci (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS)) 93662306a36Sopenharmony_ci rc = -EOPNOTSUPP; 93762306a36Sopenharmony_ci goto out; 93862306a36Sopenharmony_ci } 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci rc = SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ciout: 94362306a36Sopenharmony_ci free_rsp_buf(err_buftype, err_iov.iov_base); 94462306a36Sopenharmony_ci kfree(utf16_path); 94562306a36Sopenharmony_ci return rc; 94662306a36Sopenharmony_ci} 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_cistatic int smb2_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon, 94962306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb, const char *full_path, 95062306a36Sopenharmony_ci u64 *uniqueid, struct cifs_open_info_data *data) 95162306a36Sopenharmony_ci{ 95262306a36Sopenharmony_ci *uniqueid = le64_to_cpu(data->fi.IndexNumber); 95362306a36Sopenharmony_ci return 0; 95462306a36Sopenharmony_ci} 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_cistatic int smb2_query_file_info(const unsigned int xid, struct cifs_tcon *tcon, 95762306a36Sopenharmony_ci struct cifsFileInfo *cfile, struct cifs_open_info_data *data) 95862306a36Sopenharmony_ci{ 95962306a36Sopenharmony_ci struct cifs_fid *fid = &cfile->fid; 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci if (cfile->symlink_target) { 96262306a36Sopenharmony_ci data->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL); 96362306a36Sopenharmony_ci if (!data->symlink_target) 96462306a36Sopenharmony_ci return -ENOMEM; 96562306a36Sopenharmony_ci } 96662306a36Sopenharmony_ci return SMB2_query_info(xid, tcon, fid->persistent_fid, fid->volatile_fid, &data->fi); 96762306a36Sopenharmony_ci} 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci#ifdef CONFIG_CIFS_XATTR 97062306a36Sopenharmony_cistatic ssize_t 97162306a36Sopenharmony_cimove_smb2_ea_to_cifs(char *dst, size_t dst_size, 97262306a36Sopenharmony_ci struct smb2_file_full_ea_info *src, size_t src_size, 97362306a36Sopenharmony_ci const unsigned char *ea_name) 97462306a36Sopenharmony_ci{ 97562306a36Sopenharmony_ci int rc = 0; 97662306a36Sopenharmony_ci unsigned int ea_name_len = ea_name ? strlen(ea_name) : 0; 97762306a36Sopenharmony_ci char *name, *value; 97862306a36Sopenharmony_ci size_t buf_size = dst_size; 97962306a36Sopenharmony_ci size_t name_len, value_len, user_name_len; 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci while (src_size > 0) { 98262306a36Sopenharmony_ci name_len = (size_t)src->ea_name_length; 98362306a36Sopenharmony_ci value_len = (size_t)le16_to_cpu(src->ea_value_length); 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci if (name_len == 0) 98662306a36Sopenharmony_ci break; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci if (src_size < 8 + name_len + 1 + value_len) { 98962306a36Sopenharmony_ci cifs_dbg(FYI, "EA entry goes beyond length of list\n"); 99062306a36Sopenharmony_ci rc = -EIO; 99162306a36Sopenharmony_ci goto out; 99262306a36Sopenharmony_ci } 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci name = &src->ea_data[0]; 99562306a36Sopenharmony_ci value = &src->ea_data[src->ea_name_length + 1]; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci if (ea_name) { 99862306a36Sopenharmony_ci if (ea_name_len == name_len && 99962306a36Sopenharmony_ci memcmp(ea_name, name, name_len) == 0) { 100062306a36Sopenharmony_ci rc = value_len; 100162306a36Sopenharmony_ci if (dst_size == 0) 100262306a36Sopenharmony_ci goto out; 100362306a36Sopenharmony_ci if (dst_size < value_len) { 100462306a36Sopenharmony_ci rc = -ERANGE; 100562306a36Sopenharmony_ci goto out; 100662306a36Sopenharmony_ci } 100762306a36Sopenharmony_ci memcpy(dst, value, value_len); 100862306a36Sopenharmony_ci goto out; 100962306a36Sopenharmony_ci } 101062306a36Sopenharmony_ci } else { 101162306a36Sopenharmony_ci /* 'user.' plus a terminating null */ 101262306a36Sopenharmony_ci user_name_len = 5 + 1 + name_len; 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci if (buf_size == 0) { 101562306a36Sopenharmony_ci /* skip copy - calc size only */ 101662306a36Sopenharmony_ci rc += user_name_len; 101762306a36Sopenharmony_ci } else if (dst_size >= user_name_len) { 101862306a36Sopenharmony_ci dst_size -= user_name_len; 101962306a36Sopenharmony_ci memcpy(dst, "user.", 5); 102062306a36Sopenharmony_ci dst += 5; 102162306a36Sopenharmony_ci memcpy(dst, src->ea_data, name_len); 102262306a36Sopenharmony_ci dst += name_len; 102362306a36Sopenharmony_ci *dst = 0; 102462306a36Sopenharmony_ci ++dst; 102562306a36Sopenharmony_ci rc += user_name_len; 102662306a36Sopenharmony_ci } else { 102762306a36Sopenharmony_ci /* stop before overrun buffer */ 102862306a36Sopenharmony_ci rc = -ERANGE; 102962306a36Sopenharmony_ci break; 103062306a36Sopenharmony_ci } 103162306a36Sopenharmony_ci } 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci if (!src->next_entry_offset) 103462306a36Sopenharmony_ci break; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci if (src_size < le32_to_cpu(src->next_entry_offset)) { 103762306a36Sopenharmony_ci /* stop before overrun buffer */ 103862306a36Sopenharmony_ci rc = -ERANGE; 103962306a36Sopenharmony_ci break; 104062306a36Sopenharmony_ci } 104162306a36Sopenharmony_ci src_size -= le32_to_cpu(src->next_entry_offset); 104262306a36Sopenharmony_ci src = (void *)((char *)src + 104362306a36Sopenharmony_ci le32_to_cpu(src->next_entry_offset)); 104462306a36Sopenharmony_ci } 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci /* didn't find the named attribute */ 104762306a36Sopenharmony_ci if (ea_name) 104862306a36Sopenharmony_ci rc = -ENODATA; 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ciout: 105162306a36Sopenharmony_ci return (ssize_t)rc; 105262306a36Sopenharmony_ci} 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_cistatic ssize_t 105562306a36Sopenharmony_cismb2_query_eas(const unsigned int xid, struct cifs_tcon *tcon, 105662306a36Sopenharmony_ci const unsigned char *path, const unsigned char *ea_name, 105762306a36Sopenharmony_ci char *ea_data, size_t buf_size, 105862306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb) 105962306a36Sopenharmony_ci{ 106062306a36Sopenharmony_ci int rc; 106162306a36Sopenharmony_ci struct kvec rsp_iov = {NULL, 0}; 106262306a36Sopenharmony_ci int buftype = CIFS_NO_BUFFER; 106362306a36Sopenharmony_ci struct smb2_query_info_rsp *rsp; 106462306a36Sopenharmony_ci struct smb2_file_full_ea_info *info = NULL; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci rc = smb2_query_info_compound(xid, tcon, path, 106762306a36Sopenharmony_ci FILE_READ_EA, 106862306a36Sopenharmony_ci FILE_FULL_EA_INFORMATION, 106962306a36Sopenharmony_ci SMB2_O_INFO_FILE, 107062306a36Sopenharmony_ci CIFSMaxBufSize - 107162306a36Sopenharmony_ci MAX_SMB2_CREATE_RESPONSE_SIZE - 107262306a36Sopenharmony_ci MAX_SMB2_CLOSE_RESPONSE_SIZE, 107362306a36Sopenharmony_ci &rsp_iov, &buftype, cifs_sb); 107462306a36Sopenharmony_ci if (rc) { 107562306a36Sopenharmony_ci /* 107662306a36Sopenharmony_ci * If ea_name is NULL (listxattr) and there are no EAs, 107762306a36Sopenharmony_ci * return 0 as it's not an error. Otherwise, the specified 107862306a36Sopenharmony_ci * ea_name was not found. 107962306a36Sopenharmony_ci */ 108062306a36Sopenharmony_ci if (!ea_name && rc == -ENODATA) 108162306a36Sopenharmony_ci rc = 0; 108262306a36Sopenharmony_ci goto qeas_exit; 108362306a36Sopenharmony_ci } 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; 108662306a36Sopenharmony_ci rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), 108762306a36Sopenharmony_ci le32_to_cpu(rsp->OutputBufferLength), 108862306a36Sopenharmony_ci &rsp_iov, 108962306a36Sopenharmony_ci sizeof(struct smb2_file_full_ea_info)); 109062306a36Sopenharmony_ci if (rc) 109162306a36Sopenharmony_ci goto qeas_exit; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci info = (struct smb2_file_full_ea_info *)( 109462306a36Sopenharmony_ci le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp); 109562306a36Sopenharmony_ci rc = move_smb2_ea_to_cifs(ea_data, buf_size, info, 109662306a36Sopenharmony_ci le32_to_cpu(rsp->OutputBufferLength), ea_name); 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci qeas_exit: 109962306a36Sopenharmony_ci free_rsp_buf(buftype, rsp_iov.iov_base); 110062306a36Sopenharmony_ci return rc; 110162306a36Sopenharmony_ci} 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_cistatic int 110462306a36Sopenharmony_cismb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, 110562306a36Sopenharmony_ci const char *path, const char *ea_name, const void *ea_value, 110662306a36Sopenharmony_ci const __u16 ea_value_len, const struct nls_table *nls_codepage, 110762306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb) 110862306a36Sopenharmony_ci{ 110962306a36Sopenharmony_ci struct smb2_compound_vars *vars; 111062306a36Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 111162306a36Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(ses); 111262306a36Sopenharmony_ci struct smb_rqst *rqst; 111362306a36Sopenharmony_ci struct kvec *rsp_iov; 111462306a36Sopenharmony_ci __le16 *utf16_path = NULL; 111562306a36Sopenharmony_ci int ea_name_len = strlen(ea_name); 111662306a36Sopenharmony_ci int flags = CIFS_CP_CREATE_CLOSE_OP; 111762306a36Sopenharmony_ci int len; 111862306a36Sopenharmony_ci int resp_buftype[3]; 111962306a36Sopenharmony_ci struct cifs_open_parms oparms; 112062306a36Sopenharmony_ci __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; 112162306a36Sopenharmony_ci struct cifs_fid fid; 112262306a36Sopenharmony_ci unsigned int size[1]; 112362306a36Sopenharmony_ci void *data[1]; 112462306a36Sopenharmony_ci struct smb2_file_full_ea_info *ea = NULL; 112562306a36Sopenharmony_ci struct smb2_query_info_rsp *rsp; 112662306a36Sopenharmony_ci int rc, used_len = 0; 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci if (smb3_encryption_required(tcon)) 112962306a36Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci if (ea_name_len > 255) 113262306a36Sopenharmony_ci return -EINVAL; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); 113562306a36Sopenharmony_ci if (!utf16_path) 113662306a36Sopenharmony_ci return -ENOMEM; 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; 113962306a36Sopenharmony_ci vars = kzalloc(sizeof(*vars), GFP_KERNEL); 114062306a36Sopenharmony_ci if (!vars) { 114162306a36Sopenharmony_ci rc = -ENOMEM; 114262306a36Sopenharmony_ci goto out_free_path; 114362306a36Sopenharmony_ci } 114462306a36Sopenharmony_ci rqst = vars->rqst; 114562306a36Sopenharmony_ci rsp_iov = vars->rsp_iov; 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci if (ses->server->ops->query_all_EAs) { 114862306a36Sopenharmony_ci if (!ea_value) { 114962306a36Sopenharmony_ci rc = ses->server->ops->query_all_EAs(xid, tcon, path, 115062306a36Sopenharmony_ci ea_name, NULL, 0, 115162306a36Sopenharmony_ci cifs_sb); 115262306a36Sopenharmony_ci if (rc == -ENODATA) 115362306a36Sopenharmony_ci goto sea_exit; 115462306a36Sopenharmony_ci } else { 115562306a36Sopenharmony_ci /* If we are adding a attribute we should first check 115662306a36Sopenharmony_ci * if there will be enough space available to store 115762306a36Sopenharmony_ci * the new EA. If not we should not add it since we 115862306a36Sopenharmony_ci * would not be able to even read the EAs back. 115962306a36Sopenharmony_ci */ 116062306a36Sopenharmony_ci rc = smb2_query_info_compound(xid, tcon, path, 116162306a36Sopenharmony_ci FILE_READ_EA, 116262306a36Sopenharmony_ci FILE_FULL_EA_INFORMATION, 116362306a36Sopenharmony_ci SMB2_O_INFO_FILE, 116462306a36Sopenharmony_ci CIFSMaxBufSize - 116562306a36Sopenharmony_ci MAX_SMB2_CREATE_RESPONSE_SIZE - 116662306a36Sopenharmony_ci MAX_SMB2_CLOSE_RESPONSE_SIZE, 116762306a36Sopenharmony_ci &rsp_iov[1], &resp_buftype[1], cifs_sb); 116862306a36Sopenharmony_ci if (rc == 0) { 116962306a36Sopenharmony_ci rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base; 117062306a36Sopenharmony_ci used_len = le32_to_cpu(rsp->OutputBufferLength); 117162306a36Sopenharmony_ci } 117262306a36Sopenharmony_ci free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); 117362306a36Sopenharmony_ci resp_buftype[1] = CIFS_NO_BUFFER; 117462306a36Sopenharmony_ci memset(&rsp_iov[1], 0, sizeof(rsp_iov[1])); 117562306a36Sopenharmony_ci rc = 0; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci /* Use a fudge factor of 256 bytes in case we collide 117862306a36Sopenharmony_ci * with a different set_EAs command. 117962306a36Sopenharmony_ci */ 118062306a36Sopenharmony_ci if (CIFSMaxBufSize - MAX_SMB2_CREATE_RESPONSE_SIZE - 118162306a36Sopenharmony_ci MAX_SMB2_CLOSE_RESPONSE_SIZE - 256 < 118262306a36Sopenharmony_ci used_len + ea_name_len + ea_value_len + 1) { 118362306a36Sopenharmony_ci rc = -ENOSPC; 118462306a36Sopenharmony_ci goto sea_exit; 118562306a36Sopenharmony_ci } 118662306a36Sopenharmony_ci } 118762306a36Sopenharmony_ci } 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci /* Open */ 119062306a36Sopenharmony_ci rqst[0].rq_iov = vars->open_iov; 119162306a36Sopenharmony_ci rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci oparms = (struct cifs_open_parms) { 119462306a36Sopenharmony_ci .tcon = tcon, 119562306a36Sopenharmony_ci .path = path, 119662306a36Sopenharmony_ci .desired_access = FILE_WRITE_EA, 119762306a36Sopenharmony_ci .disposition = FILE_OPEN, 119862306a36Sopenharmony_ci .create_options = cifs_create_options(cifs_sb, 0), 119962306a36Sopenharmony_ci .fid = &fid, 120062306a36Sopenharmony_ci }; 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci rc = SMB2_open_init(tcon, server, 120362306a36Sopenharmony_ci &rqst[0], &oplock, &oparms, utf16_path); 120462306a36Sopenharmony_ci if (rc) 120562306a36Sopenharmony_ci goto sea_exit; 120662306a36Sopenharmony_ci smb2_set_next_command(tcon, &rqst[0]); 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci /* Set Info */ 121062306a36Sopenharmony_ci rqst[1].rq_iov = vars->si_iov; 121162306a36Sopenharmony_ci rqst[1].rq_nvec = 1; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci len = sizeof(*ea) + ea_name_len + ea_value_len + 1; 121462306a36Sopenharmony_ci ea = kzalloc(len, GFP_KERNEL); 121562306a36Sopenharmony_ci if (ea == NULL) { 121662306a36Sopenharmony_ci rc = -ENOMEM; 121762306a36Sopenharmony_ci goto sea_exit; 121862306a36Sopenharmony_ci } 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci ea->ea_name_length = ea_name_len; 122162306a36Sopenharmony_ci ea->ea_value_length = cpu_to_le16(ea_value_len); 122262306a36Sopenharmony_ci memcpy(ea->ea_data, ea_name, ea_name_len + 1); 122362306a36Sopenharmony_ci memcpy(ea->ea_data + ea_name_len + 1, ea_value, ea_value_len); 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci size[0] = len; 122662306a36Sopenharmony_ci data[0] = ea; 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci rc = SMB2_set_info_init(tcon, server, 122962306a36Sopenharmony_ci &rqst[1], COMPOUND_FID, 123062306a36Sopenharmony_ci COMPOUND_FID, current->tgid, 123162306a36Sopenharmony_ci FILE_FULL_EA_INFORMATION, 123262306a36Sopenharmony_ci SMB2_O_INFO_FILE, 0, data, size); 123362306a36Sopenharmony_ci if (rc) 123462306a36Sopenharmony_ci goto sea_exit; 123562306a36Sopenharmony_ci smb2_set_next_command(tcon, &rqst[1]); 123662306a36Sopenharmony_ci smb2_set_related(&rqst[1]); 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci /* Close */ 123962306a36Sopenharmony_ci rqst[2].rq_iov = &vars->close_iov; 124062306a36Sopenharmony_ci rqst[2].rq_nvec = 1; 124162306a36Sopenharmony_ci rc = SMB2_close_init(tcon, server, 124262306a36Sopenharmony_ci &rqst[2], COMPOUND_FID, COMPOUND_FID, false); 124362306a36Sopenharmony_ci if (rc) 124462306a36Sopenharmony_ci goto sea_exit; 124562306a36Sopenharmony_ci smb2_set_related(&rqst[2]); 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci rc = compound_send_recv(xid, ses, server, 124862306a36Sopenharmony_ci flags, 3, rqst, 124962306a36Sopenharmony_ci resp_buftype, rsp_iov); 125062306a36Sopenharmony_ci /* no need to bump num_remote_opens because handle immediately closed */ 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci sea_exit: 125362306a36Sopenharmony_ci kfree(ea); 125462306a36Sopenharmony_ci SMB2_open_free(&rqst[0]); 125562306a36Sopenharmony_ci SMB2_set_info_free(&rqst[1]); 125662306a36Sopenharmony_ci SMB2_close_free(&rqst[2]); 125762306a36Sopenharmony_ci free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); 125862306a36Sopenharmony_ci free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); 125962306a36Sopenharmony_ci free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); 126062306a36Sopenharmony_ci kfree(vars); 126162306a36Sopenharmony_ciout_free_path: 126262306a36Sopenharmony_ci kfree(utf16_path); 126362306a36Sopenharmony_ci return rc; 126462306a36Sopenharmony_ci} 126562306a36Sopenharmony_ci#endif 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_cistatic bool 126862306a36Sopenharmony_cismb2_can_echo(struct TCP_Server_Info *server) 126962306a36Sopenharmony_ci{ 127062306a36Sopenharmony_ci return server->echoes; 127162306a36Sopenharmony_ci} 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_cistatic void 127462306a36Sopenharmony_cismb2_clear_stats(struct cifs_tcon *tcon) 127562306a36Sopenharmony_ci{ 127662306a36Sopenharmony_ci int i; 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci for (i = 0; i < NUMBER_OF_SMB2_COMMANDS; i++) { 127962306a36Sopenharmony_ci atomic_set(&tcon->stats.smb2_stats.smb2_com_sent[i], 0); 128062306a36Sopenharmony_ci atomic_set(&tcon->stats.smb2_stats.smb2_com_failed[i], 0); 128162306a36Sopenharmony_ci } 128262306a36Sopenharmony_ci} 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_cistatic void 128562306a36Sopenharmony_cismb2_dump_share_caps(struct seq_file *m, struct cifs_tcon *tcon) 128662306a36Sopenharmony_ci{ 128762306a36Sopenharmony_ci seq_puts(m, "\n\tShare Capabilities:"); 128862306a36Sopenharmony_ci if (tcon->capabilities & SMB2_SHARE_CAP_DFS) 128962306a36Sopenharmony_ci seq_puts(m, " DFS,"); 129062306a36Sopenharmony_ci if (tcon->capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY) 129162306a36Sopenharmony_ci seq_puts(m, " CONTINUOUS AVAILABILITY,"); 129262306a36Sopenharmony_ci if (tcon->capabilities & SMB2_SHARE_CAP_SCALEOUT) 129362306a36Sopenharmony_ci seq_puts(m, " SCALEOUT,"); 129462306a36Sopenharmony_ci if (tcon->capabilities & SMB2_SHARE_CAP_CLUSTER) 129562306a36Sopenharmony_ci seq_puts(m, " CLUSTER,"); 129662306a36Sopenharmony_ci if (tcon->capabilities & SMB2_SHARE_CAP_ASYMMETRIC) 129762306a36Sopenharmony_ci seq_puts(m, " ASYMMETRIC,"); 129862306a36Sopenharmony_ci if (tcon->capabilities == 0) 129962306a36Sopenharmony_ci seq_puts(m, " None"); 130062306a36Sopenharmony_ci if (tcon->ss_flags & SSINFO_FLAGS_ALIGNED_DEVICE) 130162306a36Sopenharmony_ci seq_puts(m, " Aligned,"); 130262306a36Sopenharmony_ci if (tcon->ss_flags & SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE) 130362306a36Sopenharmony_ci seq_puts(m, " Partition Aligned,"); 130462306a36Sopenharmony_ci if (tcon->ss_flags & SSINFO_FLAGS_NO_SEEK_PENALTY) 130562306a36Sopenharmony_ci seq_puts(m, " SSD,"); 130662306a36Sopenharmony_ci if (tcon->ss_flags & SSINFO_FLAGS_TRIM_ENABLED) 130762306a36Sopenharmony_ci seq_puts(m, " TRIM-support,"); 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci seq_printf(m, "\tShare Flags: 0x%x", tcon->share_flags); 131062306a36Sopenharmony_ci seq_printf(m, "\n\ttid: 0x%x", tcon->tid); 131162306a36Sopenharmony_ci if (tcon->perf_sector_size) 131262306a36Sopenharmony_ci seq_printf(m, "\tOptimal sector size: 0x%x", 131362306a36Sopenharmony_ci tcon->perf_sector_size); 131462306a36Sopenharmony_ci seq_printf(m, "\tMaximal Access: 0x%x", tcon->maximal_access); 131562306a36Sopenharmony_ci} 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_cistatic void 131862306a36Sopenharmony_cismb2_print_stats(struct seq_file *m, struct cifs_tcon *tcon) 131962306a36Sopenharmony_ci{ 132062306a36Sopenharmony_ci atomic_t *sent = tcon->stats.smb2_stats.smb2_com_sent; 132162306a36Sopenharmony_ci atomic_t *failed = tcon->stats.smb2_stats.smb2_com_failed; 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci /* 132462306a36Sopenharmony_ci * Can't display SMB2_NEGOTIATE, SESSION_SETUP, LOGOFF, CANCEL and ECHO 132562306a36Sopenharmony_ci * totals (requests sent) since those SMBs are per-session not per tcon 132662306a36Sopenharmony_ci */ 132762306a36Sopenharmony_ci seq_printf(m, "\nBytes read: %llu Bytes written: %llu", 132862306a36Sopenharmony_ci (long long)(tcon->bytes_read), 132962306a36Sopenharmony_ci (long long)(tcon->bytes_written)); 133062306a36Sopenharmony_ci seq_printf(m, "\nOpen files: %d total (local), %d open on server", 133162306a36Sopenharmony_ci atomic_read(&tcon->num_local_opens), 133262306a36Sopenharmony_ci atomic_read(&tcon->num_remote_opens)); 133362306a36Sopenharmony_ci seq_printf(m, "\nTreeConnects: %d total %d failed", 133462306a36Sopenharmony_ci atomic_read(&sent[SMB2_TREE_CONNECT_HE]), 133562306a36Sopenharmony_ci atomic_read(&failed[SMB2_TREE_CONNECT_HE])); 133662306a36Sopenharmony_ci seq_printf(m, "\nTreeDisconnects: %d total %d failed", 133762306a36Sopenharmony_ci atomic_read(&sent[SMB2_TREE_DISCONNECT_HE]), 133862306a36Sopenharmony_ci atomic_read(&failed[SMB2_TREE_DISCONNECT_HE])); 133962306a36Sopenharmony_ci seq_printf(m, "\nCreates: %d total %d failed", 134062306a36Sopenharmony_ci atomic_read(&sent[SMB2_CREATE_HE]), 134162306a36Sopenharmony_ci atomic_read(&failed[SMB2_CREATE_HE])); 134262306a36Sopenharmony_ci seq_printf(m, "\nCloses: %d total %d failed", 134362306a36Sopenharmony_ci atomic_read(&sent[SMB2_CLOSE_HE]), 134462306a36Sopenharmony_ci atomic_read(&failed[SMB2_CLOSE_HE])); 134562306a36Sopenharmony_ci seq_printf(m, "\nFlushes: %d total %d failed", 134662306a36Sopenharmony_ci atomic_read(&sent[SMB2_FLUSH_HE]), 134762306a36Sopenharmony_ci atomic_read(&failed[SMB2_FLUSH_HE])); 134862306a36Sopenharmony_ci seq_printf(m, "\nReads: %d total %d failed", 134962306a36Sopenharmony_ci atomic_read(&sent[SMB2_READ_HE]), 135062306a36Sopenharmony_ci atomic_read(&failed[SMB2_READ_HE])); 135162306a36Sopenharmony_ci seq_printf(m, "\nWrites: %d total %d failed", 135262306a36Sopenharmony_ci atomic_read(&sent[SMB2_WRITE_HE]), 135362306a36Sopenharmony_ci atomic_read(&failed[SMB2_WRITE_HE])); 135462306a36Sopenharmony_ci seq_printf(m, "\nLocks: %d total %d failed", 135562306a36Sopenharmony_ci atomic_read(&sent[SMB2_LOCK_HE]), 135662306a36Sopenharmony_ci atomic_read(&failed[SMB2_LOCK_HE])); 135762306a36Sopenharmony_ci seq_printf(m, "\nIOCTLs: %d total %d failed", 135862306a36Sopenharmony_ci atomic_read(&sent[SMB2_IOCTL_HE]), 135962306a36Sopenharmony_ci atomic_read(&failed[SMB2_IOCTL_HE])); 136062306a36Sopenharmony_ci seq_printf(m, "\nQueryDirectories: %d total %d failed", 136162306a36Sopenharmony_ci atomic_read(&sent[SMB2_QUERY_DIRECTORY_HE]), 136262306a36Sopenharmony_ci atomic_read(&failed[SMB2_QUERY_DIRECTORY_HE])); 136362306a36Sopenharmony_ci seq_printf(m, "\nChangeNotifies: %d total %d failed", 136462306a36Sopenharmony_ci atomic_read(&sent[SMB2_CHANGE_NOTIFY_HE]), 136562306a36Sopenharmony_ci atomic_read(&failed[SMB2_CHANGE_NOTIFY_HE])); 136662306a36Sopenharmony_ci seq_printf(m, "\nQueryInfos: %d total %d failed", 136762306a36Sopenharmony_ci atomic_read(&sent[SMB2_QUERY_INFO_HE]), 136862306a36Sopenharmony_ci atomic_read(&failed[SMB2_QUERY_INFO_HE])); 136962306a36Sopenharmony_ci seq_printf(m, "\nSetInfos: %d total %d failed", 137062306a36Sopenharmony_ci atomic_read(&sent[SMB2_SET_INFO_HE]), 137162306a36Sopenharmony_ci atomic_read(&failed[SMB2_SET_INFO_HE])); 137262306a36Sopenharmony_ci seq_printf(m, "\nOplockBreaks: %d sent %d failed", 137362306a36Sopenharmony_ci atomic_read(&sent[SMB2_OPLOCK_BREAK_HE]), 137462306a36Sopenharmony_ci atomic_read(&failed[SMB2_OPLOCK_BREAK_HE])); 137562306a36Sopenharmony_ci} 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_cistatic void 137862306a36Sopenharmony_cismb2_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock) 137962306a36Sopenharmony_ci{ 138062306a36Sopenharmony_ci struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); 138162306a36Sopenharmony_ci struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server; 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci cfile->fid.persistent_fid = fid->persistent_fid; 138462306a36Sopenharmony_ci cfile->fid.volatile_fid = fid->volatile_fid; 138562306a36Sopenharmony_ci cfile->fid.access = fid->access; 138662306a36Sopenharmony_ci#ifdef CONFIG_CIFS_DEBUG2 138762306a36Sopenharmony_ci cfile->fid.mid = fid->mid; 138862306a36Sopenharmony_ci#endif /* CIFS_DEBUG2 */ 138962306a36Sopenharmony_ci server->ops->set_oplock_level(cinode, oplock, fid->epoch, 139062306a36Sopenharmony_ci &fid->purge_cache); 139162306a36Sopenharmony_ci cinode->can_cache_brlcks = CIFS_CACHE_WRITE(cinode); 139262306a36Sopenharmony_ci memcpy(cfile->fid.create_guid, fid->create_guid, 16); 139362306a36Sopenharmony_ci} 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_cistatic void 139662306a36Sopenharmony_cismb2_close_file(const unsigned int xid, struct cifs_tcon *tcon, 139762306a36Sopenharmony_ci struct cifs_fid *fid) 139862306a36Sopenharmony_ci{ 139962306a36Sopenharmony_ci SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid); 140062306a36Sopenharmony_ci} 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_cistatic void 140362306a36Sopenharmony_cismb2_close_getattr(const unsigned int xid, struct cifs_tcon *tcon, 140462306a36Sopenharmony_ci struct cifsFileInfo *cfile) 140562306a36Sopenharmony_ci{ 140662306a36Sopenharmony_ci struct smb2_file_network_open_info file_inf; 140762306a36Sopenharmony_ci struct inode *inode; 140862306a36Sopenharmony_ci int rc; 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci rc = __SMB2_close(xid, tcon, cfile->fid.persistent_fid, 141162306a36Sopenharmony_ci cfile->fid.volatile_fid, &file_inf); 141262306a36Sopenharmony_ci if (rc) 141362306a36Sopenharmony_ci return; 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci inode = d_inode(cfile->dentry); 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci spin_lock(&inode->i_lock); 141862306a36Sopenharmony_ci CIFS_I(inode)->time = jiffies; 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci /* Creation time should not need to be updated on close */ 142162306a36Sopenharmony_ci if (file_inf.LastWriteTime) 142262306a36Sopenharmony_ci inode_set_mtime_to_ts(inode, 142362306a36Sopenharmony_ci cifs_NTtimeToUnix(file_inf.LastWriteTime)); 142462306a36Sopenharmony_ci if (file_inf.ChangeTime) 142562306a36Sopenharmony_ci inode_set_ctime_to_ts(inode, 142662306a36Sopenharmony_ci cifs_NTtimeToUnix(file_inf.ChangeTime)); 142762306a36Sopenharmony_ci if (file_inf.LastAccessTime) 142862306a36Sopenharmony_ci inode_set_atime_to_ts(inode, 142962306a36Sopenharmony_ci cifs_NTtimeToUnix(file_inf.LastAccessTime)); 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci /* 143262306a36Sopenharmony_ci * i_blocks is not related to (i_size / i_blksize), 143362306a36Sopenharmony_ci * but instead 512 byte (2**9) size is required for 143462306a36Sopenharmony_ci * calculating num blocks. 143562306a36Sopenharmony_ci */ 143662306a36Sopenharmony_ci if (le64_to_cpu(file_inf.AllocationSize) > 4096) 143762306a36Sopenharmony_ci inode->i_blocks = 143862306a36Sopenharmony_ci (512 - 1 + le64_to_cpu(file_inf.AllocationSize)) >> 9; 143962306a36Sopenharmony_ci 144062306a36Sopenharmony_ci /* End of file and Attributes should not have to be updated on close */ 144162306a36Sopenharmony_ci spin_unlock(&inode->i_lock); 144262306a36Sopenharmony_ci} 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_cistatic int 144562306a36Sopenharmony_ciSMB2_request_res_key(const unsigned int xid, struct cifs_tcon *tcon, 144662306a36Sopenharmony_ci u64 persistent_fid, u64 volatile_fid, 144762306a36Sopenharmony_ci struct copychunk_ioctl *pcchunk) 144862306a36Sopenharmony_ci{ 144962306a36Sopenharmony_ci int rc; 145062306a36Sopenharmony_ci unsigned int ret_data_len; 145162306a36Sopenharmony_ci struct resume_key_req *res_key; 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid, 145462306a36Sopenharmony_ci FSCTL_SRV_REQUEST_RESUME_KEY, NULL, 0 /* no input */, 145562306a36Sopenharmony_ci CIFSMaxBufSize, (char **)&res_key, &ret_data_len); 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci if (rc == -EOPNOTSUPP) { 145862306a36Sopenharmony_ci pr_warn_once("Server share %s does not support copy range\n", tcon->tree_name); 145962306a36Sopenharmony_ci goto req_res_key_exit; 146062306a36Sopenharmony_ci } else if (rc) { 146162306a36Sopenharmony_ci cifs_tcon_dbg(VFS, "refcpy ioctl error %d getting resume key\n", rc); 146262306a36Sopenharmony_ci goto req_res_key_exit; 146362306a36Sopenharmony_ci } 146462306a36Sopenharmony_ci if (ret_data_len < sizeof(struct resume_key_req)) { 146562306a36Sopenharmony_ci cifs_tcon_dbg(VFS, "Invalid refcopy resume key length\n"); 146662306a36Sopenharmony_ci rc = -EINVAL; 146762306a36Sopenharmony_ci goto req_res_key_exit; 146862306a36Sopenharmony_ci } 146962306a36Sopenharmony_ci memcpy(pcchunk->SourceKey, res_key->ResumeKey, COPY_CHUNK_RES_KEY_SIZE); 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_cireq_res_key_exit: 147262306a36Sopenharmony_ci kfree(res_key); 147362306a36Sopenharmony_ci return rc; 147462306a36Sopenharmony_ci} 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_cistatic int 147762306a36Sopenharmony_cismb2_ioctl_query_info(const unsigned int xid, 147862306a36Sopenharmony_ci struct cifs_tcon *tcon, 147962306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb, 148062306a36Sopenharmony_ci __le16 *path, int is_dir, 148162306a36Sopenharmony_ci unsigned long p) 148262306a36Sopenharmony_ci{ 148362306a36Sopenharmony_ci struct smb2_compound_vars *vars; 148462306a36Sopenharmony_ci struct smb_rqst *rqst; 148562306a36Sopenharmony_ci struct kvec *rsp_iov; 148662306a36Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 148762306a36Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(ses); 148862306a36Sopenharmony_ci char __user *arg = (char __user *)p; 148962306a36Sopenharmony_ci struct smb_query_info qi; 149062306a36Sopenharmony_ci struct smb_query_info __user *pqi; 149162306a36Sopenharmony_ci int rc = 0; 149262306a36Sopenharmony_ci int flags = CIFS_CP_CREATE_CLOSE_OP; 149362306a36Sopenharmony_ci struct smb2_query_info_rsp *qi_rsp = NULL; 149462306a36Sopenharmony_ci struct smb2_ioctl_rsp *io_rsp = NULL; 149562306a36Sopenharmony_ci void *buffer = NULL; 149662306a36Sopenharmony_ci int resp_buftype[3]; 149762306a36Sopenharmony_ci struct cifs_open_parms oparms; 149862306a36Sopenharmony_ci u8 oplock = SMB2_OPLOCK_LEVEL_NONE; 149962306a36Sopenharmony_ci struct cifs_fid fid; 150062306a36Sopenharmony_ci unsigned int size[2]; 150162306a36Sopenharmony_ci void *data[2]; 150262306a36Sopenharmony_ci int create_options = is_dir ? CREATE_NOT_FILE : CREATE_NOT_DIR; 150362306a36Sopenharmony_ci void (*free_req1_func)(struct smb_rqst *r); 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci vars = kzalloc(sizeof(*vars), GFP_ATOMIC); 150662306a36Sopenharmony_ci if (vars == NULL) 150762306a36Sopenharmony_ci return -ENOMEM; 150862306a36Sopenharmony_ci rqst = &vars->rqst[0]; 150962306a36Sopenharmony_ci rsp_iov = &vars->rsp_iov[0]; 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_ci resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci if (copy_from_user(&qi, arg, sizeof(struct smb_query_info))) { 151462306a36Sopenharmony_ci rc = -EFAULT; 151562306a36Sopenharmony_ci goto free_vars; 151662306a36Sopenharmony_ci } 151762306a36Sopenharmony_ci if (qi.output_buffer_length > 1024) { 151862306a36Sopenharmony_ci rc = -EINVAL; 151962306a36Sopenharmony_ci goto free_vars; 152062306a36Sopenharmony_ci } 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci if (!ses || !server) { 152362306a36Sopenharmony_ci rc = -EIO; 152462306a36Sopenharmony_ci goto free_vars; 152562306a36Sopenharmony_ci } 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci if (smb3_encryption_required(tcon)) 152862306a36Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci if (qi.output_buffer_length) { 153162306a36Sopenharmony_ci buffer = memdup_user(arg + sizeof(struct smb_query_info), qi.output_buffer_length); 153262306a36Sopenharmony_ci if (IS_ERR(buffer)) { 153362306a36Sopenharmony_ci rc = PTR_ERR(buffer); 153462306a36Sopenharmony_ci goto free_vars; 153562306a36Sopenharmony_ci } 153662306a36Sopenharmony_ci } 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci /* Open */ 153962306a36Sopenharmony_ci rqst[0].rq_iov = &vars->open_iov[0]; 154062306a36Sopenharmony_ci rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ci oparms = (struct cifs_open_parms) { 154362306a36Sopenharmony_ci .tcon = tcon, 154462306a36Sopenharmony_ci .disposition = FILE_OPEN, 154562306a36Sopenharmony_ci .create_options = cifs_create_options(cifs_sb, create_options), 154662306a36Sopenharmony_ci .fid = &fid, 154762306a36Sopenharmony_ci }; 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_ci if (qi.flags & PASSTHRU_FSCTL) { 155062306a36Sopenharmony_ci switch (qi.info_type & FSCTL_DEVICE_ACCESS_MASK) { 155162306a36Sopenharmony_ci case FSCTL_DEVICE_ACCESS_FILE_READ_WRITE_ACCESS: 155262306a36Sopenharmony_ci oparms.desired_access = FILE_READ_DATA | FILE_WRITE_DATA | FILE_READ_ATTRIBUTES | SYNCHRONIZE; 155362306a36Sopenharmony_ci break; 155462306a36Sopenharmony_ci case FSCTL_DEVICE_ACCESS_FILE_ANY_ACCESS: 155562306a36Sopenharmony_ci oparms.desired_access = GENERIC_ALL; 155662306a36Sopenharmony_ci break; 155762306a36Sopenharmony_ci case FSCTL_DEVICE_ACCESS_FILE_READ_ACCESS: 155862306a36Sopenharmony_ci oparms.desired_access = GENERIC_READ; 155962306a36Sopenharmony_ci break; 156062306a36Sopenharmony_ci case FSCTL_DEVICE_ACCESS_FILE_WRITE_ACCESS: 156162306a36Sopenharmony_ci oparms.desired_access = GENERIC_WRITE; 156262306a36Sopenharmony_ci break; 156362306a36Sopenharmony_ci } 156462306a36Sopenharmony_ci } else if (qi.flags & PASSTHRU_SET_INFO) { 156562306a36Sopenharmony_ci oparms.desired_access = GENERIC_WRITE; 156662306a36Sopenharmony_ci } else { 156762306a36Sopenharmony_ci oparms.desired_access = FILE_READ_ATTRIBUTES | READ_CONTROL; 156862306a36Sopenharmony_ci } 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci rc = SMB2_open_init(tcon, server, 157162306a36Sopenharmony_ci &rqst[0], &oplock, &oparms, path); 157262306a36Sopenharmony_ci if (rc) 157362306a36Sopenharmony_ci goto free_output_buffer; 157462306a36Sopenharmony_ci smb2_set_next_command(tcon, &rqst[0]); 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci /* Query */ 157762306a36Sopenharmony_ci if (qi.flags & PASSTHRU_FSCTL) { 157862306a36Sopenharmony_ci /* Can eventually relax perm check since server enforces too */ 157962306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) { 158062306a36Sopenharmony_ci rc = -EPERM; 158162306a36Sopenharmony_ci goto free_open_req; 158262306a36Sopenharmony_ci } 158362306a36Sopenharmony_ci rqst[1].rq_iov = &vars->io_iov[0]; 158462306a36Sopenharmony_ci rqst[1].rq_nvec = SMB2_IOCTL_IOV_SIZE; 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci rc = SMB2_ioctl_init(tcon, server, &rqst[1], COMPOUND_FID, COMPOUND_FID, 158762306a36Sopenharmony_ci qi.info_type, buffer, qi.output_buffer_length, 158862306a36Sopenharmony_ci CIFSMaxBufSize - MAX_SMB2_CREATE_RESPONSE_SIZE - 158962306a36Sopenharmony_ci MAX_SMB2_CLOSE_RESPONSE_SIZE); 159062306a36Sopenharmony_ci free_req1_func = SMB2_ioctl_free; 159162306a36Sopenharmony_ci } else if (qi.flags == PASSTHRU_SET_INFO) { 159262306a36Sopenharmony_ci /* Can eventually relax perm check since server enforces too */ 159362306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) { 159462306a36Sopenharmony_ci rc = -EPERM; 159562306a36Sopenharmony_ci goto free_open_req; 159662306a36Sopenharmony_ci } 159762306a36Sopenharmony_ci if (qi.output_buffer_length < 8) { 159862306a36Sopenharmony_ci rc = -EINVAL; 159962306a36Sopenharmony_ci goto free_open_req; 160062306a36Sopenharmony_ci } 160162306a36Sopenharmony_ci rqst[1].rq_iov = vars->si_iov; 160262306a36Sopenharmony_ci rqst[1].rq_nvec = 1; 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci /* MS-FSCC 2.4.13 FileEndOfFileInformation */ 160562306a36Sopenharmony_ci size[0] = 8; 160662306a36Sopenharmony_ci data[0] = buffer; 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci rc = SMB2_set_info_init(tcon, server, &rqst[1], COMPOUND_FID, COMPOUND_FID, 160962306a36Sopenharmony_ci current->tgid, FILE_END_OF_FILE_INFORMATION, 161062306a36Sopenharmony_ci SMB2_O_INFO_FILE, 0, data, size); 161162306a36Sopenharmony_ci free_req1_func = SMB2_set_info_free; 161262306a36Sopenharmony_ci } else if (qi.flags == PASSTHRU_QUERY_INFO) { 161362306a36Sopenharmony_ci rqst[1].rq_iov = &vars->qi_iov; 161462306a36Sopenharmony_ci rqst[1].rq_nvec = 1; 161562306a36Sopenharmony_ci 161662306a36Sopenharmony_ci rc = SMB2_query_info_init(tcon, server, 161762306a36Sopenharmony_ci &rqst[1], COMPOUND_FID, 161862306a36Sopenharmony_ci COMPOUND_FID, qi.file_info_class, 161962306a36Sopenharmony_ci qi.info_type, qi.additional_information, 162062306a36Sopenharmony_ci qi.input_buffer_length, 162162306a36Sopenharmony_ci qi.output_buffer_length, buffer); 162262306a36Sopenharmony_ci free_req1_func = SMB2_query_info_free; 162362306a36Sopenharmony_ci } else { /* unknown flags */ 162462306a36Sopenharmony_ci cifs_tcon_dbg(VFS, "Invalid passthru query flags: 0x%x\n", 162562306a36Sopenharmony_ci qi.flags); 162662306a36Sopenharmony_ci rc = -EINVAL; 162762306a36Sopenharmony_ci } 162862306a36Sopenharmony_ci 162962306a36Sopenharmony_ci if (rc) 163062306a36Sopenharmony_ci goto free_open_req; 163162306a36Sopenharmony_ci smb2_set_next_command(tcon, &rqst[1]); 163262306a36Sopenharmony_ci smb2_set_related(&rqst[1]); 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ci /* Close */ 163562306a36Sopenharmony_ci rqst[2].rq_iov = &vars->close_iov; 163662306a36Sopenharmony_ci rqst[2].rq_nvec = 1; 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci rc = SMB2_close_init(tcon, server, 163962306a36Sopenharmony_ci &rqst[2], COMPOUND_FID, COMPOUND_FID, false); 164062306a36Sopenharmony_ci if (rc) 164162306a36Sopenharmony_ci goto free_req_1; 164262306a36Sopenharmony_ci smb2_set_related(&rqst[2]); 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci rc = compound_send_recv(xid, ses, server, 164562306a36Sopenharmony_ci flags, 3, rqst, 164662306a36Sopenharmony_ci resp_buftype, rsp_iov); 164762306a36Sopenharmony_ci if (rc) 164862306a36Sopenharmony_ci goto out; 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci /* No need to bump num_remote_opens since handle immediately closed */ 165162306a36Sopenharmony_ci if (qi.flags & PASSTHRU_FSCTL) { 165262306a36Sopenharmony_ci pqi = (struct smb_query_info __user *)arg; 165362306a36Sopenharmony_ci io_rsp = (struct smb2_ioctl_rsp *)rsp_iov[1].iov_base; 165462306a36Sopenharmony_ci if (le32_to_cpu(io_rsp->OutputCount) < qi.input_buffer_length) 165562306a36Sopenharmony_ci qi.input_buffer_length = le32_to_cpu(io_rsp->OutputCount); 165662306a36Sopenharmony_ci if (qi.input_buffer_length > 0 && 165762306a36Sopenharmony_ci le32_to_cpu(io_rsp->OutputOffset) + qi.input_buffer_length 165862306a36Sopenharmony_ci > rsp_iov[1].iov_len) { 165962306a36Sopenharmony_ci rc = -EFAULT; 166062306a36Sopenharmony_ci goto out; 166162306a36Sopenharmony_ci } 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci if (copy_to_user(&pqi->input_buffer_length, 166462306a36Sopenharmony_ci &qi.input_buffer_length, 166562306a36Sopenharmony_ci sizeof(qi.input_buffer_length))) { 166662306a36Sopenharmony_ci rc = -EFAULT; 166762306a36Sopenharmony_ci goto out; 166862306a36Sopenharmony_ci } 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci if (copy_to_user((void __user *)pqi + sizeof(struct smb_query_info), 167162306a36Sopenharmony_ci (const void *)io_rsp + le32_to_cpu(io_rsp->OutputOffset), 167262306a36Sopenharmony_ci qi.input_buffer_length)) 167362306a36Sopenharmony_ci rc = -EFAULT; 167462306a36Sopenharmony_ci } else { 167562306a36Sopenharmony_ci pqi = (struct smb_query_info __user *)arg; 167662306a36Sopenharmony_ci qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base; 167762306a36Sopenharmony_ci if (le32_to_cpu(qi_rsp->OutputBufferLength) < qi.input_buffer_length) 167862306a36Sopenharmony_ci qi.input_buffer_length = le32_to_cpu(qi_rsp->OutputBufferLength); 167962306a36Sopenharmony_ci if (copy_to_user(&pqi->input_buffer_length, 168062306a36Sopenharmony_ci &qi.input_buffer_length, 168162306a36Sopenharmony_ci sizeof(qi.input_buffer_length))) { 168262306a36Sopenharmony_ci rc = -EFAULT; 168362306a36Sopenharmony_ci goto out; 168462306a36Sopenharmony_ci } 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_ci if (copy_to_user(pqi + 1, qi_rsp->Buffer, 168762306a36Sopenharmony_ci qi.input_buffer_length)) 168862306a36Sopenharmony_ci rc = -EFAULT; 168962306a36Sopenharmony_ci } 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_ciout: 169262306a36Sopenharmony_ci free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); 169362306a36Sopenharmony_ci free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); 169462306a36Sopenharmony_ci free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); 169562306a36Sopenharmony_ci SMB2_close_free(&rqst[2]); 169662306a36Sopenharmony_cifree_req_1: 169762306a36Sopenharmony_ci free_req1_func(&rqst[1]); 169862306a36Sopenharmony_cifree_open_req: 169962306a36Sopenharmony_ci SMB2_open_free(&rqst[0]); 170062306a36Sopenharmony_cifree_output_buffer: 170162306a36Sopenharmony_ci kfree(buffer); 170262306a36Sopenharmony_cifree_vars: 170362306a36Sopenharmony_ci kfree(vars); 170462306a36Sopenharmony_ci return rc; 170562306a36Sopenharmony_ci} 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_cistatic ssize_t 170862306a36Sopenharmony_cismb2_copychunk_range(const unsigned int xid, 170962306a36Sopenharmony_ci struct cifsFileInfo *srcfile, 171062306a36Sopenharmony_ci struct cifsFileInfo *trgtfile, u64 src_off, 171162306a36Sopenharmony_ci u64 len, u64 dest_off) 171262306a36Sopenharmony_ci{ 171362306a36Sopenharmony_ci int rc; 171462306a36Sopenharmony_ci unsigned int ret_data_len; 171562306a36Sopenharmony_ci struct copychunk_ioctl *pcchunk; 171662306a36Sopenharmony_ci struct copychunk_ioctl_rsp *retbuf = NULL; 171762306a36Sopenharmony_ci struct cifs_tcon *tcon; 171862306a36Sopenharmony_ci int chunks_copied = 0; 171962306a36Sopenharmony_ci bool chunk_sizes_updated = false; 172062306a36Sopenharmony_ci ssize_t bytes_written, total_bytes_written = 0; 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ci pcchunk = kmalloc(sizeof(struct copychunk_ioctl), GFP_KERNEL); 172362306a36Sopenharmony_ci if (pcchunk == NULL) 172462306a36Sopenharmony_ci return -ENOMEM; 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci cifs_dbg(FYI, "%s: about to call request res key\n", __func__); 172762306a36Sopenharmony_ci /* Request a key from the server to identify the source of the copy */ 172862306a36Sopenharmony_ci rc = SMB2_request_res_key(xid, tlink_tcon(srcfile->tlink), 172962306a36Sopenharmony_ci srcfile->fid.persistent_fid, 173062306a36Sopenharmony_ci srcfile->fid.volatile_fid, pcchunk); 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_ci /* Note: request_res_key sets res_key null only if rc !=0 */ 173362306a36Sopenharmony_ci if (rc) 173462306a36Sopenharmony_ci goto cchunk_out; 173562306a36Sopenharmony_ci 173662306a36Sopenharmony_ci /* For now array only one chunk long, will make more flexible later */ 173762306a36Sopenharmony_ci pcchunk->ChunkCount = cpu_to_le32(1); 173862306a36Sopenharmony_ci pcchunk->Reserved = 0; 173962306a36Sopenharmony_ci pcchunk->Reserved2 = 0; 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci tcon = tlink_tcon(trgtfile->tlink); 174262306a36Sopenharmony_ci 174362306a36Sopenharmony_ci while (len > 0) { 174462306a36Sopenharmony_ci pcchunk->SourceOffset = cpu_to_le64(src_off); 174562306a36Sopenharmony_ci pcchunk->TargetOffset = cpu_to_le64(dest_off); 174662306a36Sopenharmony_ci pcchunk->Length = 174762306a36Sopenharmony_ci cpu_to_le32(min_t(u64, len, tcon->max_bytes_chunk)); 174862306a36Sopenharmony_ci 174962306a36Sopenharmony_ci /* Request server copy to target from src identified by key */ 175062306a36Sopenharmony_ci kfree(retbuf); 175162306a36Sopenharmony_ci retbuf = NULL; 175262306a36Sopenharmony_ci rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid, 175362306a36Sopenharmony_ci trgtfile->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE, 175462306a36Sopenharmony_ci (char *)pcchunk, sizeof(struct copychunk_ioctl), 175562306a36Sopenharmony_ci CIFSMaxBufSize, (char **)&retbuf, &ret_data_len); 175662306a36Sopenharmony_ci if (rc == 0) { 175762306a36Sopenharmony_ci if (ret_data_len != 175862306a36Sopenharmony_ci sizeof(struct copychunk_ioctl_rsp)) { 175962306a36Sopenharmony_ci cifs_tcon_dbg(VFS, "Invalid cchunk response size\n"); 176062306a36Sopenharmony_ci rc = -EIO; 176162306a36Sopenharmony_ci goto cchunk_out; 176262306a36Sopenharmony_ci } 176362306a36Sopenharmony_ci if (retbuf->TotalBytesWritten == 0) { 176462306a36Sopenharmony_ci cifs_dbg(FYI, "no bytes copied\n"); 176562306a36Sopenharmony_ci rc = -EIO; 176662306a36Sopenharmony_ci goto cchunk_out; 176762306a36Sopenharmony_ci } 176862306a36Sopenharmony_ci /* 176962306a36Sopenharmony_ci * Check if server claimed to write more than we asked 177062306a36Sopenharmony_ci */ 177162306a36Sopenharmony_ci if (le32_to_cpu(retbuf->TotalBytesWritten) > 177262306a36Sopenharmony_ci le32_to_cpu(pcchunk->Length)) { 177362306a36Sopenharmony_ci cifs_tcon_dbg(VFS, "Invalid copy chunk response\n"); 177462306a36Sopenharmony_ci rc = -EIO; 177562306a36Sopenharmony_ci goto cchunk_out; 177662306a36Sopenharmony_ci } 177762306a36Sopenharmony_ci if (le32_to_cpu(retbuf->ChunksWritten) != 1) { 177862306a36Sopenharmony_ci cifs_tcon_dbg(VFS, "Invalid num chunks written\n"); 177962306a36Sopenharmony_ci rc = -EIO; 178062306a36Sopenharmony_ci goto cchunk_out; 178162306a36Sopenharmony_ci } 178262306a36Sopenharmony_ci chunks_copied++; 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_ci bytes_written = le32_to_cpu(retbuf->TotalBytesWritten); 178562306a36Sopenharmony_ci src_off += bytes_written; 178662306a36Sopenharmony_ci dest_off += bytes_written; 178762306a36Sopenharmony_ci len -= bytes_written; 178862306a36Sopenharmony_ci total_bytes_written += bytes_written; 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci cifs_dbg(FYI, "Chunks %d PartialChunk %d Total %zu\n", 179162306a36Sopenharmony_ci le32_to_cpu(retbuf->ChunksWritten), 179262306a36Sopenharmony_ci le32_to_cpu(retbuf->ChunkBytesWritten), 179362306a36Sopenharmony_ci bytes_written); 179462306a36Sopenharmony_ci } else if (rc == -EINVAL) { 179562306a36Sopenharmony_ci if (ret_data_len != sizeof(struct copychunk_ioctl_rsp)) 179662306a36Sopenharmony_ci goto cchunk_out; 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_ci cifs_dbg(FYI, "MaxChunks %d BytesChunk %d MaxCopy %d\n", 179962306a36Sopenharmony_ci le32_to_cpu(retbuf->ChunksWritten), 180062306a36Sopenharmony_ci le32_to_cpu(retbuf->ChunkBytesWritten), 180162306a36Sopenharmony_ci le32_to_cpu(retbuf->TotalBytesWritten)); 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_ci /* 180462306a36Sopenharmony_ci * Check if this is the first request using these sizes, 180562306a36Sopenharmony_ci * (ie check if copy succeed once with original sizes 180662306a36Sopenharmony_ci * and check if the server gave us different sizes after 180762306a36Sopenharmony_ci * we already updated max sizes on previous request). 180862306a36Sopenharmony_ci * if not then why is the server returning an error now 180962306a36Sopenharmony_ci */ 181062306a36Sopenharmony_ci if ((chunks_copied != 0) || chunk_sizes_updated) 181162306a36Sopenharmony_ci goto cchunk_out; 181262306a36Sopenharmony_ci 181362306a36Sopenharmony_ci /* Check that server is not asking us to grow size */ 181462306a36Sopenharmony_ci if (le32_to_cpu(retbuf->ChunkBytesWritten) < 181562306a36Sopenharmony_ci tcon->max_bytes_chunk) 181662306a36Sopenharmony_ci tcon->max_bytes_chunk = 181762306a36Sopenharmony_ci le32_to_cpu(retbuf->ChunkBytesWritten); 181862306a36Sopenharmony_ci else 181962306a36Sopenharmony_ci goto cchunk_out; /* server gave us bogus size */ 182062306a36Sopenharmony_ci 182162306a36Sopenharmony_ci /* No need to change MaxChunks since already set to 1 */ 182262306a36Sopenharmony_ci chunk_sizes_updated = true; 182362306a36Sopenharmony_ci } else 182462306a36Sopenharmony_ci goto cchunk_out; 182562306a36Sopenharmony_ci } 182662306a36Sopenharmony_ci 182762306a36Sopenharmony_cicchunk_out: 182862306a36Sopenharmony_ci kfree(pcchunk); 182962306a36Sopenharmony_ci kfree(retbuf); 183062306a36Sopenharmony_ci if (rc) 183162306a36Sopenharmony_ci return rc; 183262306a36Sopenharmony_ci else 183362306a36Sopenharmony_ci return total_bytes_written; 183462306a36Sopenharmony_ci} 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_cistatic int 183762306a36Sopenharmony_cismb2_flush_file(const unsigned int xid, struct cifs_tcon *tcon, 183862306a36Sopenharmony_ci struct cifs_fid *fid) 183962306a36Sopenharmony_ci{ 184062306a36Sopenharmony_ci return SMB2_flush(xid, tcon, fid->persistent_fid, fid->volatile_fid); 184162306a36Sopenharmony_ci} 184262306a36Sopenharmony_ci 184362306a36Sopenharmony_cistatic unsigned int 184462306a36Sopenharmony_cismb2_read_data_offset(char *buf) 184562306a36Sopenharmony_ci{ 184662306a36Sopenharmony_ci struct smb2_read_rsp *rsp = (struct smb2_read_rsp *)buf; 184762306a36Sopenharmony_ci 184862306a36Sopenharmony_ci return rsp->DataOffset; 184962306a36Sopenharmony_ci} 185062306a36Sopenharmony_ci 185162306a36Sopenharmony_cistatic unsigned int 185262306a36Sopenharmony_cismb2_read_data_length(char *buf, bool in_remaining) 185362306a36Sopenharmony_ci{ 185462306a36Sopenharmony_ci struct smb2_read_rsp *rsp = (struct smb2_read_rsp *)buf; 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_ci if (in_remaining) 185762306a36Sopenharmony_ci return le32_to_cpu(rsp->DataRemaining); 185862306a36Sopenharmony_ci 185962306a36Sopenharmony_ci return le32_to_cpu(rsp->DataLength); 186062306a36Sopenharmony_ci} 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_ci 186362306a36Sopenharmony_cistatic int 186462306a36Sopenharmony_cismb2_sync_read(const unsigned int xid, struct cifs_fid *pfid, 186562306a36Sopenharmony_ci struct cifs_io_parms *parms, unsigned int *bytes_read, 186662306a36Sopenharmony_ci char **buf, int *buf_type) 186762306a36Sopenharmony_ci{ 186862306a36Sopenharmony_ci parms->persistent_fid = pfid->persistent_fid; 186962306a36Sopenharmony_ci parms->volatile_fid = pfid->volatile_fid; 187062306a36Sopenharmony_ci return SMB2_read(xid, parms, bytes_read, buf, buf_type); 187162306a36Sopenharmony_ci} 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_cistatic int 187462306a36Sopenharmony_cismb2_sync_write(const unsigned int xid, struct cifs_fid *pfid, 187562306a36Sopenharmony_ci struct cifs_io_parms *parms, unsigned int *written, 187662306a36Sopenharmony_ci struct kvec *iov, unsigned long nr_segs) 187762306a36Sopenharmony_ci{ 187862306a36Sopenharmony_ci 187962306a36Sopenharmony_ci parms->persistent_fid = pfid->persistent_fid; 188062306a36Sopenharmony_ci parms->volatile_fid = pfid->volatile_fid; 188162306a36Sopenharmony_ci return SMB2_write(xid, parms, written, iov, nr_segs); 188262306a36Sopenharmony_ci} 188362306a36Sopenharmony_ci 188462306a36Sopenharmony_ci/* Set or clear the SPARSE_FILE attribute based on value passed in setsparse */ 188562306a36Sopenharmony_cistatic bool smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon, 188662306a36Sopenharmony_ci struct cifsFileInfo *cfile, struct inode *inode, __u8 setsparse) 188762306a36Sopenharmony_ci{ 188862306a36Sopenharmony_ci struct cifsInodeInfo *cifsi; 188962306a36Sopenharmony_ci int rc; 189062306a36Sopenharmony_ci 189162306a36Sopenharmony_ci cifsi = CIFS_I(inode); 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci /* if file already sparse don't bother setting sparse again */ 189462306a36Sopenharmony_ci if ((cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) && setsparse) 189562306a36Sopenharmony_ci return true; /* already sparse */ 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_ci if (!(cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) && !setsparse) 189862306a36Sopenharmony_ci return true; /* already not sparse */ 189962306a36Sopenharmony_ci 190062306a36Sopenharmony_ci /* 190162306a36Sopenharmony_ci * Can't check for sparse support on share the usual way via the 190262306a36Sopenharmony_ci * FS attribute info (FILE_SUPPORTS_SPARSE_FILES) on the share 190362306a36Sopenharmony_ci * since Samba server doesn't set the flag on the share, yet 190462306a36Sopenharmony_ci * supports the set sparse FSCTL and returns sparse correctly 190562306a36Sopenharmony_ci * in the file attributes. If we fail setting sparse though we 190662306a36Sopenharmony_ci * mark that server does not support sparse files for this share 190762306a36Sopenharmony_ci * to avoid repeatedly sending the unsupported fsctl to server 190862306a36Sopenharmony_ci * if the file is repeatedly extended. 190962306a36Sopenharmony_ci */ 191062306a36Sopenharmony_ci if (tcon->broken_sparse_sup) 191162306a36Sopenharmony_ci return false; 191262306a36Sopenharmony_ci 191362306a36Sopenharmony_ci rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, 191462306a36Sopenharmony_ci cfile->fid.volatile_fid, FSCTL_SET_SPARSE, 191562306a36Sopenharmony_ci &setsparse, 1, CIFSMaxBufSize, NULL, NULL); 191662306a36Sopenharmony_ci if (rc) { 191762306a36Sopenharmony_ci tcon->broken_sparse_sup = true; 191862306a36Sopenharmony_ci cifs_dbg(FYI, "set sparse rc = %d\n", rc); 191962306a36Sopenharmony_ci return false; 192062306a36Sopenharmony_ci } 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_ci if (setsparse) 192362306a36Sopenharmony_ci cifsi->cifsAttrs |= FILE_ATTRIBUTE_SPARSE_FILE; 192462306a36Sopenharmony_ci else 192562306a36Sopenharmony_ci cifsi->cifsAttrs &= (~FILE_ATTRIBUTE_SPARSE_FILE); 192662306a36Sopenharmony_ci 192762306a36Sopenharmony_ci return true; 192862306a36Sopenharmony_ci} 192962306a36Sopenharmony_ci 193062306a36Sopenharmony_cistatic int 193162306a36Sopenharmony_cismb2_set_file_size(const unsigned int xid, struct cifs_tcon *tcon, 193262306a36Sopenharmony_ci struct cifsFileInfo *cfile, __u64 size, bool set_alloc) 193362306a36Sopenharmony_ci{ 193462306a36Sopenharmony_ci __le64 eof = cpu_to_le64(size); 193562306a36Sopenharmony_ci struct inode *inode; 193662306a36Sopenharmony_ci 193762306a36Sopenharmony_ci /* 193862306a36Sopenharmony_ci * If extending file more than one page make sparse. Many Linux fs 193962306a36Sopenharmony_ci * make files sparse by default when extending via ftruncate 194062306a36Sopenharmony_ci */ 194162306a36Sopenharmony_ci inode = d_inode(cfile->dentry); 194262306a36Sopenharmony_ci 194362306a36Sopenharmony_ci if (!set_alloc && (size > inode->i_size + 8192)) { 194462306a36Sopenharmony_ci __u8 set_sparse = 1; 194562306a36Sopenharmony_ci 194662306a36Sopenharmony_ci /* whether set sparse succeeds or not, extend the file */ 194762306a36Sopenharmony_ci smb2_set_sparse(xid, tcon, cfile, inode, set_sparse); 194862306a36Sopenharmony_ci } 194962306a36Sopenharmony_ci 195062306a36Sopenharmony_ci return SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, 195162306a36Sopenharmony_ci cfile->fid.volatile_fid, cfile->pid, &eof); 195262306a36Sopenharmony_ci} 195362306a36Sopenharmony_ci 195462306a36Sopenharmony_cistatic int 195562306a36Sopenharmony_cismb2_duplicate_extents(const unsigned int xid, 195662306a36Sopenharmony_ci struct cifsFileInfo *srcfile, 195762306a36Sopenharmony_ci struct cifsFileInfo *trgtfile, u64 src_off, 195862306a36Sopenharmony_ci u64 len, u64 dest_off) 195962306a36Sopenharmony_ci{ 196062306a36Sopenharmony_ci int rc; 196162306a36Sopenharmony_ci unsigned int ret_data_len; 196262306a36Sopenharmony_ci struct inode *inode; 196362306a36Sopenharmony_ci struct duplicate_extents_to_file dup_ext_buf; 196462306a36Sopenharmony_ci struct cifs_tcon *tcon = tlink_tcon(trgtfile->tlink); 196562306a36Sopenharmony_ci 196662306a36Sopenharmony_ci /* server fileays advertise duplicate extent support with this flag */ 196762306a36Sopenharmony_ci if ((le32_to_cpu(tcon->fsAttrInfo.Attributes) & 196862306a36Sopenharmony_ci FILE_SUPPORTS_BLOCK_REFCOUNTING) == 0) 196962306a36Sopenharmony_ci return -EOPNOTSUPP; 197062306a36Sopenharmony_ci 197162306a36Sopenharmony_ci dup_ext_buf.VolatileFileHandle = srcfile->fid.volatile_fid; 197262306a36Sopenharmony_ci dup_ext_buf.PersistentFileHandle = srcfile->fid.persistent_fid; 197362306a36Sopenharmony_ci dup_ext_buf.SourceFileOffset = cpu_to_le64(src_off); 197462306a36Sopenharmony_ci dup_ext_buf.TargetFileOffset = cpu_to_le64(dest_off); 197562306a36Sopenharmony_ci dup_ext_buf.ByteCount = cpu_to_le64(len); 197662306a36Sopenharmony_ci cifs_dbg(FYI, "Duplicate extents: src off %lld dst off %lld len %lld\n", 197762306a36Sopenharmony_ci src_off, dest_off, len); 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_ci inode = d_inode(trgtfile->dentry); 198062306a36Sopenharmony_ci if (inode->i_size < dest_off + len) { 198162306a36Sopenharmony_ci rc = smb2_set_file_size(xid, tcon, trgtfile, dest_off + len, false); 198262306a36Sopenharmony_ci if (rc) 198362306a36Sopenharmony_ci goto duplicate_extents_out; 198462306a36Sopenharmony_ci 198562306a36Sopenharmony_ci /* 198662306a36Sopenharmony_ci * Although also could set plausible allocation size (i_blocks) 198762306a36Sopenharmony_ci * here in addition to setting the file size, in reflink 198862306a36Sopenharmony_ci * it is likely that the target file is sparse. Its allocation 198962306a36Sopenharmony_ci * size will be queried on next revalidate, but it is important 199062306a36Sopenharmony_ci * to make sure that file's cached size is updated immediately 199162306a36Sopenharmony_ci */ 199262306a36Sopenharmony_ci cifs_setsize(inode, dest_off + len); 199362306a36Sopenharmony_ci } 199462306a36Sopenharmony_ci rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid, 199562306a36Sopenharmony_ci trgtfile->fid.volatile_fid, 199662306a36Sopenharmony_ci FSCTL_DUPLICATE_EXTENTS_TO_FILE, 199762306a36Sopenharmony_ci (char *)&dup_ext_buf, 199862306a36Sopenharmony_ci sizeof(struct duplicate_extents_to_file), 199962306a36Sopenharmony_ci CIFSMaxBufSize, NULL, 200062306a36Sopenharmony_ci &ret_data_len); 200162306a36Sopenharmony_ci 200262306a36Sopenharmony_ci if (ret_data_len > 0) 200362306a36Sopenharmony_ci cifs_dbg(FYI, "Non-zero response length in duplicate extents\n"); 200462306a36Sopenharmony_ci 200562306a36Sopenharmony_ciduplicate_extents_out: 200662306a36Sopenharmony_ci return rc; 200762306a36Sopenharmony_ci} 200862306a36Sopenharmony_ci 200962306a36Sopenharmony_cistatic int 201062306a36Sopenharmony_cismb2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, 201162306a36Sopenharmony_ci struct cifsFileInfo *cfile) 201262306a36Sopenharmony_ci{ 201362306a36Sopenharmony_ci return SMB2_set_compression(xid, tcon, cfile->fid.persistent_fid, 201462306a36Sopenharmony_ci cfile->fid.volatile_fid); 201562306a36Sopenharmony_ci} 201662306a36Sopenharmony_ci 201762306a36Sopenharmony_cistatic int 201862306a36Sopenharmony_cismb3_set_integrity(const unsigned int xid, struct cifs_tcon *tcon, 201962306a36Sopenharmony_ci struct cifsFileInfo *cfile) 202062306a36Sopenharmony_ci{ 202162306a36Sopenharmony_ci struct fsctl_set_integrity_information_req integr_info; 202262306a36Sopenharmony_ci unsigned int ret_data_len; 202362306a36Sopenharmony_ci 202462306a36Sopenharmony_ci integr_info.ChecksumAlgorithm = cpu_to_le16(CHECKSUM_TYPE_UNCHANGED); 202562306a36Sopenharmony_ci integr_info.Flags = 0; 202662306a36Sopenharmony_ci integr_info.Reserved = 0; 202762306a36Sopenharmony_ci 202862306a36Sopenharmony_ci return SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, 202962306a36Sopenharmony_ci cfile->fid.volatile_fid, 203062306a36Sopenharmony_ci FSCTL_SET_INTEGRITY_INFORMATION, 203162306a36Sopenharmony_ci (char *)&integr_info, 203262306a36Sopenharmony_ci sizeof(struct fsctl_set_integrity_information_req), 203362306a36Sopenharmony_ci CIFSMaxBufSize, NULL, 203462306a36Sopenharmony_ci &ret_data_len); 203562306a36Sopenharmony_ci 203662306a36Sopenharmony_ci} 203762306a36Sopenharmony_ci 203862306a36Sopenharmony_ci/* GMT Token is @GMT-YYYY.MM.DD-HH.MM.SS Unicode which is 48 bytes + null */ 203962306a36Sopenharmony_ci#define GMT_TOKEN_SIZE 50 204062306a36Sopenharmony_ci 204162306a36Sopenharmony_ci#define MIN_SNAPSHOT_ARRAY_SIZE 16 /* See MS-SMB2 section 3.3.5.15.1 */ 204262306a36Sopenharmony_ci 204362306a36Sopenharmony_ci/* 204462306a36Sopenharmony_ci * Input buffer contains (empty) struct smb_snapshot array with size filled in 204562306a36Sopenharmony_ci * For output see struct SRV_SNAPSHOT_ARRAY in MS-SMB2 section 2.2.32.2 204662306a36Sopenharmony_ci */ 204762306a36Sopenharmony_cistatic int 204862306a36Sopenharmony_cismb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon, 204962306a36Sopenharmony_ci struct cifsFileInfo *cfile, void __user *ioc_buf) 205062306a36Sopenharmony_ci{ 205162306a36Sopenharmony_ci char *retbuf = NULL; 205262306a36Sopenharmony_ci unsigned int ret_data_len = 0; 205362306a36Sopenharmony_ci int rc; 205462306a36Sopenharmony_ci u32 max_response_size; 205562306a36Sopenharmony_ci struct smb_snapshot_array snapshot_in; 205662306a36Sopenharmony_ci 205762306a36Sopenharmony_ci /* 205862306a36Sopenharmony_ci * On the first query to enumerate the list of snapshots available 205962306a36Sopenharmony_ci * for this volume the buffer begins with 0 (number of snapshots 206062306a36Sopenharmony_ci * which can be returned is zero since at that point we do not know 206162306a36Sopenharmony_ci * how big the buffer needs to be). On the second query, 206262306a36Sopenharmony_ci * it (ret_data_len) is set to number of snapshots so we can 206362306a36Sopenharmony_ci * know to set the maximum response size larger (see below). 206462306a36Sopenharmony_ci */ 206562306a36Sopenharmony_ci if (get_user(ret_data_len, (unsigned int __user *)ioc_buf)) 206662306a36Sopenharmony_ci return -EFAULT; 206762306a36Sopenharmony_ci 206862306a36Sopenharmony_ci /* 206962306a36Sopenharmony_ci * Note that for snapshot queries that servers like Azure expect that 207062306a36Sopenharmony_ci * the first query be minimal size (and just used to get the number/size 207162306a36Sopenharmony_ci * of previous versions) so response size must be specified as EXACTLY 207262306a36Sopenharmony_ci * sizeof(struct snapshot_array) which is 16 when rounded up to multiple 207362306a36Sopenharmony_ci * of eight bytes. 207462306a36Sopenharmony_ci */ 207562306a36Sopenharmony_ci if (ret_data_len == 0) 207662306a36Sopenharmony_ci max_response_size = MIN_SNAPSHOT_ARRAY_SIZE; 207762306a36Sopenharmony_ci else 207862306a36Sopenharmony_ci max_response_size = CIFSMaxBufSize; 207962306a36Sopenharmony_ci 208062306a36Sopenharmony_ci rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, 208162306a36Sopenharmony_ci cfile->fid.volatile_fid, 208262306a36Sopenharmony_ci FSCTL_SRV_ENUMERATE_SNAPSHOTS, 208362306a36Sopenharmony_ci NULL, 0 /* no input data */, max_response_size, 208462306a36Sopenharmony_ci (char **)&retbuf, 208562306a36Sopenharmony_ci &ret_data_len); 208662306a36Sopenharmony_ci cifs_dbg(FYI, "enum snaphots ioctl returned %d and ret buflen is %d\n", 208762306a36Sopenharmony_ci rc, ret_data_len); 208862306a36Sopenharmony_ci if (rc) 208962306a36Sopenharmony_ci return rc; 209062306a36Sopenharmony_ci 209162306a36Sopenharmony_ci if (ret_data_len && (ioc_buf != NULL) && (retbuf != NULL)) { 209262306a36Sopenharmony_ci /* Fixup buffer */ 209362306a36Sopenharmony_ci if (copy_from_user(&snapshot_in, ioc_buf, 209462306a36Sopenharmony_ci sizeof(struct smb_snapshot_array))) { 209562306a36Sopenharmony_ci rc = -EFAULT; 209662306a36Sopenharmony_ci kfree(retbuf); 209762306a36Sopenharmony_ci return rc; 209862306a36Sopenharmony_ci } 209962306a36Sopenharmony_ci 210062306a36Sopenharmony_ci /* 210162306a36Sopenharmony_ci * Check for min size, ie not large enough to fit even one GMT 210262306a36Sopenharmony_ci * token (snapshot). On the first ioctl some users may pass in 210362306a36Sopenharmony_ci * smaller size (or zero) to simply get the size of the array 210462306a36Sopenharmony_ci * so the user space caller can allocate sufficient memory 210562306a36Sopenharmony_ci * and retry the ioctl again with larger array size sufficient 210662306a36Sopenharmony_ci * to hold all of the snapshot GMT tokens on the second try. 210762306a36Sopenharmony_ci */ 210862306a36Sopenharmony_ci if (snapshot_in.snapshot_array_size < GMT_TOKEN_SIZE) 210962306a36Sopenharmony_ci ret_data_len = sizeof(struct smb_snapshot_array); 211062306a36Sopenharmony_ci 211162306a36Sopenharmony_ci /* 211262306a36Sopenharmony_ci * We return struct SRV_SNAPSHOT_ARRAY, followed by 211362306a36Sopenharmony_ci * the snapshot array (of 50 byte GMT tokens) each 211462306a36Sopenharmony_ci * representing an available previous version of the data 211562306a36Sopenharmony_ci */ 211662306a36Sopenharmony_ci if (ret_data_len > (snapshot_in.snapshot_array_size + 211762306a36Sopenharmony_ci sizeof(struct smb_snapshot_array))) 211862306a36Sopenharmony_ci ret_data_len = snapshot_in.snapshot_array_size + 211962306a36Sopenharmony_ci sizeof(struct smb_snapshot_array); 212062306a36Sopenharmony_ci 212162306a36Sopenharmony_ci if (copy_to_user(ioc_buf, retbuf, ret_data_len)) 212262306a36Sopenharmony_ci rc = -EFAULT; 212362306a36Sopenharmony_ci } 212462306a36Sopenharmony_ci 212562306a36Sopenharmony_ci kfree(retbuf); 212662306a36Sopenharmony_ci return rc; 212762306a36Sopenharmony_ci} 212862306a36Sopenharmony_ci 212962306a36Sopenharmony_ci 213062306a36Sopenharmony_ci 213162306a36Sopenharmony_cistatic int 213262306a36Sopenharmony_cismb3_notify(const unsigned int xid, struct file *pfile, 213362306a36Sopenharmony_ci void __user *ioc_buf, bool return_changes) 213462306a36Sopenharmony_ci{ 213562306a36Sopenharmony_ci struct smb3_notify_info notify; 213662306a36Sopenharmony_ci struct smb3_notify_info __user *pnotify_buf; 213762306a36Sopenharmony_ci struct dentry *dentry = pfile->f_path.dentry; 213862306a36Sopenharmony_ci struct inode *inode = file_inode(pfile); 213962306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); 214062306a36Sopenharmony_ci struct cifs_open_parms oparms; 214162306a36Sopenharmony_ci struct cifs_fid fid; 214262306a36Sopenharmony_ci struct cifs_tcon *tcon; 214362306a36Sopenharmony_ci const unsigned char *path; 214462306a36Sopenharmony_ci char *returned_ioctl_info = NULL; 214562306a36Sopenharmony_ci void *page = alloc_dentry_path(); 214662306a36Sopenharmony_ci __le16 *utf16_path = NULL; 214762306a36Sopenharmony_ci u8 oplock = SMB2_OPLOCK_LEVEL_NONE; 214862306a36Sopenharmony_ci int rc = 0; 214962306a36Sopenharmony_ci __u32 ret_len = 0; 215062306a36Sopenharmony_ci 215162306a36Sopenharmony_ci path = build_path_from_dentry(dentry, page); 215262306a36Sopenharmony_ci if (IS_ERR(path)) { 215362306a36Sopenharmony_ci rc = PTR_ERR(path); 215462306a36Sopenharmony_ci goto notify_exit; 215562306a36Sopenharmony_ci } 215662306a36Sopenharmony_ci 215762306a36Sopenharmony_ci utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); 215862306a36Sopenharmony_ci if (utf16_path == NULL) { 215962306a36Sopenharmony_ci rc = -ENOMEM; 216062306a36Sopenharmony_ci goto notify_exit; 216162306a36Sopenharmony_ci } 216262306a36Sopenharmony_ci 216362306a36Sopenharmony_ci if (return_changes) { 216462306a36Sopenharmony_ci if (copy_from_user(¬ify, ioc_buf, sizeof(struct smb3_notify_info))) { 216562306a36Sopenharmony_ci rc = -EFAULT; 216662306a36Sopenharmony_ci goto notify_exit; 216762306a36Sopenharmony_ci } 216862306a36Sopenharmony_ci } else { 216962306a36Sopenharmony_ci if (copy_from_user(¬ify, ioc_buf, sizeof(struct smb3_notify))) { 217062306a36Sopenharmony_ci rc = -EFAULT; 217162306a36Sopenharmony_ci goto notify_exit; 217262306a36Sopenharmony_ci } 217362306a36Sopenharmony_ci notify.data_len = 0; 217462306a36Sopenharmony_ci } 217562306a36Sopenharmony_ci 217662306a36Sopenharmony_ci tcon = cifs_sb_master_tcon(cifs_sb); 217762306a36Sopenharmony_ci oparms = (struct cifs_open_parms) { 217862306a36Sopenharmony_ci .tcon = tcon, 217962306a36Sopenharmony_ci .path = path, 218062306a36Sopenharmony_ci .desired_access = FILE_READ_ATTRIBUTES | FILE_READ_DATA, 218162306a36Sopenharmony_ci .disposition = FILE_OPEN, 218262306a36Sopenharmony_ci .create_options = cifs_create_options(cifs_sb, 0), 218362306a36Sopenharmony_ci .fid = &fid, 218462306a36Sopenharmony_ci }; 218562306a36Sopenharmony_ci 218662306a36Sopenharmony_ci rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL, 218762306a36Sopenharmony_ci NULL); 218862306a36Sopenharmony_ci if (rc) 218962306a36Sopenharmony_ci goto notify_exit; 219062306a36Sopenharmony_ci 219162306a36Sopenharmony_ci rc = SMB2_change_notify(xid, tcon, fid.persistent_fid, fid.volatile_fid, 219262306a36Sopenharmony_ci notify.watch_tree, notify.completion_filter, 219362306a36Sopenharmony_ci notify.data_len, &returned_ioctl_info, &ret_len); 219462306a36Sopenharmony_ci 219562306a36Sopenharmony_ci SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); 219662306a36Sopenharmony_ci 219762306a36Sopenharmony_ci cifs_dbg(FYI, "change notify for path %s rc %d\n", path, rc); 219862306a36Sopenharmony_ci if (return_changes && (ret_len > 0) && (notify.data_len > 0)) { 219962306a36Sopenharmony_ci if (ret_len > notify.data_len) 220062306a36Sopenharmony_ci ret_len = notify.data_len; 220162306a36Sopenharmony_ci pnotify_buf = (struct smb3_notify_info __user *)ioc_buf; 220262306a36Sopenharmony_ci if (copy_to_user(pnotify_buf->notify_data, returned_ioctl_info, ret_len)) 220362306a36Sopenharmony_ci rc = -EFAULT; 220462306a36Sopenharmony_ci else if (copy_to_user(&pnotify_buf->data_len, &ret_len, sizeof(ret_len))) 220562306a36Sopenharmony_ci rc = -EFAULT; 220662306a36Sopenharmony_ci } 220762306a36Sopenharmony_ci kfree(returned_ioctl_info); 220862306a36Sopenharmony_cinotify_exit: 220962306a36Sopenharmony_ci free_dentry_path(page); 221062306a36Sopenharmony_ci kfree(utf16_path); 221162306a36Sopenharmony_ci return rc; 221262306a36Sopenharmony_ci} 221362306a36Sopenharmony_ci 221462306a36Sopenharmony_cistatic int 221562306a36Sopenharmony_cismb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon, 221662306a36Sopenharmony_ci const char *path, struct cifs_sb_info *cifs_sb, 221762306a36Sopenharmony_ci struct cifs_fid *fid, __u16 search_flags, 221862306a36Sopenharmony_ci struct cifs_search_info *srch_inf) 221962306a36Sopenharmony_ci{ 222062306a36Sopenharmony_ci __le16 *utf16_path; 222162306a36Sopenharmony_ci struct smb_rqst rqst[2]; 222262306a36Sopenharmony_ci struct kvec rsp_iov[2]; 222362306a36Sopenharmony_ci int resp_buftype[2]; 222462306a36Sopenharmony_ci struct kvec open_iov[SMB2_CREATE_IOV_SIZE]; 222562306a36Sopenharmony_ci struct kvec qd_iov[SMB2_QUERY_DIRECTORY_IOV_SIZE]; 222662306a36Sopenharmony_ci int rc, flags = 0; 222762306a36Sopenharmony_ci u8 oplock = SMB2_OPLOCK_LEVEL_NONE; 222862306a36Sopenharmony_ci struct cifs_open_parms oparms; 222962306a36Sopenharmony_ci struct smb2_query_directory_rsp *qd_rsp = NULL; 223062306a36Sopenharmony_ci struct smb2_create_rsp *op_rsp = NULL; 223162306a36Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); 223262306a36Sopenharmony_ci int retry_count = 0; 223362306a36Sopenharmony_ci 223462306a36Sopenharmony_ci utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); 223562306a36Sopenharmony_ci if (!utf16_path) 223662306a36Sopenharmony_ci return -ENOMEM; 223762306a36Sopenharmony_ci 223862306a36Sopenharmony_ci if (smb3_encryption_required(tcon)) 223962306a36Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 224062306a36Sopenharmony_ci 224162306a36Sopenharmony_ci memset(rqst, 0, sizeof(rqst)); 224262306a36Sopenharmony_ci resp_buftype[0] = resp_buftype[1] = CIFS_NO_BUFFER; 224362306a36Sopenharmony_ci memset(rsp_iov, 0, sizeof(rsp_iov)); 224462306a36Sopenharmony_ci 224562306a36Sopenharmony_ci /* Open */ 224662306a36Sopenharmony_ci memset(&open_iov, 0, sizeof(open_iov)); 224762306a36Sopenharmony_ci rqst[0].rq_iov = open_iov; 224862306a36Sopenharmony_ci rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; 224962306a36Sopenharmony_ci 225062306a36Sopenharmony_ci oparms = (struct cifs_open_parms) { 225162306a36Sopenharmony_ci .tcon = tcon, 225262306a36Sopenharmony_ci .path = path, 225362306a36Sopenharmony_ci .desired_access = FILE_READ_ATTRIBUTES | FILE_READ_DATA, 225462306a36Sopenharmony_ci .disposition = FILE_OPEN, 225562306a36Sopenharmony_ci .create_options = cifs_create_options(cifs_sb, 0), 225662306a36Sopenharmony_ci .fid = fid, 225762306a36Sopenharmony_ci }; 225862306a36Sopenharmony_ci 225962306a36Sopenharmony_ci rc = SMB2_open_init(tcon, server, 226062306a36Sopenharmony_ci &rqst[0], &oplock, &oparms, utf16_path); 226162306a36Sopenharmony_ci if (rc) 226262306a36Sopenharmony_ci goto qdf_free; 226362306a36Sopenharmony_ci smb2_set_next_command(tcon, &rqst[0]); 226462306a36Sopenharmony_ci 226562306a36Sopenharmony_ci /* Query directory */ 226662306a36Sopenharmony_ci srch_inf->entries_in_buffer = 0; 226762306a36Sopenharmony_ci srch_inf->index_of_last_entry = 2; 226862306a36Sopenharmony_ci 226962306a36Sopenharmony_ci memset(&qd_iov, 0, sizeof(qd_iov)); 227062306a36Sopenharmony_ci rqst[1].rq_iov = qd_iov; 227162306a36Sopenharmony_ci rqst[1].rq_nvec = SMB2_QUERY_DIRECTORY_IOV_SIZE; 227262306a36Sopenharmony_ci 227362306a36Sopenharmony_ci rc = SMB2_query_directory_init(xid, tcon, server, 227462306a36Sopenharmony_ci &rqst[1], 227562306a36Sopenharmony_ci COMPOUND_FID, COMPOUND_FID, 227662306a36Sopenharmony_ci 0, srch_inf->info_level); 227762306a36Sopenharmony_ci if (rc) 227862306a36Sopenharmony_ci goto qdf_free; 227962306a36Sopenharmony_ci 228062306a36Sopenharmony_ci smb2_set_related(&rqst[1]); 228162306a36Sopenharmony_ci 228262306a36Sopenharmony_ciagain: 228362306a36Sopenharmony_ci rc = compound_send_recv(xid, tcon->ses, server, 228462306a36Sopenharmony_ci flags, 2, rqst, 228562306a36Sopenharmony_ci resp_buftype, rsp_iov); 228662306a36Sopenharmony_ci 228762306a36Sopenharmony_ci if (rc == -EAGAIN && retry_count++ < 10) 228862306a36Sopenharmony_ci goto again; 228962306a36Sopenharmony_ci 229062306a36Sopenharmony_ci /* If the open failed there is nothing to do */ 229162306a36Sopenharmony_ci op_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base; 229262306a36Sopenharmony_ci if (op_rsp == NULL || op_rsp->hdr.Status != STATUS_SUCCESS) { 229362306a36Sopenharmony_ci cifs_dbg(FYI, "query_dir_first: open failed rc=%d\n", rc); 229462306a36Sopenharmony_ci goto qdf_free; 229562306a36Sopenharmony_ci } 229662306a36Sopenharmony_ci fid->persistent_fid = op_rsp->PersistentFileId; 229762306a36Sopenharmony_ci fid->volatile_fid = op_rsp->VolatileFileId; 229862306a36Sopenharmony_ci 229962306a36Sopenharmony_ci /* Anything else than ENODATA means a genuine error */ 230062306a36Sopenharmony_ci if (rc && rc != -ENODATA) { 230162306a36Sopenharmony_ci SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid); 230262306a36Sopenharmony_ci cifs_dbg(FYI, "query_dir_first: query directory failed rc=%d\n", rc); 230362306a36Sopenharmony_ci trace_smb3_query_dir_err(xid, fid->persistent_fid, 230462306a36Sopenharmony_ci tcon->tid, tcon->ses->Suid, 0, 0, rc); 230562306a36Sopenharmony_ci goto qdf_free; 230662306a36Sopenharmony_ci } 230762306a36Sopenharmony_ci 230862306a36Sopenharmony_ci atomic_inc(&tcon->num_remote_opens); 230962306a36Sopenharmony_ci 231062306a36Sopenharmony_ci qd_rsp = (struct smb2_query_directory_rsp *)rsp_iov[1].iov_base; 231162306a36Sopenharmony_ci if (qd_rsp->hdr.Status == STATUS_NO_MORE_FILES) { 231262306a36Sopenharmony_ci trace_smb3_query_dir_done(xid, fid->persistent_fid, 231362306a36Sopenharmony_ci tcon->tid, tcon->ses->Suid, 0, 0); 231462306a36Sopenharmony_ci srch_inf->endOfSearch = true; 231562306a36Sopenharmony_ci rc = 0; 231662306a36Sopenharmony_ci goto qdf_free; 231762306a36Sopenharmony_ci } 231862306a36Sopenharmony_ci 231962306a36Sopenharmony_ci rc = smb2_parse_query_directory(tcon, &rsp_iov[1], resp_buftype[1], 232062306a36Sopenharmony_ci srch_inf); 232162306a36Sopenharmony_ci if (rc) { 232262306a36Sopenharmony_ci trace_smb3_query_dir_err(xid, fid->persistent_fid, tcon->tid, 232362306a36Sopenharmony_ci tcon->ses->Suid, 0, 0, rc); 232462306a36Sopenharmony_ci goto qdf_free; 232562306a36Sopenharmony_ci } 232662306a36Sopenharmony_ci resp_buftype[1] = CIFS_NO_BUFFER; 232762306a36Sopenharmony_ci 232862306a36Sopenharmony_ci trace_smb3_query_dir_done(xid, fid->persistent_fid, tcon->tid, 232962306a36Sopenharmony_ci tcon->ses->Suid, 0, srch_inf->entries_in_buffer); 233062306a36Sopenharmony_ci 233162306a36Sopenharmony_ci qdf_free: 233262306a36Sopenharmony_ci kfree(utf16_path); 233362306a36Sopenharmony_ci SMB2_open_free(&rqst[0]); 233462306a36Sopenharmony_ci SMB2_query_directory_free(&rqst[1]); 233562306a36Sopenharmony_ci free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); 233662306a36Sopenharmony_ci free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); 233762306a36Sopenharmony_ci return rc; 233862306a36Sopenharmony_ci} 233962306a36Sopenharmony_ci 234062306a36Sopenharmony_cistatic int 234162306a36Sopenharmony_cismb2_query_dir_next(const unsigned int xid, struct cifs_tcon *tcon, 234262306a36Sopenharmony_ci struct cifs_fid *fid, __u16 search_flags, 234362306a36Sopenharmony_ci struct cifs_search_info *srch_inf) 234462306a36Sopenharmony_ci{ 234562306a36Sopenharmony_ci return SMB2_query_directory(xid, tcon, fid->persistent_fid, 234662306a36Sopenharmony_ci fid->volatile_fid, 0, srch_inf); 234762306a36Sopenharmony_ci} 234862306a36Sopenharmony_ci 234962306a36Sopenharmony_cistatic int 235062306a36Sopenharmony_cismb2_close_dir(const unsigned int xid, struct cifs_tcon *tcon, 235162306a36Sopenharmony_ci struct cifs_fid *fid) 235262306a36Sopenharmony_ci{ 235362306a36Sopenharmony_ci return SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid); 235462306a36Sopenharmony_ci} 235562306a36Sopenharmony_ci 235662306a36Sopenharmony_ci/* 235762306a36Sopenharmony_ci * If we negotiate SMB2 protocol and get STATUS_PENDING - update 235862306a36Sopenharmony_ci * the number of credits and return true. Otherwise - return false. 235962306a36Sopenharmony_ci */ 236062306a36Sopenharmony_cistatic bool 236162306a36Sopenharmony_cismb2_is_status_pending(char *buf, struct TCP_Server_Info *server) 236262306a36Sopenharmony_ci{ 236362306a36Sopenharmony_ci struct smb2_hdr *shdr = (struct smb2_hdr *)buf; 236462306a36Sopenharmony_ci int scredits, in_flight; 236562306a36Sopenharmony_ci 236662306a36Sopenharmony_ci if (shdr->Status != STATUS_PENDING) 236762306a36Sopenharmony_ci return false; 236862306a36Sopenharmony_ci 236962306a36Sopenharmony_ci if (shdr->CreditRequest) { 237062306a36Sopenharmony_ci spin_lock(&server->req_lock); 237162306a36Sopenharmony_ci server->credits += le16_to_cpu(shdr->CreditRequest); 237262306a36Sopenharmony_ci scredits = server->credits; 237362306a36Sopenharmony_ci in_flight = server->in_flight; 237462306a36Sopenharmony_ci spin_unlock(&server->req_lock); 237562306a36Sopenharmony_ci wake_up(&server->request_q); 237662306a36Sopenharmony_ci 237762306a36Sopenharmony_ci trace_smb3_pend_credits(server->CurrentMid, 237862306a36Sopenharmony_ci server->conn_id, server->hostname, scredits, 237962306a36Sopenharmony_ci le16_to_cpu(shdr->CreditRequest), in_flight); 238062306a36Sopenharmony_ci cifs_dbg(FYI, "%s: status pending add %u credits total=%d\n", 238162306a36Sopenharmony_ci __func__, le16_to_cpu(shdr->CreditRequest), scredits); 238262306a36Sopenharmony_ci } 238362306a36Sopenharmony_ci 238462306a36Sopenharmony_ci return true; 238562306a36Sopenharmony_ci} 238662306a36Sopenharmony_ci 238762306a36Sopenharmony_cistatic bool 238862306a36Sopenharmony_cismb2_is_session_expired(char *buf) 238962306a36Sopenharmony_ci{ 239062306a36Sopenharmony_ci struct smb2_hdr *shdr = (struct smb2_hdr *)buf; 239162306a36Sopenharmony_ci 239262306a36Sopenharmony_ci if (shdr->Status != STATUS_NETWORK_SESSION_EXPIRED && 239362306a36Sopenharmony_ci shdr->Status != STATUS_USER_SESSION_DELETED) 239462306a36Sopenharmony_ci return false; 239562306a36Sopenharmony_ci 239662306a36Sopenharmony_ci trace_smb3_ses_expired(le32_to_cpu(shdr->Id.SyncId.TreeId), 239762306a36Sopenharmony_ci le64_to_cpu(shdr->SessionId), 239862306a36Sopenharmony_ci le16_to_cpu(shdr->Command), 239962306a36Sopenharmony_ci le64_to_cpu(shdr->MessageId)); 240062306a36Sopenharmony_ci cifs_dbg(FYI, "Session expired or deleted\n"); 240162306a36Sopenharmony_ci 240262306a36Sopenharmony_ci return true; 240362306a36Sopenharmony_ci} 240462306a36Sopenharmony_ci 240562306a36Sopenharmony_cistatic bool 240662306a36Sopenharmony_cismb2_is_status_io_timeout(char *buf) 240762306a36Sopenharmony_ci{ 240862306a36Sopenharmony_ci struct smb2_hdr *shdr = (struct smb2_hdr *)buf; 240962306a36Sopenharmony_ci 241062306a36Sopenharmony_ci if (shdr->Status == STATUS_IO_TIMEOUT) 241162306a36Sopenharmony_ci return true; 241262306a36Sopenharmony_ci else 241362306a36Sopenharmony_ci return false; 241462306a36Sopenharmony_ci} 241562306a36Sopenharmony_ci 241662306a36Sopenharmony_cistatic bool 241762306a36Sopenharmony_cismb2_is_network_name_deleted(char *buf, struct TCP_Server_Info *server) 241862306a36Sopenharmony_ci{ 241962306a36Sopenharmony_ci struct smb2_hdr *shdr = (struct smb2_hdr *)buf; 242062306a36Sopenharmony_ci struct TCP_Server_Info *pserver; 242162306a36Sopenharmony_ci struct cifs_ses *ses; 242262306a36Sopenharmony_ci struct cifs_tcon *tcon; 242362306a36Sopenharmony_ci 242462306a36Sopenharmony_ci if (shdr->Status != STATUS_NETWORK_NAME_DELETED) 242562306a36Sopenharmony_ci return false; 242662306a36Sopenharmony_ci 242762306a36Sopenharmony_ci /* If server is a channel, select the primary channel */ 242862306a36Sopenharmony_ci pserver = SERVER_IS_CHAN(server) ? server->primary_server : server; 242962306a36Sopenharmony_ci 243062306a36Sopenharmony_ci spin_lock(&cifs_tcp_ses_lock); 243162306a36Sopenharmony_ci list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { 243262306a36Sopenharmony_ci list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { 243362306a36Sopenharmony_ci if (tcon->tid == le32_to_cpu(shdr->Id.SyncId.TreeId)) { 243462306a36Sopenharmony_ci spin_lock(&tcon->tc_lock); 243562306a36Sopenharmony_ci tcon->need_reconnect = true; 243662306a36Sopenharmony_ci spin_unlock(&tcon->tc_lock); 243762306a36Sopenharmony_ci spin_unlock(&cifs_tcp_ses_lock); 243862306a36Sopenharmony_ci pr_warn_once("Server share %s deleted.\n", 243962306a36Sopenharmony_ci tcon->tree_name); 244062306a36Sopenharmony_ci return true; 244162306a36Sopenharmony_ci } 244262306a36Sopenharmony_ci } 244362306a36Sopenharmony_ci } 244462306a36Sopenharmony_ci spin_unlock(&cifs_tcp_ses_lock); 244562306a36Sopenharmony_ci 244662306a36Sopenharmony_ci return false; 244762306a36Sopenharmony_ci} 244862306a36Sopenharmony_ci 244962306a36Sopenharmony_cistatic int 245062306a36Sopenharmony_cismb2_oplock_response(struct cifs_tcon *tcon, __u64 persistent_fid, 245162306a36Sopenharmony_ci __u64 volatile_fid, __u16 net_fid, struct cifsInodeInfo *cinode) 245262306a36Sopenharmony_ci{ 245362306a36Sopenharmony_ci if (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING) 245462306a36Sopenharmony_ci return SMB2_lease_break(0, tcon, cinode->lease_key, 245562306a36Sopenharmony_ci smb2_get_lease_state(cinode)); 245662306a36Sopenharmony_ci 245762306a36Sopenharmony_ci return SMB2_oplock_break(0, tcon, persistent_fid, volatile_fid, 245862306a36Sopenharmony_ci CIFS_CACHE_READ(cinode) ? 1 : 0); 245962306a36Sopenharmony_ci} 246062306a36Sopenharmony_ci 246162306a36Sopenharmony_civoid 246262306a36Sopenharmony_cismb2_set_related(struct smb_rqst *rqst) 246362306a36Sopenharmony_ci{ 246462306a36Sopenharmony_ci struct smb2_hdr *shdr; 246562306a36Sopenharmony_ci 246662306a36Sopenharmony_ci shdr = (struct smb2_hdr *)(rqst->rq_iov[0].iov_base); 246762306a36Sopenharmony_ci if (shdr == NULL) { 246862306a36Sopenharmony_ci cifs_dbg(FYI, "shdr NULL in smb2_set_related\n"); 246962306a36Sopenharmony_ci return; 247062306a36Sopenharmony_ci } 247162306a36Sopenharmony_ci shdr->Flags |= SMB2_FLAGS_RELATED_OPERATIONS; 247262306a36Sopenharmony_ci} 247362306a36Sopenharmony_ci 247462306a36Sopenharmony_cichar smb2_padding[7] = {0, 0, 0, 0, 0, 0, 0}; 247562306a36Sopenharmony_ci 247662306a36Sopenharmony_civoid 247762306a36Sopenharmony_cismb2_set_next_command(struct cifs_tcon *tcon, struct smb_rqst *rqst) 247862306a36Sopenharmony_ci{ 247962306a36Sopenharmony_ci struct smb2_hdr *shdr; 248062306a36Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 248162306a36Sopenharmony_ci struct TCP_Server_Info *server = ses->server; 248262306a36Sopenharmony_ci unsigned long len = smb_rqst_len(server, rqst); 248362306a36Sopenharmony_ci int i, num_padding; 248462306a36Sopenharmony_ci 248562306a36Sopenharmony_ci shdr = (struct smb2_hdr *)(rqst->rq_iov[0].iov_base); 248662306a36Sopenharmony_ci if (shdr == NULL) { 248762306a36Sopenharmony_ci cifs_dbg(FYI, "shdr NULL in smb2_set_next_command\n"); 248862306a36Sopenharmony_ci return; 248962306a36Sopenharmony_ci } 249062306a36Sopenharmony_ci 249162306a36Sopenharmony_ci /* SMB headers in a compound are 8 byte aligned. */ 249262306a36Sopenharmony_ci 249362306a36Sopenharmony_ci /* No padding needed */ 249462306a36Sopenharmony_ci if (!(len & 7)) 249562306a36Sopenharmony_ci goto finished; 249662306a36Sopenharmony_ci 249762306a36Sopenharmony_ci num_padding = 8 - (len & 7); 249862306a36Sopenharmony_ci if (!smb3_encryption_required(tcon)) { 249962306a36Sopenharmony_ci /* 250062306a36Sopenharmony_ci * If we do not have encryption then we can just add an extra 250162306a36Sopenharmony_ci * iov for the padding. 250262306a36Sopenharmony_ci */ 250362306a36Sopenharmony_ci rqst->rq_iov[rqst->rq_nvec].iov_base = smb2_padding; 250462306a36Sopenharmony_ci rqst->rq_iov[rqst->rq_nvec].iov_len = num_padding; 250562306a36Sopenharmony_ci rqst->rq_nvec++; 250662306a36Sopenharmony_ci len += num_padding; 250762306a36Sopenharmony_ci } else { 250862306a36Sopenharmony_ci /* 250962306a36Sopenharmony_ci * We can not add a small padding iov for the encryption case 251062306a36Sopenharmony_ci * because the encryption framework can not handle the padding 251162306a36Sopenharmony_ci * iovs. 251262306a36Sopenharmony_ci * We have to flatten this into a single buffer and add 251362306a36Sopenharmony_ci * the padding to it. 251462306a36Sopenharmony_ci */ 251562306a36Sopenharmony_ci for (i = 1; i < rqst->rq_nvec; i++) { 251662306a36Sopenharmony_ci memcpy(rqst->rq_iov[0].iov_base + 251762306a36Sopenharmony_ci rqst->rq_iov[0].iov_len, 251862306a36Sopenharmony_ci rqst->rq_iov[i].iov_base, 251962306a36Sopenharmony_ci rqst->rq_iov[i].iov_len); 252062306a36Sopenharmony_ci rqst->rq_iov[0].iov_len += rqst->rq_iov[i].iov_len; 252162306a36Sopenharmony_ci } 252262306a36Sopenharmony_ci memset(rqst->rq_iov[0].iov_base + rqst->rq_iov[0].iov_len, 252362306a36Sopenharmony_ci 0, num_padding); 252462306a36Sopenharmony_ci rqst->rq_iov[0].iov_len += num_padding; 252562306a36Sopenharmony_ci len += num_padding; 252662306a36Sopenharmony_ci rqst->rq_nvec = 1; 252762306a36Sopenharmony_ci } 252862306a36Sopenharmony_ci 252962306a36Sopenharmony_ci finished: 253062306a36Sopenharmony_ci shdr->NextCommand = cpu_to_le32(len); 253162306a36Sopenharmony_ci} 253262306a36Sopenharmony_ci 253362306a36Sopenharmony_ci/* 253462306a36Sopenharmony_ci * Passes the query info response back to the caller on success. 253562306a36Sopenharmony_ci * Caller need to free this with free_rsp_buf(). 253662306a36Sopenharmony_ci */ 253762306a36Sopenharmony_ciint 253862306a36Sopenharmony_cismb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon, 253962306a36Sopenharmony_ci const char *path, u32 desired_access, 254062306a36Sopenharmony_ci u32 class, u32 type, u32 output_len, 254162306a36Sopenharmony_ci struct kvec *rsp, int *buftype, 254262306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb) 254362306a36Sopenharmony_ci{ 254462306a36Sopenharmony_ci struct smb2_compound_vars *vars; 254562306a36Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 254662306a36Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(ses); 254762306a36Sopenharmony_ci int flags = CIFS_CP_CREATE_CLOSE_OP; 254862306a36Sopenharmony_ci struct smb_rqst *rqst; 254962306a36Sopenharmony_ci int resp_buftype[3]; 255062306a36Sopenharmony_ci struct kvec *rsp_iov; 255162306a36Sopenharmony_ci u8 oplock = SMB2_OPLOCK_LEVEL_NONE; 255262306a36Sopenharmony_ci struct cifs_open_parms oparms; 255362306a36Sopenharmony_ci struct cifs_fid fid; 255462306a36Sopenharmony_ci int rc; 255562306a36Sopenharmony_ci __le16 *utf16_path; 255662306a36Sopenharmony_ci struct cached_fid *cfid = NULL; 255762306a36Sopenharmony_ci 255862306a36Sopenharmony_ci if (!path) 255962306a36Sopenharmony_ci path = ""; 256062306a36Sopenharmony_ci utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); 256162306a36Sopenharmony_ci if (!utf16_path) 256262306a36Sopenharmony_ci return -ENOMEM; 256362306a36Sopenharmony_ci 256462306a36Sopenharmony_ci if (smb3_encryption_required(tcon)) 256562306a36Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 256662306a36Sopenharmony_ci 256762306a36Sopenharmony_ci resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; 256862306a36Sopenharmony_ci vars = kzalloc(sizeof(*vars), GFP_KERNEL); 256962306a36Sopenharmony_ci if (!vars) { 257062306a36Sopenharmony_ci rc = -ENOMEM; 257162306a36Sopenharmony_ci goto out_free_path; 257262306a36Sopenharmony_ci } 257362306a36Sopenharmony_ci rqst = vars->rqst; 257462306a36Sopenharmony_ci rsp_iov = vars->rsp_iov; 257562306a36Sopenharmony_ci 257662306a36Sopenharmony_ci /* 257762306a36Sopenharmony_ci * We can only call this for things we know are directories. 257862306a36Sopenharmony_ci */ 257962306a36Sopenharmony_ci if (!strcmp(path, "")) 258062306a36Sopenharmony_ci open_cached_dir(xid, tcon, path, cifs_sb, false, 258162306a36Sopenharmony_ci &cfid); /* cfid null if open dir failed */ 258262306a36Sopenharmony_ci 258362306a36Sopenharmony_ci rqst[0].rq_iov = vars->open_iov; 258462306a36Sopenharmony_ci rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; 258562306a36Sopenharmony_ci 258662306a36Sopenharmony_ci oparms = (struct cifs_open_parms) { 258762306a36Sopenharmony_ci .tcon = tcon, 258862306a36Sopenharmony_ci .path = path, 258962306a36Sopenharmony_ci .desired_access = desired_access, 259062306a36Sopenharmony_ci .disposition = FILE_OPEN, 259162306a36Sopenharmony_ci .create_options = cifs_create_options(cifs_sb, 0), 259262306a36Sopenharmony_ci .fid = &fid, 259362306a36Sopenharmony_ci }; 259462306a36Sopenharmony_ci 259562306a36Sopenharmony_ci rc = SMB2_open_init(tcon, server, 259662306a36Sopenharmony_ci &rqst[0], &oplock, &oparms, utf16_path); 259762306a36Sopenharmony_ci if (rc) 259862306a36Sopenharmony_ci goto qic_exit; 259962306a36Sopenharmony_ci smb2_set_next_command(tcon, &rqst[0]); 260062306a36Sopenharmony_ci 260162306a36Sopenharmony_ci rqst[1].rq_iov = &vars->qi_iov; 260262306a36Sopenharmony_ci rqst[1].rq_nvec = 1; 260362306a36Sopenharmony_ci 260462306a36Sopenharmony_ci if (cfid) { 260562306a36Sopenharmony_ci rc = SMB2_query_info_init(tcon, server, 260662306a36Sopenharmony_ci &rqst[1], 260762306a36Sopenharmony_ci cfid->fid.persistent_fid, 260862306a36Sopenharmony_ci cfid->fid.volatile_fid, 260962306a36Sopenharmony_ci class, type, 0, 261062306a36Sopenharmony_ci output_len, 0, 261162306a36Sopenharmony_ci NULL); 261262306a36Sopenharmony_ci } else { 261362306a36Sopenharmony_ci rc = SMB2_query_info_init(tcon, server, 261462306a36Sopenharmony_ci &rqst[1], 261562306a36Sopenharmony_ci COMPOUND_FID, 261662306a36Sopenharmony_ci COMPOUND_FID, 261762306a36Sopenharmony_ci class, type, 0, 261862306a36Sopenharmony_ci output_len, 0, 261962306a36Sopenharmony_ci NULL); 262062306a36Sopenharmony_ci } 262162306a36Sopenharmony_ci if (rc) 262262306a36Sopenharmony_ci goto qic_exit; 262362306a36Sopenharmony_ci if (!cfid) { 262462306a36Sopenharmony_ci smb2_set_next_command(tcon, &rqst[1]); 262562306a36Sopenharmony_ci smb2_set_related(&rqst[1]); 262662306a36Sopenharmony_ci } 262762306a36Sopenharmony_ci 262862306a36Sopenharmony_ci rqst[2].rq_iov = &vars->close_iov; 262962306a36Sopenharmony_ci rqst[2].rq_nvec = 1; 263062306a36Sopenharmony_ci 263162306a36Sopenharmony_ci rc = SMB2_close_init(tcon, server, 263262306a36Sopenharmony_ci &rqst[2], COMPOUND_FID, COMPOUND_FID, false); 263362306a36Sopenharmony_ci if (rc) 263462306a36Sopenharmony_ci goto qic_exit; 263562306a36Sopenharmony_ci smb2_set_related(&rqst[2]); 263662306a36Sopenharmony_ci 263762306a36Sopenharmony_ci if (cfid) { 263862306a36Sopenharmony_ci rc = compound_send_recv(xid, ses, server, 263962306a36Sopenharmony_ci flags, 1, &rqst[1], 264062306a36Sopenharmony_ci &resp_buftype[1], &rsp_iov[1]); 264162306a36Sopenharmony_ci } else { 264262306a36Sopenharmony_ci rc = compound_send_recv(xid, ses, server, 264362306a36Sopenharmony_ci flags, 3, rqst, 264462306a36Sopenharmony_ci resp_buftype, rsp_iov); 264562306a36Sopenharmony_ci } 264662306a36Sopenharmony_ci if (rc) { 264762306a36Sopenharmony_ci free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); 264862306a36Sopenharmony_ci if (rc == -EREMCHG) { 264962306a36Sopenharmony_ci tcon->need_reconnect = true; 265062306a36Sopenharmony_ci pr_warn_once("server share %s deleted\n", 265162306a36Sopenharmony_ci tcon->tree_name); 265262306a36Sopenharmony_ci } 265362306a36Sopenharmony_ci goto qic_exit; 265462306a36Sopenharmony_ci } 265562306a36Sopenharmony_ci *rsp = rsp_iov[1]; 265662306a36Sopenharmony_ci *buftype = resp_buftype[1]; 265762306a36Sopenharmony_ci 265862306a36Sopenharmony_ci qic_exit: 265962306a36Sopenharmony_ci SMB2_open_free(&rqst[0]); 266062306a36Sopenharmony_ci SMB2_query_info_free(&rqst[1]); 266162306a36Sopenharmony_ci SMB2_close_free(&rqst[2]); 266262306a36Sopenharmony_ci free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); 266362306a36Sopenharmony_ci free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); 266462306a36Sopenharmony_ci if (cfid) 266562306a36Sopenharmony_ci close_cached_dir(cfid); 266662306a36Sopenharmony_ci kfree(vars); 266762306a36Sopenharmony_ciout_free_path: 266862306a36Sopenharmony_ci kfree(utf16_path); 266962306a36Sopenharmony_ci return rc; 267062306a36Sopenharmony_ci} 267162306a36Sopenharmony_ci 267262306a36Sopenharmony_cistatic int 267362306a36Sopenharmony_cismb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon, 267462306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb, struct kstatfs *buf) 267562306a36Sopenharmony_ci{ 267662306a36Sopenharmony_ci struct smb2_query_info_rsp *rsp; 267762306a36Sopenharmony_ci struct smb2_fs_full_size_info *info = NULL; 267862306a36Sopenharmony_ci struct kvec rsp_iov = {NULL, 0}; 267962306a36Sopenharmony_ci int buftype = CIFS_NO_BUFFER; 268062306a36Sopenharmony_ci int rc; 268162306a36Sopenharmony_ci 268262306a36Sopenharmony_ci 268362306a36Sopenharmony_ci rc = smb2_query_info_compound(xid, tcon, "", 268462306a36Sopenharmony_ci FILE_READ_ATTRIBUTES, 268562306a36Sopenharmony_ci FS_FULL_SIZE_INFORMATION, 268662306a36Sopenharmony_ci SMB2_O_INFO_FILESYSTEM, 268762306a36Sopenharmony_ci sizeof(struct smb2_fs_full_size_info), 268862306a36Sopenharmony_ci &rsp_iov, &buftype, cifs_sb); 268962306a36Sopenharmony_ci if (rc) 269062306a36Sopenharmony_ci goto qfs_exit; 269162306a36Sopenharmony_ci 269262306a36Sopenharmony_ci rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; 269362306a36Sopenharmony_ci buf->f_type = SMB2_SUPER_MAGIC; 269462306a36Sopenharmony_ci info = (struct smb2_fs_full_size_info *)( 269562306a36Sopenharmony_ci le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp); 269662306a36Sopenharmony_ci rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), 269762306a36Sopenharmony_ci le32_to_cpu(rsp->OutputBufferLength), 269862306a36Sopenharmony_ci &rsp_iov, 269962306a36Sopenharmony_ci sizeof(struct smb2_fs_full_size_info)); 270062306a36Sopenharmony_ci if (!rc) 270162306a36Sopenharmony_ci smb2_copy_fs_info_to_kstatfs(info, buf); 270262306a36Sopenharmony_ci 270362306a36Sopenharmony_ciqfs_exit: 270462306a36Sopenharmony_ci trace_smb3_qfs_done(xid, tcon->tid, tcon->ses->Suid, tcon->tree_name, rc); 270562306a36Sopenharmony_ci free_rsp_buf(buftype, rsp_iov.iov_base); 270662306a36Sopenharmony_ci return rc; 270762306a36Sopenharmony_ci} 270862306a36Sopenharmony_ci 270962306a36Sopenharmony_cistatic int 271062306a36Sopenharmony_cismb311_queryfs(const unsigned int xid, struct cifs_tcon *tcon, 271162306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb, struct kstatfs *buf) 271262306a36Sopenharmony_ci{ 271362306a36Sopenharmony_ci int rc; 271462306a36Sopenharmony_ci __le16 srch_path = 0; /* Null - open root of share */ 271562306a36Sopenharmony_ci u8 oplock = SMB2_OPLOCK_LEVEL_NONE; 271662306a36Sopenharmony_ci struct cifs_open_parms oparms; 271762306a36Sopenharmony_ci struct cifs_fid fid; 271862306a36Sopenharmony_ci 271962306a36Sopenharmony_ci if (!tcon->posix_extensions) 272062306a36Sopenharmony_ci return smb2_queryfs(xid, tcon, cifs_sb, buf); 272162306a36Sopenharmony_ci 272262306a36Sopenharmony_ci oparms = (struct cifs_open_parms) { 272362306a36Sopenharmony_ci .tcon = tcon, 272462306a36Sopenharmony_ci .path = "", 272562306a36Sopenharmony_ci .desired_access = FILE_READ_ATTRIBUTES, 272662306a36Sopenharmony_ci .disposition = FILE_OPEN, 272762306a36Sopenharmony_ci .create_options = cifs_create_options(cifs_sb, 0), 272862306a36Sopenharmony_ci .fid = &fid, 272962306a36Sopenharmony_ci }; 273062306a36Sopenharmony_ci 273162306a36Sopenharmony_ci rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL, 273262306a36Sopenharmony_ci NULL, NULL); 273362306a36Sopenharmony_ci if (rc) 273462306a36Sopenharmony_ci return rc; 273562306a36Sopenharmony_ci 273662306a36Sopenharmony_ci rc = SMB311_posix_qfs_info(xid, tcon, fid.persistent_fid, 273762306a36Sopenharmony_ci fid.volatile_fid, buf); 273862306a36Sopenharmony_ci buf->f_type = SMB2_SUPER_MAGIC; 273962306a36Sopenharmony_ci SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); 274062306a36Sopenharmony_ci return rc; 274162306a36Sopenharmony_ci} 274262306a36Sopenharmony_ci 274362306a36Sopenharmony_cistatic bool 274462306a36Sopenharmony_cismb2_compare_fids(struct cifsFileInfo *ob1, struct cifsFileInfo *ob2) 274562306a36Sopenharmony_ci{ 274662306a36Sopenharmony_ci return ob1->fid.persistent_fid == ob2->fid.persistent_fid && 274762306a36Sopenharmony_ci ob1->fid.volatile_fid == ob2->fid.volatile_fid; 274862306a36Sopenharmony_ci} 274962306a36Sopenharmony_ci 275062306a36Sopenharmony_cistatic int 275162306a36Sopenharmony_cismb2_mand_lock(const unsigned int xid, struct cifsFileInfo *cfile, __u64 offset, 275262306a36Sopenharmony_ci __u64 length, __u32 type, int lock, int unlock, bool wait) 275362306a36Sopenharmony_ci{ 275462306a36Sopenharmony_ci if (unlock && !lock) 275562306a36Sopenharmony_ci type = SMB2_LOCKFLAG_UNLOCK; 275662306a36Sopenharmony_ci return SMB2_lock(xid, tlink_tcon(cfile->tlink), 275762306a36Sopenharmony_ci cfile->fid.persistent_fid, cfile->fid.volatile_fid, 275862306a36Sopenharmony_ci current->tgid, length, offset, type, wait); 275962306a36Sopenharmony_ci} 276062306a36Sopenharmony_ci 276162306a36Sopenharmony_cistatic void 276262306a36Sopenharmony_cismb2_get_lease_key(struct inode *inode, struct cifs_fid *fid) 276362306a36Sopenharmony_ci{ 276462306a36Sopenharmony_ci memcpy(fid->lease_key, CIFS_I(inode)->lease_key, SMB2_LEASE_KEY_SIZE); 276562306a36Sopenharmony_ci} 276662306a36Sopenharmony_ci 276762306a36Sopenharmony_cistatic void 276862306a36Sopenharmony_cismb2_set_lease_key(struct inode *inode, struct cifs_fid *fid) 276962306a36Sopenharmony_ci{ 277062306a36Sopenharmony_ci memcpy(CIFS_I(inode)->lease_key, fid->lease_key, SMB2_LEASE_KEY_SIZE); 277162306a36Sopenharmony_ci} 277262306a36Sopenharmony_ci 277362306a36Sopenharmony_cistatic void 277462306a36Sopenharmony_cismb2_new_lease_key(struct cifs_fid *fid) 277562306a36Sopenharmony_ci{ 277662306a36Sopenharmony_ci generate_random_uuid(fid->lease_key); 277762306a36Sopenharmony_ci} 277862306a36Sopenharmony_ci 277962306a36Sopenharmony_cistatic int 278062306a36Sopenharmony_cismb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses, 278162306a36Sopenharmony_ci const char *search_name, 278262306a36Sopenharmony_ci struct dfs_info3_param **target_nodes, 278362306a36Sopenharmony_ci unsigned int *num_of_nodes, 278462306a36Sopenharmony_ci const struct nls_table *nls_codepage, int remap) 278562306a36Sopenharmony_ci{ 278662306a36Sopenharmony_ci int rc; 278762306a36Sopenharmony_ci __le16 *utf16_path = NULL; 278862306a36Sopenharmony_ci int utf16_path_len = 0; 278962306a36Sopenharmony_ci struct cifs_tcon *tcon; 279062306a36Sopenharmony_ci struct fsctl_get_dfs_referral_req *dfs_req = NULL; 279162306a36Sopenharmony_ci struct get_dfs_referral_rsp *dfs_rsp = NULL; 279262306a36Sopenharmony_ci u32 dfs_req_size = 0, dfs_rsp_size = 0; 279362306a36Sopenharmony_ci int retry_count = 0; 279462306a36Sopenharmony_ci 279562306a36Sopenharmony_ci cifs_dbg(FYI, "%s: path: %s\n", __func__, search_name); 279662306a36Sopenharmony_ci 279762306a36Sopenharmony_ci /* 279862306a36Sopenharmony_ci * Try to use the IPC tcon, otherwise just use any 279962306a36Sopenharmony_ci */ 280062306a36Sopenharmony_ci tcon = ses->tcon_ipc; 280162306a36Sopenharmony_ci if (tcon == NULL) { 280262306a36Sopenharmony_ci spin_lock(&cifs_tcp_ses_lock); 280362306a36Sopenharmony_ci tcon = list_first_entry_or_null(&ses->tcon_list, 280462306a36Sopenharmony_ci struct cifs_tcon, 280562306a36Sopenharmony_ci tcon_list); 280662306a36Sopenharmony_ci if (tcon) 280762306a36Sopenharmony_ci tcon->tc_count++; 280862306a36Sopenharmony_ci spin_unlock(&cifs_tcp_ses_lock); 280962306a36Sopenharmony_ci } 281062306a36Sopenharmony_ci 281162306a36Sopenharmony_ci if (tcon == NULL) { 281262306a36Sopenharmony_ci cifs_dbg(VFS, "session %p has no tcon available for a dfs referral request\n", 281362306a36Sopenharmony_ci ses); 281462306a36Sopenharmony_ci rc = -ENOTCONN; 281562306a36Sopenharmony_ci goto out; 281662306a36Sopenharmony_ci } 281762306a36Sopenharmony_ci 281862306a36Sopenharmony_ci utf16_path = cifs_strndup_to_utf16(search_name, PATH_MAX, 281962306a36Sopenharmony_ci &utf16_path_len, 282062306a36Sopenharmony_ci nls_codepage, remap); 282162306a36Sopenharmony_ci if (!utf16_path) { 282262306a36Sopenharmony_ci rc = -ENOMEM; 282362306a36Sopenharmony_ci goto out; 282462306a36Sopenharmony_ci } 282562306a36Sopenharmony_ci 282662306a36Sopenharmony_ci dfs_req_size = sizeof(*dfs_req) + utf16_path_len; 282762306a36Sopenharmony_ci dfs_req = kzalloc(dfs_req_size, GFP_KERNEL); 282862306a36Sopenharmony_ci if (!dfs_req) { 282962306a36Sopenharmony_ci rc = -ENOMEM; 283062306a36Sopenharmony_ci goto out; 283162306a36Sopenharmony_ci } 283262306a36Sopenharmony_ci 283362306a36Sopenharmony_ci /* Highest DFS referral version understood */ 283462306a36Sopenharmony_ci dfs_req->MaxReferralLevel = DFS_VERSION; 283562306a36Sopenharmony_ci 283662306a36Sopenharmony_ci /* Path to resolve in an UTF-16 null-terminated string */ 283762306a36Sopenharmony_ci memcpy(dfs_req->RequestFileName, utf16_path, utf16_path_len); 283862306a36Sopenharmony_ci 283962306a36Sopenharmony_ci do { 284062306a36Sopenharmony_ci rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, 284162306a36Sopenharmony_ci FSCTL_DFS_GET_REFERRALS, 284262306a36Sopenharmony_ci (char *)dfs_req, dfs_req_size, CIFSMaxBufSize, 284362306a36Sopenharmony_ci (char **)&dfs_rsp, &dfs_rsp_size); 284462306a36Sopenharmony_ci if (!is_retryable_error(rc)) 284562306a36Sopenharmony_ci break; 284662306a36Sopenharmony_ci usleep_range(512, 2048); 284762306a36Sopenharmony_ci } while (++retry_count < 5); 284862306a36Sopenharmony_ci 284962306a36Sopenharmony_ci if (!rc && !dfs_rsp) 285062306a36Sopenharmony_ci rc = -EIO; 285162306a36Sopenharmony_ci if (rc) { 285262306a36Sopenharmony_ci if (!is_retryable_error(rc) && rc != -ENOENT && rc != -EOPNOTSUPP) 285362306a36Sopenharmony_ci cifs_tcon_dbg(VFS, "%s: ioctl error: rc=%d\n", __func__, rc); 285462306a36Sopenharmony_ci goto out; 285562306a36Sopenharmony_ci } 285662306a36Sopenharmony_ci 285762306a36Sopenharmony_ci rc = parse_dfs_referrals(dfs_rsp, dfs_rsp_size, 285862306a36Sopenharmony_ci num_of_nodes, target_nodes, 285962306a36Sopenharmony_ci nls_codepage, remap, search_name, 286062306a36Sopenharmony_ci true /* is_unicode */); 286162306a36Sopenharmony_ci if (rc) { 286262306a36Sopenharmony_ci cifs_tcon_dbg(VFS, "parse error in %s rc=%d\n", __func__, rc); 286362306a36Sopenharmony_ci goto out; 286462306a36Sopenharmony_ci } 286562306a36Sopenharmony_ci 286662306a36Sopenharmony_ci out: 286762306a36Sopenharmony_ci if (tcon && !tcon->ipc) { 286862306a36Sopenharmony_ci /* ipc tcons are not refcounted */ 286962306a36Sopenharmony_ci spin_lock(&cifs_tcp_ses_lock); 287062306a36Sopenharmony_ci tcon->tc_count--; 287162306a36Sopenharmony_ci /* tc_count can never go negative */ 287262306a36Sopenharmony_ci WARN_ON(tcon->tc_count < 0); 287362306a36Sopenharmony_ci spin_unlock(&cifs_tcp_ses_lock); 287462306a36Sopenharmony_ci } 287562306a36Sopenharmony_ci kfree(utf16_path); 287662306a36Sopenharmony_ci kfree(dfs_req); 287762306a36Sopenharmony_ci kfree(dfs_rsp); 287862306a36Sopenharmony_ci return rc; 287962306a36Sopenharmony_ci} 288062306a36Sopenharmony_ci 288162306a36Sopenharmony_ci/* See MS-FSCC 2.1.2.6 for the 'NFS' style reparse tags */ 288262306a36Sopenharmony_cistatic int parse_reparse_posix(struct reparse_posix_data *buf, 288362306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb, 288462306a36Sopenharmony_ci struct cifs_open_info_data *data) 288562306a36Sopenharmony_ci{ 288662306a36Sopenharmony_ci unsigned int len; 288762306a36Sopenharmony_ci u64 type; 288862306a36Sopenharmony_ci 288962306a36Sopenharmony_ci switch ((type = le64_to_cpu(buf->InodeType))) { 289062306a36Sopenharmony_ci case NFS_SPECFILE_LNK: 289162306a36Sopenharmony_ci len = le16_to_cpu(buf->ReparseDataLength); 289262306a36Sopenharmony_ci data->symlink_target = cifs_strndup_from_utf16(buf->DataBuffer, 289362306a36Sopenharmony_ci len, true, 289462306a36Sopenharmony_ci cifs_sb->local_nls); 289562306a36Sopenharmony_ci if (!data->symlink_target) 289662306a36Sopenharmony_ci return -ENOMEM; 289762306a36Sopenharmony_ci convert_delimiter(data->symlink_target, '/'); 289862306a36Sopenharmony_ci cifs_dbg(FYI, "%s: target path: %s\n", 289962306a36Sopenharmony_ci __func__, data->symlink_target); 290062306a36Sopenharmony_ci break; 290162306a36Sopenharmony_ci case NFS_SPECFILE_CHR: 290262306a36Sopenharmony_ci case NFS_SPECFILE_BLK: 290362306a36Sopenharmony_ci case NFS_SPECFILE_FIFO: 290462306a36Sopenharmony_ci case NFS_SPECFILE_SOCK: 290562306a36Sopenharmony_ci break; 290662306a36Sopenharmony_ci default: 290762306a36Sopenharmony_ci cifs_dbg(VFS, "%s: unhandled inode type: 0x%llx\n", 290862306a36Sopenharmony_ci __func__, type); 290962306a36Sopenharmony_ci return -EOPNOTSUPP; 291062306a36Sopenharmony_ci } 291162306a36Sopenharmony_ci return 0; 291262306a36Sopenharmony_ci} 291362306a36Sopenharmony_ci 291462306a36Sopenharmony_cistatic int parse_reparse_symlink(struct reparse_symlink_data_buffer *sym, 291562306a36Sopenharmony_ci u32 plen, bool unicode, 291662306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb, 291762306a36Sopenharmony_ci struct cifs_open_info_data *data) 291862306a36Sopenharmony_ci{ 291962306a36Sopenharmony_ci unsigned int len; 292062306a36Sopenharmony_ci unsigned int offs; 292162306a36Sopenharmony_ci 292262306a36Sopenharmony_ci /* We handle Symbolic Link reparse tag here. See: MS-FSCC 2.1.2.4 */ 292362306a36Sopenharmony_ci 292462306a36Sopenharmony_ci offs = le16_to_cpu(sym->SubstituteNameOffset); 292562306a36Sopenharmony_ci len = le16_to_cpu(sym->SubstituteNameLength); 292662306a36Sopenharmony_ci if (offs + 20 > plen || offs + len + 20 > plen) { 292762306a36Sopenharmony_ci cifs_dbg(VFS, "srv returned malformed symlink buffer\n"); 292862306a36Sopenharmony_ci return -EIO; 292962306a36Sopenharmony_ci } 293062306a36Sopenharmony_ci 293162306a36Sopenharmony_ci data->symlink_target = cifs_strndup_from_utf16(sym->PathBuffer + offs, 293262306a36Sopenharmony_ci len, unicode, 293362306a36Sopenharmony_ci cifs_sb->local_nls); 293462306a36Sopenharmony_ci if (!data->symlink_target) 293562306a36Sopenharmony_ci return -ENOMEM; 293662306a36Sopenharmony_ci 293762306a36Sopenharmony_ci convert_delimiter(data->symlink_target, '/'); 293862306a36Sopenharmony_ci cifs_dbg(FYI, "%s: target path: %s\n", __func__, data->symlink_target); 293962306a36Sopenharmony_ci 294062306a36Sopenharmony_ci return 0; 294162306a36Sopenharmony_ci} 294262306a36Sopenharmony_ci 294362306a36Sopenharmony_ciint parse_reparse_point(struct reparse_data_buffer *buf, 294462306a36Sopenharmony_ci u32 plen, struct cifs_sb_info *cifs_sb, 294562306a36Sopenharmony_ci bool unicode, struct cifs_open_info_data *data) 294662306a36Sopenharmony_ci{ 294762306a36Sopenharmony_ci if (plen < sizeof(*buf)) { 294862306a36Sopenharmony_ci cifs_dbg(VFS, "%s: reparse buffer is too small. Must be at least 8 bytes but was %d\n", 294962306a36Sopenharmony_ci __func__, plen); 295062306a36Sopenharmony_ci return -EIO; 295162306a36Sopenharmony_ci } 295262306a36Sopenharmony_ci 295362306a36Sopenharmony_ci if (plen < le16_to_cpu(buf->ReparseDataLength) + sizeof(*buf)) { 295462306a36Sopenharmony_ci cifs_dbg(VFS, "%s: invalid reparse buf length: %d\n", 295562306a36Sopenharmony_ci __func__, plen); 295662306a36Sopenharmony_ci return -EIO; 295762306a36Sopenharmony_ci } 295862306a36Sopenharmony_ci 295962306a36Sopenharmony_ci data->reparse.buf = buf; 296062306a36Sopenharmony_ci 296162306a36Sopenharmony_ci /* See MS-FSCC 2.1.2 */ 296262306a36Sopenharmony_ci switch (le32_to_cpu(buf->ReparseTag)) { 296362306a36Sopenharmony_ci case IO_REPARSE_TAG_NFS: 296462306a36Sopenharmony_ci return parse_reparse_posix((struct reparse_posix_data *)buf, 296562306a36Sopenharmony_ci cifs_sb, data); 296662306a36Sopenharmony_ci case IO_REPARSE_TAG_SYMLINK: 296762306a36Sopenharmony_ci return parse_reparse_symlink( 296862306a36Sopenharmony_ci (struct reparse_symlink_data_buffer *)buf, 296962306a36Sopenharmony_ci plen, unicode, cifs_sb, data); 297062306a36Sopenharmony_ci case IO_REPARSE_TAG_LX_SYMLINK: 297162306a36Sopenharmony_ci case IO_REPARSE_TAG_AF_UNIX: 297262306a36Sopenharmony_ci case IO_REPARSE_TAG_LX_FIFO: 297362306a36Sopenharmony_ci case IO_REPARSE_TAG_LX_CHR: 297462306a36Sopenharmony_ci case IO_REPARSE_TAG_LX_BLK: 297562306a36Sopenharmony_ci return 0; 297662306a36Sopenharmony_ci default: 297762306a36Sopenharmony_ci cifs_dbg(VFS, "%s: unhandled reparse tag: 0x%08x\n", 297862306a36Sopenharmony_ci __func__, le32_to_cpu(buf->ReparseTag)); 297962306a36Sopenharmony_ci return -EOPNOTSUPP; 298062306a36Sopenharmony_ci } 298162306a36Sopenharmony_ci} 298262306a36Sopenharmony_ci 298362306a36Sopenharmony_cistatic int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb, 298462306a36Sopenharmony_ci struct kvec *rsp_iov, 298562306a36Sopenharmony_ci struct cifs_open_info_data *data) 298662306a36Sopenharmony_ci{ 298762306a36Sopenharmony_ci struct reparse_data_buffer *buf; 298862306a36Sopenharmony_ci struct smb2_ioctl_rsp *io = rsp_iov->iov_base; 298962306a36Sopenharmony_ci u32 plen = le32_to_cpu(io->OutputCount); 299062306a36Sopenharmony_ci 299162306a36Sopenharmony_ci buf = (struct reparse_data_buffer *)((u8 *)io + 299262306a36Sopenharmony_ci le32_to_cpu(io->OutputOffset)); 299362306a36Sopenharmony_ci return parse_reparse_point(buf, plen, cifs_sb, true, data); 299462306a36Sopenharmony_ci} 299562306a36Sopenharmony_ci 299662306a36Sopenharmony_cistatic int smb2_query_reparse_point(const unsigned int xid, 299762306a36Sopenharmony_ci struct cifs_tcon *tcon, 299862306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb, 299962306a36Sopenharmony_ci const char *full_path, 300062306a36Sopenharmony_ci u32 *tag, struct kvec *rsp, 300162306a36Sopenharmony_ci int *rsp_buftype) 300262306a36Sopenharmony_ci{ 300362306a36Sopenharmony_ci struct smb2_compound_vars *vars; 300462306a36Sopenharmony_ci int rc; 300562306a36Sopenharmony_ci __le16 *utf16_path = NULL; 300662306a36Sopenharmony_ci __u8 oplock = SMB2_OPLOCK_LEVEL_NONE; 300762306a36Sopenharmony_ci struct cifs_open_parms oparms; 300862306a36Sopenharmony_ci struct cifs_fid fid; 300962306a36Sopenharmony_ci struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); 301062306a36Sopenharmony_ci int flags = CIFS_CP_CREATE_CLOSE_OP; 301162306a36Sopenharmony_ci struct smb_rqst *rqst; 301262306a36Sopenharmony_ci int resp_buftype[3]; 301362306a36Sopenharmony_ci struct kvec *rsp_iov; 301462306a36Sopenharmony_ci struct smb2_ioctl_rsp *ioctl_rsp; 301562306a36Sopenharmony_ci struct reparse_data_buffer *reparse_buf; 301662306a36Sopenharmony_ci u32 off, count, len; 301762306a36Sopenharmony_ci 301862306a36Sopenharmony_ci cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path); 301962306a36Sopenharmony_ci 302062306a36Sopenharmony_ci if (smb3_encryption_required(tcon)) 302162306a36Sopenharmony_ci flags |= CIFS_TRANSFORM_REQ; 302262306a36Sopenharmony_ci 302362306a36Sopenharmony_ci utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); 302462306a36Sopenharmony_ci if (!utf16_path) 302562306a36Sopenharmony_ci return -ENOMEM; 302662306a36Sopenharmony_ci 302762306a36Sopenharmony_ci resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER; 302862306a36Sopenharmony_ci vars = kzalloc(sizeof(*vars), GFP_KERNEL); 302962306a36Sopenharmony_ci if (!vars) { 303062306a36Sopenharmony_ci rc = -ENOMEM; 303162306a36Sopenharmony_ci goto out_free_path; 303262306a36Sopenharmony_ci } 303362306a36Sopenharmony_ci rqst = vars->rqst; 303462306a36Sopenharmony_ci rsp_iov = vars->rsp_iov; 303562306a36Sopenharmony_ci 303662306a36Sopenharmony_ci /* 303762306a36Sopenharmony_ci * setup smb2open - TODO add optimization to call cifs_get_readable_path 303862306a36Sopenharmony_ci * to see if there is a handle already open that we can use 303962306a36Sopenharmony_ci */ 304062306a36Sopenharmony_ci rqst[0].rq_iov = vars->open_iov; 304162306a36Sopenharmony_ci rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE; 304262306a36Sopenharmony_ci 304362306a36Sopenharmony_ci oparms = (struct cifs_open_parms) { 304462306a36Sopenharmony_ci .tcon = tcon, 304562306a36Sopenharmony_ci .path = full_path, 304662306a36Sopenharmony_ci .desired_access = FILE_READ_ATTRIBUTES, 304762306a36Sopenharmony_ci .disposition = FILE_OPEN, 304862306a36Sopenharmony_ci .create_options = cifs_create_options(cifs_sb, OPEN_REPARSE_POINT), 304962306a36Sopenharmony_ci .fid = &fid, 305062306a36Sopenharmony_ci }; 305162306a36Sopenharmony_ci 305262306a36Sopenharmony_ci rc = SMB2_open_init(tcon, server, 305362306a36Sopenharmony_ci &rqst[0], &oplock, &oparms, utf16_path); 305462306a36Sopenharmony_ci if (rc) 305562306a36Sopenharmony_ci goto query_rp_exit; 305662306a36Sopenharmony_ci smb2_set_next_command(tcon, &rqst[0]); 305762306a36Sopenharmony_ci 305862306a36Sopenharmony_ci 305962306a36Sopenharmony_ci /* IOCTL */ 306062306a36Sopenharmony_ci rqst[1].rq_iov = vars->io_iov; 306162306a36Sopenharmony_ci rqst[1].rq_nvec = SMB2_IOCTL_IOV_SIZE; 306262306a36Sopenharmony_ci 306362306a36Sopenharmony_ci rc = SMB2_ioctl_init(tcon, server, 306462306a36Sopenharmony_ci &rqst[1], COMPOUND_FID, 306562306a36Sopenharmony_ci COMPOUND_FID, FSCTL_GET_REPARSE_POINT, NULL, 0, 306662306a36Sopenharmony_ci CIFSMaxBufSize - 306762306a36Sopenharmony_ci MAX_SMB2_CREATE_RESPONSE_SIZE - 306862306a36Sopenharmony_ci MAX_SMB2_CLOSE_RESPONSE_SIZE); 306962306a36Sopenharmony_ci if (rc) 307062306a36Sopenharmony_ci goto query_rp_exit; 307162306a36Sopenharmony_ci 307262306a36Sopenharmony_ci smb2_set_next_command(tcon, &rqst[1]); 307362306a36Sopenharmony_ci smb2_set_related(&rqst[1]); 307462306a36Sopenharmony_ci 307562306a36Sopenharmony_ci /* Close */ 307662306a36Sopenharmony_ci rqst[2].rq_iov = &vars->close_iov; 307762306a36Sopenharmony_ci rqst[2].rq_nvec = 1; 307862306a36Sopenharmony_ci 307962306a36Sopenharmony_ci rc = SMB2_close_init(tcon, server, 308062306a36Sopenharmony_ci &rqst[2], COMPOUND_FID, COMPOUND_FID, false); 308162306a36Sopenharmony_ci if (rc) 308262306a36Sopenharmony_ci goto query_rp_exit; 308362306a36Sopenharmony_ci 308462306a36Sopenharmony_ci smb2_set_related(&rqst[2]); 308562306a36Sopenharmony_ci 308662306a36Sopenharmony_ci rc = compound_send_recv(xid, tcon->ses, server, 308762306a36Sopenharmony_ci flags, 3, rqst, 308862306a36Sopenharmony_ci resp_buftype, rsp_iov); 308962306a36Sopenharmony_ci 309062306a36Sopenharmony_ci ioctl_rsp = rsp_iov[1].iov_base; 309162306a36Sopenharmony_ci 309262306a36Sopenharmony_ci /* 309362306a36Sopenharmony_ci * Open was successful and we got an ioctl response. 309462306a36Sopenharmony_ci */ 309562306a36Sopenharmony_ci if (rc == 0) { 309662306a36Sopenharmony_ci /* See MS-FSCC 2.3.23 */ 309762306a36Sopenharmony_ci off = le32_to_cpu(ioctl_rsp->OutputOffset); 309862306a36Sopenharmony_ci count = le32_to_cpu(ioctl_rsp->OutputCount); 309962306a36Sopenharmony_ci if (check_add_overflow(off, count, &len) || 310062306a36Sopenharmony_ci len > rsp_iov[1].iov_len) { 310162306a36Sopenharmony_ci cifs_tcon_dbg(VFS, "%s: invalid ioctl: off=%d count=%d\n", 310262306a36Sopenharmony_ci __func__, off, count); 310362306a36Sopenharmony_ci rc = -EIO; 310462306a36Sopenharmony_ci goto query_rp_exit; 310562306a36Sopenharmony_ci } 310662306a36Sopenharmony_ci 310762306a36Sopenharmony_ci reparse_buf = (void *)((u8 *)ioctl_rsp + off); 310862306a36Sopenharmony_ci len = sizeof(*reparse_buf); 310962306a36Sopenharmony_ci if (count < len || 311062306a36Sopenharmony_ci count < le16_to_cpu(reparse_buf->ReparseDataLength) + len) { 311162306a36Sopenharmony_ci cifs_tcon_dbg(VFS, "%s: invalid ioctl: off=%d count=%d\n", 311262306a36Sopenharmony_ci __func__, off, count); 311362306a36Sopenharmony_ci rc = -EIO; 311462306a36Sopenharmony_ci goto query_rp_exit; 311562306a36Sopenharmony_ci } 311662306a36Sopenharmony_ci *tag = le32_to_cpu(reparse_buf->ReparseTag); 311762306a36Sopenharmony_ci *rsp = rsp_iov[1]; 311862306a36Sopenharmony_ci *rsp_buftype = resp_buftype[1]; 311962306a36Sopenharmony_ci resp_buftype[1] = CIFS_NO_BUFFER; 312062306a36Sopenharmony_ci } 312162306a36Sopenharmony_ci 312262306a36Sopenharmony_ci query_rp_exit: 312362306a36Sopenharmony_ci SMB2_open_free(&rqst[0]); 312462306a36Sopenharmony_ci SMB2_ioctl_free(&rqst[1]); 312562306a36Sopenharmony_ci SMB2_close_free(&rqst[2]); 312662306a36Sopenharmony_ci free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); 312762306a36Sopenharmony_ci free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); 312862306a36Sopenharmony_ci free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base); 312962306a36Sopenharmony_ci kfree(vars); 313062306a36Sopenharmony_ciout_free_path: 313162306a36Sopenharmony_ci kfree(utf16_path); 313262306a36Sopenharmony_ci return rc; 313362306a36Sopenharmony_ci} 313462306a36Sopenharmony_ci 313562306a36Sopenharmony_cistatic struct cifs_ntsd * 313662306a36Sopenharmony_ciget_smb2_acl_by_fid(struct cifs_sb_info *cifs_sb, 313762306a36Sopenharmony_ci const struct cifs_fid *cifsfid, u32 *pacllen, u32 info) 313862306a36Sopenharmony_ci{ 313962306a36Sopenharmony_ci struct cifs_ntsd *pntsd = NULL; 314062306a36Sopenharmony_ci unsigned int xid; 314162306a36Sopenharmony_ci int rc = -EOPNOTSUPP; 314262306a36Sopenharmony_ci struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); 314362306a36Sopenharmony_ci 314462306a36Sopenharmony_ci if (IS_ERR(tlink)) 314562306a36Sopenharmony_ci return ERR_CAST(tlink); 314662306a36Sopenharmony_ci 314762306a36Sopenharmony_ci xid = get_xid(); 314862306a36Sopenharmony_ci cifs_dbg(FYI, "trying to get acl\n"); 314962306a36Sopenharmony_ci 315062306a36Sopenharmony_ci rc = SMB2_query_acl(xid, tlink_tcon(tlink), cifsfid->persistent_fid, 315162306a36Sopenharmony_ci cifsfid->volatile_fid, (void **)&pntsd, pacllen, 315262306a36Sopenharmony_ci info); 315362306a36Sopenharmony_ci free_xid(xid); 315462306a36Sopenharmony_ci 315562306a36Sopenharmony_ci cifs_put_tlink(tlink); 315662306a36Sopenharmony_ci 315762306a36Sopenharmony_ci cifs_dbg(FYI, "%s: rc = %d ACL len %d\n", __func__, rc, *pacllen); 315862306a36Sopenharmony_ci if (rc) 315962306a36Sopenharmony_ci return ERR_PTR(rc); 316062306a36Sopenharmony_ci return pntsd; 316162306a36Sopenharmony_ci 316262306a36Sopenharmony_ci} 316362306a36Sopenharmony_ci 316462306a36Sopenharmony_cistatic struct cifs_ntsd * 316562306a36Sopenharmony_ciget_smb2_acl_by_path(struct cifs_sb_info *cifs_sb, 316662306a36Sopenharmony_ci const char *path, u32 *pacllen, u32 info) 316762306a36Sopenharmony_ci{ 316862306a36Sopenharmony_ci struct cifs_ntsd *pntsd = NULL; 316962306a36Sopenharmony_ci u8 oplock = SMB2_OPLOCK_LEVEL_NONE; 317062306a36Sopenharmony_ci unsigned int xid; 317162306a36Sopenharmony_ci int rc; 317262306a36Sopenharmony_ci struct cifs_tcon *tcon; 317362306a36Sopenharmony_ci struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); 317462306a36Sopenharmony_ci struct cifs_fid fid; 317562306a36Sopenharmony_ci struct cifs_open_parms oparms; 317662306a36Sopenharmony_ci __le16 *utf16_path; 317762306a36Sopenharmony_ci 317862306a36Sopenharmony_ci cifs_dbg(FYI, "get smb3 acl for path %s\n", path); 317962306a36Sopenharmony_ci if (IS_ERR(tlink)) 318062306a36Sopenharmony_ci return ERR_CAST(tlink); 318162306a36Sopenharmony_ci 318262306a36Sopenharmony_ci tcon = tlink_tcon(tlink); 318362306a36Sopenharmony_ci xid = get_xid(); 318462306a36Sopenharmony_ci 318562306a36Sopenharmony_ci utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); 318662306a36Sopenharmony_ci if (!utf16_path) { 318762306a36Sopenharmony_ci rc = -ENOMEM; 318862306a36Sopenharmony_ci free_xid(xid); 318962306a36Sopenharmony_ci return ERR_PTR(rc); 319062306a36Sopenharmony_ci } 319162306a36Sopenharmony_ci 319262306a36Sopenharmony_ci oparms = (struct cifs_open_parms) { 319362306a36Sopenharmony_ci .tcon = tcon, 319462306a36Sopenharmony_ci .path = path, 319562306a36Sopenharmony_ci .desired_access = READ_CONTROL, 319662306a36Sopenharmony_ci .disposition = FILE_OPEN, 319762306a36Sopenharmony_ci /* 319862306a36Sopenharmony_ci * When querying an ACL, even if the file is a symlink 319962306a36Sopenharmony_ci * we want to open the source not the target, and so 320062306a36Sopenharmony_ci * the protocol requires that the client specify this 320162306a36Sopenharmony_ci * flag when opening a reparse point 320262306a36Sopenharmony_ci */ 320362306a36Sopenharmony_ci .create_options = cifs_create_options(cifs_sb, 0) | 320462306a36Sopenharmony_ci OPEN_REPARSE_POINT, 320562306a36Sopenharmony_ci .fid = &fid, 320662306a36Sopenharmony_ci }; 320762306a36Sopenharmony_ci 320862306a36Sopenharmony_ci if (info & SACL_SECINFO) 320962306a36Sopenharmony_ci oparms.desired_access |= SYSTEM_SECURITY; 321062306a36Sopenharmony_ci 321162306a36Sopenharmony_ci rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL, 321262306a36Sopenharmony_ci NULL); 321362306a36Sopenharmony_ci kfree(utf16_path); 321462306a36Sopenharmony_ci if (!rc) { 321562306a36Sopenharmony_ci rc = SMB2_query_acl(xid, tlink_tcon(tlink), fid.persistent_fid, 321662306a36Sopenharmony_ci fid.volatile_fid, (void **)&pntsd, pacllen, 321762306a36Sopenharmony_ci info); 321862306a36Sopenharmony_ci SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); 321962306a36Sopenharmony_ci } 322062306a36Sopenharmony_ci 322162306a36Sopenharmony_ci cifs_put_tlink(tlink); 322262306a36Sopenharmony_ci free_xid(xid); 322362306a36Sopenharmony_ci 322462306a36Sopenharmony_ci cifs_dbg(FYI, "%s: rc = %d ACL len %d\n", __func__, rc, *pacllen); 322562306a36Sopenharmony_ci if (rc) 322662306a36Sopenharmony_ci return ERR_PTR(rc); 322762306a36Sopenharmony_ci return pntsd; 322862306a36Sopenharmony_ci} 322962306a36Sopenharmony_ci 323062306a36Sopenharmony_cistatic int 323162306a36Sopenharmony_ciset_smb2_acl(struct cifs_ntsd *pnntsd, __u32 acllen, 323262306a36Sopenharmony_ci struct inode *inode, const char *path, int aclflag) 323362306a36Sopenharmony_ci{ 323462306a36Sopenharmony_ci u8 oplock = SMB2_OPLOCK_LEVEL_NONE; 323562306a36Sopenharmony_ci unsigned int xid; 323662306a36Sopenharmony_ci int rc, access_flags = 0; 323762306a36Sopenharmony_ci struct cifs_tcon *tcon; 323862306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); 323962306a36Sopenharmony_ci struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); 324062306a36Sopenharmony_ci struct cifs_fid fid; 324162306a36Sopenharmony_ci struct cifs_open_parms oparms; 324262306a36Sopenharmony_ci __le16 *utf16_path; 324362306a36Sopenharmony_ci 324462306a36Sopenharmony_ci cifs_dbg(FYI, "set smb3 acl for path %s\n", path); 324562306a36Sopenharmony_ci if (IS_ERR(tlink)) 324662306a36Sopenharmony_ci return PTR_ERR(tlink); 324762306a36Sopenharmony_ci 324862306a36Sopenharmony_ci tcon = tlink_tcon(tlink); 324962306a36Sopenharmony_ci xid = get_xid(); 325062306a36Sopenharmony_ci 325162306a36Sopenharmony_ci if (aclflag & CIFS_ACL_OWNER || aclflag & CIFS_ACL_GROUP) 325262306a36Sopenharmony_ci access_flags |= WRITE_OWNER; 325362306a36Sopenharmony_ci if (aclflag & CIFS_ACL_SACL) 325462306a36Sopenharmony_ci access_flags |= SYSTEM_SECURITY; 325562306a36Sopenharmony_ci if (aclflag & CIFS_ACL_DACL) 325662306a36Sopenharmony_ci access_flags |= WRITE_DAC; 325762306a36Sopenharmony_ci 325862306a36Sopenharmony_ci utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); 325962306a36Sopenharmony_ci if (!utf16_path) { 326062306a36Sopenharmony_ci rc = -ENOMEM; 326162306a36Sopenharmony_ci free_xid(xid); 326262306a36Sopenharmony_ci return rc; 326362306a36Sopenharmony_ci } 326462306a36Sopenharmony_ci 326562306a36Sopenharmony_ci oparms = (struct cifs_open_parms) { 326662306a36Sopenharmony_ci .tcon = tcon, 326762306a36Sopenharmony_ci .desired_access = access_flags, 326862306a36Sopenharmony_ci .create_options = cifs_create_options(cifs_sb, 0), 326962306a36Sopenharmony_ci .disposition = FILE_OPEN, 327062306a36Sopenharmony_ci .path = path, 327162306a36Sopenharmony_ci .fid = &fid, 327262306a36Sopenharmony_ci }; 327362306a36Sopenharmony_ci 327462306a36Sopenharmony_ci rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, 327562306a36Sopenharmony_ci NULL, NULL); 327662306a36Sopenharmony_ci kfree(utf16_path); 327762306a36Sopenharmony_ci if (!rc) { 327862306a36Sopenharmony_ci rc = SMB2_set_acl(xid, tlink_tcon(tlink), fid.persistent_fid, 327962306a36Sopenharmony_ci fid.volatile_fid, pnntsd, acllen, aclflag); 328062306a36Sopenharmony_ci SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); 328162306a36Sopenharmony_ci } 328262306a36Sopenharmony_ci 328362306a36Sopenharmony_ci cifs_put_tlink(tlink); 328462306a36Sopenharmony_ci free_xid(xid); 328562306a36Sopenharmony_ci return rc; 328662306a36Sopenharmony_ci} 328762306a36Sopenharmony_ci 328862306a36Sopenharmony_ci/* Retrieve an ACL from the server */ 328962306a36Sopenharmony_cistatic struct cifs_ntsd * 329062306a36Sopenharmony_ciget_smb2_acl(struct cifs_sb_info *cifs_sb, 329162306a36Sopenharmony_ci struct inode *inode, const char *path, 329262306a36Sopenharmony_ci u32 *pacllen, u32 info) 329362306a36Sopenharmony_ci{ 329462306a36Sopenharmony_ci struct cifs_ntsd *pntsd = NULL; 329562306a36Sopenharmony_ci struct cifsFileInfo *open_file = NULL; 329662306a36Sopenharmony_ci 329762306a36Sopenharmony_ci if (inode && !(info & SACL_SECINFO)) 329862306a36Sopenharmony_ci open_file = find_readable_file(CIFS_I(inode), true); 329962306a36Sopenharmony_ci if (!open_file || (info & SACL_SECINFO)) 330062306a36Sopenharmony_ci return get_smb2_acl_by_path(cifs_sb, path, pacllen, info); 330162306a36Sopenharmony_ci 330262306a36Sopenharmony_ci pntsd = get_smb2_acl_by_fid(cifs_sb, &open_file->fid, pacllen, info); 330362306a36Sopenharmony_ci cifsFileInfo_put(open_file); 330462306a36Sopenharmony_ci return pntsd; 330562306a36Sopenharmony_ci} 330662306a36Sopenharmony_ci 330762306a36Sopenharmony_cistatic long smb3_zero_data(struct file *file, struct cifs_tcon *tcon, 330862306a36Sopenharmony_ci loff_t offset, loff_t len, unsigned int xid) 330962306a36Sopenharmony_ci{ 331062306a36Sopenharmony_ci struct cifsFileInfo *cfile = file->private_data; 331162306a36Sopenharmony_ci struct file_zero_data_information fsctl_buf; 331262306a36Sopenharmony_ci 331362306a36Sopenharmony_ci cifs_dbg(FYI, "Offset %lld len %lld\n", offset, len); 331462306a36Sopenharmony_ci 331562306a36Sopenharmony_ci fsctl_buf.FileOffset = cpu_to_le64(offset); 331662306a36Sopenharmony_ci fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len); 331762306a36Sopenharmony_ci 331862306a36Sopenharmony_ci return SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, 331962306a36Sopenharmony_ci cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA, 332062306a36Sopenharmony_ci (char *)&fsctl_buf, 332162306a36Sopenharmony_ci sizeof(struct file_zero_data_information), 332262306a36Sopenharmony_ci 0, NULL, NULL); 332362306a36Sopenharmony_ci} 332462306a36Sopenharmony_ci 332562306a36Sopenharmony_cistatic long smb3_zero_range(struct file *file, struct cifs_tcon *tcon, 332662306a36Sopenharmony_ci loff_t offset, loff_t len, bool keep_size) 332762306a36Sopenharmony_ci{ 332862306a36Sopenharmony_ci struct cifs_ses *ses = tcon->ses; 332962306a36Sopenharmony_ci struct inode *inode = file_inode(file); 333062306a36Sopenharmony_ci struct cifsInodeInfo *cifsi = CIFS_I(inode); 333162306a36Sopenharmony_ci struct cifsFileInfo *cfile = file->private_data; 333262306a36Sopenharmony_ci unsigned long long new_size; 333362306a36Sopenharmony_ci long rc; 333462306a36Sopenharmony_ci unsigned int xid; 333562306a36Sopenharmony_ci __le64 eof; 333662306a36Sopenharmony_ci 333762306a36Sopenharmony_ci xid = get_xid(); 333862306a36Sopenharmony_ci 333962306a36Sopenharmony_ci trace_smb3_zero_enter(xid, cfile->fid.persistent_fid, tcon->tid, 334062306a36Sopenharmony_ci ses->Suid, offset, len); 334162306a36Sopenharmony_ci 334262306a36Sopenharmony_ci inode_lock(inode); 334362306a36Sopenharmony_ci filemap_invalidate_lock(inode->i_mapping); 334462306a36Sopenharmony_ci 334562306a36Sopenharmony_ci /* 334662306a36Sopenharmony_ci * We zero the range through ioctl, so we need remove the page caches 334762306a36Sopenharmony_ci * first, otherwise the data may be inconsistent with the server. 334862306a36Sopenharmony_ci */ 334962306a36Sopenharmony_ci truncate_pagecache_range(inode, offset, offset + len - 1); 335062306a36Sopenharmony_ci 335162306a36Sopenharmony_ci /* if file not oplocked can't be sure whether asking to extend size */ 335262306a36Sopenharmony_ci rc = -EOPNOTSUPP; 335362306a36Sopenharmony_ci if (keep_size == false && !CIFS_CACHE_READ(cifsi)) 335462306a36Sopenharmony_ci goto zero_range_exit; 335562306a36Sopenharmony_ci 335662306a36Sopenharmony_ci rc = smb3_zero_data(file, tcon, offset, len, xid); 335762306a36Sopenharmony_ci if (rc < 0) 335862306a36Sopenharmony_ci goto zero_range_exit; 335962306a36Sopenharmony_ci 336062306a36Sopenharmony_ci /* 336162306a36Sopenharmony_ci * do we also need to change the size of the file? 336262306a36Sopenharmony_ci */ 336362306a36Sopenharmony_ci new_size = offset + len; 336462306a36Sopenharmony_ci if (keep_size == false && (unsigned long long)i_size_read(inode) < new_size) { 336562306a36Sopenharmony_ci eof = cpu_to_le64(new_size); 336662306a36Sopenharmony_ci rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, 336762306a36Sopenharmony_ci cfile->fid.volatile_fid, cfile->pid, &eof); 336862306a36Sopenharmony_ci if (rc >= 0) { 336962306a36Sopenharmony_ci truncate_setsize(inode, new_size); 337062306a36Sopenharmony_ci fscache_resize_cookie(cifs_inode_cookie(inode), new_size); 337162306a36Sopenharmony_ci } 337262306a36Sopenharmony_ci } 337362306a36Sopenharmony_ci 337462306a36Sopenharmony_ci zero_range_exit: 337562306a36Sopenharmony_ci filemap_invalidate_unlock(inode->i_mapping); 337662306a36Sopenharmony_ci inode_unlock(inode); 337762306a36Sopenharmony_ci free_xid(xid); 337862306a36Sopenharmony_ci if (rc) 337962306a36Sopenharmony_ci trace_smb3_zero_err(xid, cfile->fid.persistent_fid, tcon->tid, 338062306a36Sopenharmony_ci ses->Suid, offset, len, rc); 338162306a36Sopenharmony_ci else 338262306a36Sopenharmony_ci trace_smb3_zero_done(xid, cfile->fid.persistent_fid, tcon->tid, 338362306a36Sopenharmony_ci ses->Suid, offset, len); 338462306a36Sopenharmony_ci return rc; 338562306a36Sopenharmony_ci} 338662306a36Sopenharmony_ci 338762306a36Sopenharmony_cistatic long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon, 338862306a36Sopenharmony_ci loff_t offset, loff_t len) 338962306a36Sopenharmony_ci{ 339062306a36Sopenharmony_ci struct inode *inode = file_inode(file); 339162306a36Sopenharmony_ci struct cifsFileInfo *cfile = file->private_data; 339262306a36Sopenharmony_ci struct file_zero_data_information fsctl_buf; 339362306a36Sopenharmony_ci long rc; 339462306a36Sopenharmony_ci unsigned int xid; 339562306a36Sopenharmony_ci __u8 set_sparse = 1; 339662306a36Sopenharmony_ci 339762306a36Sopenharmony_ci xid = get_xid(); 339862306a36Sopenharmony_ci 339962306a36Sopenharmony_ci inode_lock(inode); 340062306a36Sopenharmony_ci /* Need to make file sparse, if not already, before freeing range. */ 340162306a36Sopenharmony_ci /* Consider adding equivalent for compressed since it could also work */ 340262306a36Sopenharmony_ci if (!smb2_set_sparse(xid, tcon, cfile, inode, set_sparse)) { 340362306a36Sopenharmony_ci rc = -EOPNOTSUPP; 340462306a36Sopenharmony_ci goto out; 340562306a36Sopenharmony_ci } 340662306a36Sopenharmony_ci 340762306a36Sopenharmony_ci filemap_invalidate_lock(inode->i_mapping); 340862306a36Sopenharmony_ci /* 340962306a36Sopenharmony_ci * We implement the punch hole through ioctl, so we need remove the page 341062306a36Sopenharmony_ci * caches first, otherwise the data may be inconsistent with the server. 341162306a36Sopenharmony_ci */ 341262306a36Sopenharmony_ci truncate_pagecache_range(inode, offset, offset + len - 1); 341362306a36Sopenharmony_ci 341462306a36Sopenharmony_ci cifs_dbg(FYI, "Offset %lld len %lld\n", offset, len); 341562306a36Sopenharmony_ci 341662306a36Sopenharmony_ci fsctl_buf.FileOffset = cpu_to_le64(offset); 341762306a36Sopenharmony_ci fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len); 341862306a36Sopenharmony_ci 341962306a36Sopenharmony_ci rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, 342062306a36Sopenharmony_ci cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA, 342162306a36Sopenharmony_ci (char *)&fsctl_buf, 342262306a36Sopenharmony_ci sizeof(struct file_zero_data_information), 342362306a36Sopenharmony_ci CIFSMaxBufSize, NULL, NULL); 342462306a36Sopenharmony_ci filemap_invalidate_unlock(inode->i_mapping); 342562306a36Sopenharmony_ciout: 342662306a36Sopenharmony_ci inode_unlock(inode); 342762306a36Sopenharmony_ci free_xid(xid); 342862306a36Sopenharmony_ci return rc; 342962306a36Sopenharmony_ci} 343062306a36Sopenharmony_ci 343162306a36Sopenharmony_cistatic int smb3_simple_fallocate_write_range(unsigned int xid, 343262306a36Sopenharmony_ci struct cifs_tcon *tcon, 343362306a36Sopenharmony_ci struct cifsFileInfo *cfile, 343462306a36Sopenharmony_ci loff_t off, loff_t len, 343562306a36Sopenharmony_ci char *buf) 343662306a36Sopenharmony_ci{ 343762306a36Sopenharmony_ci struct cifs_io_parms io_parms = {0}; 343862306a36Sopenharmony_ci int nbytes; 343962306a36Sopenharmony_ci int rc = 0; 344062306a36Sopenharmony_ci struct kvec iov[2]; 344162306a36Sopenharmony_ci 344262306a36Sopenharmony_ci io_parms.netfid = cfile->fid.netfid; 344362306a36Sopenharmony_ci io_parms.pid = current->tgid; 344462306a36Sopenharmony_ci io_parms.tcon = tcon; 344562306a36Sopenharmony_ci io_parms.persistent_fid = cfile->fid.persistent_fid; 344662306a36Sopenharmony_ci io_parms.volatile_fid = cfile->fid.volatile_fid; 344762306a36Sopenharmony_ci 344862306a36Sopenharmony_ci while (len) { 344962306a36Sopenharmony_ci io_parms.offset = off; 345062306a36Sopenharmony_ci io_parms.length = len; 345162306a36Sopenharmony_ci if (io_parms.length > SMB2_MAX_BUFFER_SIZE) 345262306a36Sopenharmony_ci io_parms.length = SMB2_MAX_BUFFER_SIZE; 345362306a36Sopenharmony_ci /* iov[0] is reserved for smb header */ 345462306a36Sopenharmony_ci iov[1].iov_base = buf; 345562306a36Sopenharmony_ci iov[1].iov_len = io_parms.length; 345662306a36Sopenharmony_ci rc = SMB2_write(xid, &io_parms, &nbytes, iov, 1); 345762306a36Sopenharmony_ci if (rc) 345862306a36Sopenharmony_ci break; 345962306a36Sopenharmony_ci if (nbytes > len) 346062306a36Sopenharmony_ci return -EINVAL; 346162306a36Sopenharmony_ci buf += nbytes; 346262306a36Sopenharmony_ci off += nbytes; 346362306a36Sopenharmony_ci len -= nbytes; 346462306a36Sopenharmony_ci } 346562306a36Sopenharmony_ci return rc; 346662306a36Sopenharmony_ci} 346762306a36Sopenharmony_ci 346862306a36Sopenharmony_cistatic int smb3_simple_fallocate_range(unsigned int xid, 346962306a36Sopenharmony_ci struct cifs_tcon *tcon, 347062306a36Sopenharmony_ci struct cifsFileInfo *cfile, 347162306a36Sopenharmony_ci loff_t off, loff_t len) 347262306a36Sopenharmony_ci{ 347362306a36Sopenharmony_ci struct file_allocated_range_buffer in_data, *out_data = NULL, *tmp_data; 347462306a36Sopenharmony_ci u32 out_data_len; 347562306a36Sopenharmony_ci char *buf = NULL; 347662306a36Sopenharmony_ci loff_t l; 347762306a36Sopenharmony_ci int rc; 347862306a36Sopenharmony_ci 347962306a36Sopenharmony_ci in_data.file_offset = cpu_to_le64(off); 348062306a36Sopenharmony_ci in_data.length = cpu_to_le64(len); 348162306a36Sopenharmony_ci rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, 348262306a36Sopenharmony_ci cfile->fid.volatile_fid, 348362306a36Sopenharmony_ci FSCTL_QUERY_ALLOCATED_RANGES, 348462306a36Sopenharmony_ci (char *)&in_data, sizeof(in_data), 348562306a36Sopenharmony_ci 1024 * sizeof(struct file_allocated_range_buffer), 348662306a36Sopenharmony_ci (char **)&out_data, &out_data_len); 348762306a36Sopenharmony_ci if (rc) 348862306a36Sopenharmony_ci goto out; 348962306a36Sopenharmony_ci 349062306a36Sopenharmony_ci buf = kzalloc(1024 * 1024, GFP_KERNEL); 349162306a36Sopenharmony_ci if (buf == NULL) { 349262306a36Sopenharmony_ci rc = -ENOMEM; 349362306a36Sopenharmony_ci goto out; 349462306a36Sopenharmony_ci } 349562306a36Sopenharmony_ci 349662306a36Sopenharmony_ci tmp_data = out_data; 349762306a36Sopenharmony_ci while (len) { 349862306a36Sopenharmony_ci /* 349962306a36Sopenharmony_ci * The rest of the region is unmapped so write it all. 350062306a36Sopenharmony_ci */ 350162306a36Sopenharmony_ci if (out_data_len == 0) { 350262306a36Sopenharmony_ci rc = smb3_simple_fallocate_write_range(xid, tcon, 350362306a36Sopenharmony_ci cfile, off, len, buf); 350462306a36Sopenharmony_ci goto out; 350562306a36Sopenharmony_ci } 350662306a36Sopenharmony_ci 350762306a36Sopenharmony_ci if (out_data_len < sizeof(struct file_allocated_range_buffer)) { 350862306a36Sopenharmony_ci rc = -EINVAL; 350962306a36Sopenharmony_ci goto out; 351062306a36Sopenharmony_ci } 351162306a36Sopenharmony_ci 351262306a36Sopenharmony_ci if (off < le64_to_cpu(tmp_data->file_offset)) { 351362306a36Sopenharmony_ci /* 351462306a36Sopenharmony_ci * We are at a hole. Write until the end of the region 351562306a36Sopenharmony_ci * or until the next allocated data, 351662306a36Sopenharmony_ci * whichever comes next. 351762306a36Sopenharmony_ci */ 351862306a36Sopenharmony_ci l = le64_to_cpu(tmp_data->file_offset) - off; 351962306a36Sopenharmony_ci if (len < l) 352062306a36Sopenharmony_ci l = len; 352162306a36Sopenharmony_ci rc = smb3_simple_fallocate_write_range(xid, tcon, 352262306a36Sopenharmony_ci cfile, off, l, buf); 352362306a36Sopenharmony_ci if (rc) 352462306a36Sopenharmony_ci goto out; 352562306a36Sopenharmony_ci off = off + l; 352662306a36Sopenharmony_ci len = len - l; 352762306a36Sopenharmony_ci if (len == 0) 352862306a36Sopenharmony_ci goto out; 352962306a36Sopenharmony_ci } 353062306a36Sopenharmony_ci /* 353162306a36Sopenharmony_ci * We are at a section of allocated data, just skip forward 353262306a36Sopenharmony_ci * until the end of the data or the end of the region 353362306a36Sopenharmony_ci * we are supposed to fallocate, whichever comes first. 353462306a36Sopenharmony_ci */ 353562306a36Sopenharmony_ci l = le64_to_cpu(tmp_data->length); 353662306a36Sopenharmony_ci if (len < l) 353762306a36Sopenharmony_ci l = len; 353862306a36Sopenharmony_ci off += l; 353962306a36Sopenharmony_ci len -= l; 354062306a36Sopenharmony_ci 354162306a36Sopenharmony_ci tmp_data = &tmp_data[1]; 354262306a36Sopenharmony_ci out_data_len -= sizeof(struct file_allocated_range_buffer); 354362306a36Sopenharmony_ci } 354462306a36Sopenharmony_ci 354562306a36Sopenharmony_ci out: 354662306a36Sopenharmony_ci kfree(out_data); 354762306a36Sopenharmony_ci kfree(buf); 354862306a36Sopenharmony_ci return rc; 354962306a36Sopenharmony_ci} 355062306a36Sopenharmony_ci 355162306a36Sopenharmony_ci 355262306a36Sopenharmony_cistatic long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon, 355362306a36Sopenharmony_ci loff_t off, loff_t len, bool keep_size) 355462306a36Sopenharmony_ci{ 355562306a36Sopenharmony_ci struct inode *inode; 355662306a36Sopenharmony_ci struct cifsInodeInfo *cifsi; 355762306a36Sopenharmony_ci struct cifsFileInfo *cfile = file->private_data; 355862306a36Sopenharmony_ci long rc = -EOPNOTSUPP; 355962306a36Sopenharmony_ci unsigned int xid; 356062306a36Sopenharmony_ci __le64 eof; 356162306a36Sopenharmony_ci 356262306a36Sopenharmony_ci xid = get_xid(); 356362306a36Sopenharmony_ci 356462306a36Sopenharmony_ci inode = d_inode(cfile->dentry); 356562306a36Sopenharmony_ci cifsi = CIFS_I(inode); 356662306a36Sopenharmony_ci 356762306a36Sopenharmony_ci trace_smb3_falloc_enter(xid, cfile->fid.persistent_fid, tcon->tid, 356862306a36Sopenharmony_ci tcon->ses->Suid, off, len); 356962306a36Sopenharmony_ci /* if file not oplocked can't be sure whether asking to extend size */ 357062306a36Sopenharmony_ci if (!CIFS_CACHE_READ(cifsi)) 357162306a36Sopenharmony_ci if (keep_size == false) { 357262306a36Sopenharmony_ci trace_smb3_falloc_err(xid, cfile->fid.persistent_fid, 357362306a36Sopenharmony_ci tcon->tid, tcon->ses->Suid, off, len, rc); 357462306a36Sopenharmony_ci free_xid(xid); 357562306a36Sopenharmony_ci return rc; 357662306a36Sopenharmony_ci } 357762306a36Sopenharmony_ci 357862306a36Sopenharmony_ci /* 357962306a36Sopenharmony_ci * Extending the file 358062306a36Sopenharmony_ci */ 358162306a36Sopenharmony_ci if ((keep_size == false) && i_size_read(inode) < off + len) { 358262306a36Sopenharmony_ci rc = inode_newsize_ok(inode, off + len); 358362306a36Sopenharmony_ci if (rc) 358462306a36Sopenharmony_ci goto out; 358562306a36Sopenharmony_ci 358662306a36Sopenharmony_ci if (cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) 358762306a36Sopenharmony_ci smb2_set_sparse(xid, tcon, cfile, inode, false); 358862306a36Sopenharmony_ci 358962306a36Sopenharmony_ci eof = cpu_to_le64(off + len); 359062306a36Sopenharmony_ci rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, 359162306a36Sopenharmony_ci cfile->fid.volatile_fid, cfile->pid, &eof); 359262306a36Sopenharmony_ci if (rc == 0) { 359362306a36Sopenharmony_ci cifsi->server_eof = off + len; 359462306a36Sopenharmony_ci cifs_setsize(inode, off + len); 359562306a36Sopenharmony_ci cifs_truncate_page(inode->i_mapping, inode->i_size); 359662306a36Sopenharmony_ci truncate_setsize(inode, off + len); 359762306a36Sopenharmony_ci } 359862306a36Sopenharmony_ci goto out; 359962306a36Sopenharmony_ci } 360062306a36Sopenharmony_ci 360162306a36Sopenharmony_ci /* 360262306a36Sopenharmony_ci * Files are non-sparse by default so falloc may be a no-op 360362306a36Sopenharmony_ci * Must check if file sparse. If not sparse, and since we are not 360462306a36Sopenharmony_ci * extending then no need to do anything since file already allocated 360562306a36Sopenharmony_ci */ 360662306a36Sopenharmony_ci if ((cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) == 0) { 360762306a36Sopenharmony_ci rc = 0; 360862306a36Sopenharmony_ci goto out; 360962306a36Sopenharmony_ci } 361062306a36Sopenharmony_ci 361162306a36Sopenharmony_ci if (keep_size == true) { 361262306a36Sopenharmony_ci /* 361362306a36Sopenharmony_ci * We can not preallocate pages beyond the end of the file 361462306a36Sopenharmony_ci * in SMB2 361562306a36Sopenharmony_ci */ 361662306a36Sopenharmony_ci if (off >= i_size_read(inode)) { 361762306a36Sopenharmony_ci rc = 0; 361862306a36Sopenharmony_ci goto out; 361962306a36Sopenharmony_ci } 362062306a36Sopenharmony_ci /* 362162306a36Sopenharmony_ci * For fallocates that are partially beyond the end of file, 362262306a36Sopenharmony_ci * clamp len so we only fallocate up to the end of file. 362362306a36Sopenharmony_ci */ 362462306a36Sopenharmony_ci if (off + len > i_size_read(inode)) { 362562306a36Sopenharmony_ci len = i_size_read(inode) - off; 362662306a36Sopenharmony_ci } 362762306a36Sopenharmony_ci } 362862306a36Sopenharmony_ci 362962306a36Sopenharmony_ci if ((keep_size == true) || (i_size_read(inode) >= off + len)) { 363062306a36Sopenharmony_ci /* 363162306a36Sopenharmony_ci * At this point, we are trying to fallocate an internal 363262306a36Sopenharmony_ci * regions of a sparse file. Since smb2 does not have a 363362306a36Sopenharmony_ci * fallocate command we have two otions on how to emulate this. 363462306a36Sopenharmony_ci * We can either turn the entire file to become non-sparse 363562306a36Sopenharmony_ci * which we only do if the fallocate is for virtually 363662306a36Sopenharmony_ci * the whole file, or we can overwrite the region with zeroes 363762306a36Sopenharmony_ci * using SMB2_write, which could be prohibitevly expensive 363862306a36Sopenharmony_ci * if len is large. 363962306a36Sopenharmony_ci */ 364062306a36Sopenharmony_ci /* 364162306a36Sopenharmony_ci * We are only trying to fallocate a small region so 364262306a36Sopenharmony_ci * just write it with zero. 364362306a36Sopenharmony_ci */ 364462306a36Sopenharmony_ci if (len <= 1024 * 1024) { 364562306a36Sopenharmony_ci rc = smb3_simple_fallocate_range(xid, tcon, cfile, 364662306a36Sopenharmony_ci off, len); 364762306a36Sopenharmony_ci goto out; 364862306a36Sopenharmony_ci } 364962306a36Sopenharmony_ci 365062306a36Sopenharmony_ci /* 365162306a36Sopenharmony_ci * Check if falloc starts within first few pages of file 365262306a36Sopenharmony_ci * and ends within a few pages of the end of file to 365362306a36Sopenharmony_ci * ensure that most of file is being forced to be 365462306a36Sopenharmony_ci * fallocated now. If so then setting whole file sparse 365562306a36Sopenharmony_ci * ie potentially making a few extra pages at the beginning 365662306a36Sopenharmony_ci * or end of the file non-sparse via set_sparse is harmless. 365762306a36Sopenharmony_ci */ 365862306a36Sopenharmony_ci if ((off > 8192) || (off + len + 8192 < i_size_read(inode))) { 365962306a36Sopenharmony_ci rc = -EOPNOTSUPP; 366062306a36Sopenharmony_ci goto out; 366162306a36Sopenharmony_ci } 366262306a36Sopenharmony_ci } 366362306a36Sopenharmony_ci 366462306a36Sopenharmony_ci smb2_set_sparse(xid, tcon, cfile, inode, false); 366562306a36Sopenharmony_ci rc = 0; 366662306a36Sopenharmony_ci 366762306a36Sopenharmony_ciout: 366862306a36Sopenharmony_ci if (rc) 366962306a36Sopenharmony_ci trace_smb3_falloc_err(xid, cfile->fid.persistent_fid, tcon->tid, 367062306a36Sopenharmony_ci tcon->ses->Suid, off, len, rc); 367162306a36Sopenharmony_ci else 367262306a36Sopenharmony_ci trace_smb3_falloc_done(xid, cfile->fid.persistent_fid, tcon->tid, 367362306a36Sopenharmony_ci tcon->ses->Suid, off, len); 367462306a36Sopenharmony_ci 367562306a36Sopenharmony_ci free_xid(xid); 367662306a36Sopenharmony_ci return rc; 367762306a36Sopenharmony_ci} 367862306a36Sopenharmony_ci 367962306a36Sopenharmony_cistatic long smb3_collapse_range(struct file *file, struct cifs_tcon *tcon, 368062306a36Sopenharmony_ci loff_t off, loff_t len) 368162306a36Sopenharmony_ci{ 368262306a36Sopenharmony_ci int rc; 368362306a36Sopenharmony_ci unsigned int xid; 368462306a36Sopenharmony_ci struct inode *inode = file_inode(file); 368562306a36Sopenharmony_ci struct cifsFileInfo *cfile = file->private_data; 368662306a36Sopenharmony_ci struct cifsInodeInfo *cifsi = CIFS_I(inode); 368762306a36Sopenharmony_ci __le64 eof; 368862306a36Sopenharmony_ci loff_t old_eof; 368962306a36Sopenharmony_ci 369062306a36Sopenharmony_ci xid = get_xid(); 369162306a36Sopenharmony_ci 369262306a36Sopenharmony_ci inode_lock(inode); 369362306a36Sopenharmony_ci 369462306a36Sopenharmony_ci old_eof = i_size_read(inode); 369562306a36Sopenharmony_ci if ((off >= old_eof) || 369662306a36Sopenharmony_ci off + len >= old_eof) { 369762306a36Sopenharmony_ci rc = -EINVAL; 369862306a36Sopenharmony_ci goto out; 369962306a36Sopenharmony_ci } 370062306a36Sopenharmony_ci 370162306a36Sopenharmony_ci filemap_invalidate_lock(inode->i_mapping); 370262306a36Sopenharmony_ci rc = filemap_write_and_wait_range(inode->i_mapping, off, old_eof - 1); 370362306a36Sopenharmony_ci if (rc < 0) 370462306a36Sopenharmony_ci goto out_2; 370562306a36Sopenharmony_ci 370662306a36Sopenharmony_ci truncate_pagecache_range(inode, off, old_eof); 370762306a36Sopenharmony_ci 370862306a36Sopenharmony_ci rc = smb2_copychunk_range(xid, cfile, cfile, off + len, 370962306a36Sopenharmony_ci old_eof - off - len, off); 371062306a36Sopenharmony_ci if (rc < 0) 371162306a36Sopenharmony_ci goto out_2; 371262306a36Sopenharmony_ci 371362306a36Sopenharmony_ci eof = cpu_to_le64(old_eof - len); 371462306a36Sopenharmony_ci rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, 371562306a36Sopenharmony_ci cfile->fid.volatile_fid, cfile->pid, &eof); 371662306a36Sopenharmony_ci if (rc < 0) 371762306a36Sopenharmony_ci goto out_2; 371862306a36Sopenharmony_ci 371962306a36Sopenharmony_ci rc = 0; 372062306a36Sopenharmony_ci 372162306a36Sopenharmony_ci cifsi->server_eof = i_size_read(inode) - len; 372262306a36Sopenharmony_ci truncate_setsize(inode, cifsi->server_eof); 372362306a36Sopenharmony_ci fscache_resize_cookie(cifs_inode_cookie(inode), cifsi->server_eof); 372462306a36Sopenharmony_ciout_2: 372562306a36Sopenharmony_ci filemap_invalidate_unlock(inode->i_mapping); 372662306a36Sopenharmony_ci out: 372762306a36Sopenharmony_ci inode_unlock(inode); 372862306a36Sopenharmony_ci free_xid(xid); 372962306a36Sopenharmony_ci return rc; 373062306a36Sopenharmony_ci} 373162306a36Sopenharmony_ci 373262306a36Sopenharmony_cistatic long smb3_insert_range(struct file *file, struct cifs_tcon *tcon, 373362306a36Sopenharmony_ci loff_t off, loff_t len) 373462306a36Sopenharmony_ci{ 373562306a36Sopenharmony_ci int rc; 373662306a36Sopenharmony_ci unsigned int xid; 373762306a36Sopenharmony_ci struct cifsFileInfo *cfile = file->private_data; 373862306a36Sopenharmony_ci struct inode *inode = file_inode(file); 373962306a36Sopenharmony_ci __le64 eof; 374062306a36Sopenharmony_ci __u64 count, old_eof; 374162306a36Sopenharmony_ci 374262306a36Sopenharmony_ci xid = get_xid(); 374362306a36Sopenharmony_ci 374462306a36Sopenharmony_ci inode_lock(inode); 374562306a36Sopenharmony_ci 374662306a36Sopenharmony_ci old_eof = i_size_read(inode); 374762306a36Sopenharmony_ci if (off >= old_eof) { 374862306a36Sopenharmony_ci rc = -EINVAL; 374962306a36Sopenharmony_ci goto out; 375062306a36Sopenharmony_ci } 375162306a36Sopenharmony_ci 375262306a36Sopenharmony_ci count = old_eof - off; 375362306a36Sopenharmony_ci eof = cpu_to_le64(old_eof + len); 375462306a36Sopenharmony_ci 375562306a36Sopenharmony_ci filemap_invalidate_lock(inode->i_mapping); 375662306a36Sopenharmony_ci rc = filemap_write_and_wait_range(inode->i_mapping, off, old_eof + len - 1); 375762306a36Sopenharmony_ci if (rc < 0) 375862306a36Sopenharmony_ci goto out_2; 375962306a36Sopenharmony_ci truncate_pagecache_range(inode, off, old_eof); 376062306a36Sopenharmony_ci 376162306a36Sopenharmony_ci rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, 376262306a36Sopenharmony_ci cfile->fid.volatile_fid, cfile->pid, &eof); 376362306a36Sopenharmony_ci if (rc < 0) 376462306a36Sopenharmony_ci goto out_2; 376562306a36Sopenharmony_ci 376662306a36Sopenharmony_ci truncate_setsize(inode, old_eof + len); 376762306a36Sopenharmony_ci fscache_resize_cookie(cifs_inode_cookie(inode), i_size_read(inode)); 376862306a36Sopenharmony_ci 376962306a36Sopenharmony_ci rc = smb2_copychunk_range(xid, cfile, cfile, off, count, off + len); 377062306a36Sopenharmony_ci if (rc < 0) 377162306a36Sopenharmony_ci goto out_2; 377262306a36Sopenharmony_ci 377362306a36Sopenharmony_ci rc = smb3_zero_data(file, tcon, off, len, xid); 377462306a36Sopenharmony_ci if (rc < 0) 377562306a36Sopenharmony_ci goto out_2; 377662306a36Sopenharmony_ci 377762306a36Sopenharmony_ci rc = 0; 377862306a36Sopenharmony_ciout_2: 377962306a36Sopenharmony_ci filemap_invalidate_unlock(inode->i_mapping); 378062306a36Sopenharmony_ci out: 378162306a36Sopenharmony_ci inode_unlock(inode); 378262306a36Sopenharmony_ci free_xid(xid); 378362306a36Sopenharmony_ci return rc; 378462306a36Sopenharmony_ci} 378562306a36Sopenharmony_ci 378662306a36Sopenharmony_cistatic loff_t smb3_llseek(struct file *file, struct cifs_tcon *tcon, loff_t offset, int whence) 378762306a36Sopenharmony_ci{ 378862306a36Sopenharmony_ci struct cifsFileInfo *wrcfile, *cfile = file->private_data; 378962306a36Sopenharmony_ci struct cifsInodeInfo *cifsi; 379062306a36Sopenharmony_ci struct inode *inode; 379162306a36Sopenharmony_ci int rc = 0; 379262306a36Sopenharmony_ci struct file_allocated_range_buffer in_data, *out_data = NULL; 379362306a36Sopenharmony_ci u32 out_data_len; 379462306a36Sopenharmony_ci unsigned int xid; 379562306a36Sopenharmony_ci 379662306a36Sopenharmony_ci if (whence != SEEK_HOLE && whence != SEEK_DATA) 379762306a36Sopenharmony_ci return generic_file_llseek(file, offset, whence); 379862306a36Sopenharmony_ci 379962306a36Sopenharmony_ci inode = d_inode(cfile->dentry); 380062306a36Sopenharmony_ci cifsi = CIFS_I(inode); 380162306a36Sopenharmony_ci 380262306a36Sopenharmony_ci if (offset < 0 || offset >= i_size_read(inode)) 380362306a36Sopenharmony_ci return -ENXIO; 380462306a36Sopenharmony_ci 380562306a36Sopenharmony_ci xid = get_xid(); 380662306a36Sopenharmony_ci /* 380762306a36Sopenharmony_ci * We need to be sure that all dirty pages are written as they 380862306a36Sopenharmony_ci * might fill holes on the server. 380962306a36Sopenharmony_ci * Note that we also MUST flush any written pages since at least 381062306a36Sopenharmony_ci * some servers (Windows2016) will not reflect recent writes in 381162306a36Sopenharmony_ci * QUERY_ALLOCATED_RANGES until SMB2_flush is called. 381262306a36Sopenharmony_ci */ 381362306a36Sopenharmony_ci wrcfile = find_writable_file(cifsi, FIND_WR_ANY); 381462306a36Sopenharmony_ci if (wrcfile) { 381562306a36Sopenharmony_ci filemap_write_and_wait(inode->i_mapping); 381662306a36Sopenharmony_ci smb2_flush_file(xid, tcon, &wrcfile->fid); 381762306a36Sopenharmony_ci cifsFileInfo_put(wrcfile); 381862306a36Sopenharmony_ci } 381962306a36Sopenharmony_ci 382062306a36Sopenharmony_ci if (!(cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE)) { 382162306a36Sopenharmony_ci if (whence == SEEK_HOLE) 382262306a36Sopenharmony_ci offset = i_size_read(inode); 382362306a36Sopenharmony_ci goto lseek_exit; 382462306a36Sopenharmony_ci } 382562306a36Sopenharmony_ci 382662306a36Sopenharmony_ci in_data.file_offset = cpu_to_le64(offset); 382762306a36Sopenharmony_ci in_data.length = cpu_to_le64(i_size_read(inode)); 382862306a36Sopenharmony_ci 382962306a36Sopenharmony_ci rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, 383062306a36Sopenharmony_ci cfile->fid.volatile_fid, 383162306a36Sopenharmony_ci FSCTL_QUERY_ALLOCATED_RANGES, 383262306a36Sopenharmony_ci (char *)&in_data, sizeof(in_data), 383362306a36Sopenharmony_ci sizeof(struct file_allocated_range_buffer), 383462306a36Sopenharmony_ci (char **)&out_data, &out_data_len); 383562306a36Sopenharmony_ci if (rc == -E2BIG) 383662306a36Sopenharmony_ci rc = 0; 383762306a36Sopenharmony_ci if (rc) 383862306a36Sopenharmony_ci goto lseek_exit; 383962306a36Sopenharmony_ci 384062306a36Sopenharmony_ci if (whence == SEEK_HOLE && out_data_len == 0) 384162306a36Sopenharmony_ci goto lseek_exit; 384262306a36Sopenharmony_ci 384362306a36Sopenharmony_ci if (whence == SEEK_DATA && out_data_len == 0) { 384462306a36Sopenharmony_ci rc = -ENXIO; 384562306a36Sopenharmony_ci goto lseek_exit; 384662306a36Sopenharmony_ci } 384762306a36Sopenharmony_ci 384862306a36Sopenharmony_ci if (out_data_len < sizeof(struct file_allocated_range_buffer)) { 384962306a36Sopenharmony_ci rc = -EINVAL; 385062306a36Sopenharmony_ci goto lseek_exit; 385162306a36Sopenharmony_ci } 385262306a36Sopenharmony_ci if (whence == SEEK_DATA) { 385362306a36Sopenharmony_ci offset = le64_to_cpu(out_data->file_offset); 385462306a36Sopenharmony_ci goto lseek_exit; 385562306a36Sopenharmony_ci } 385662306a36Sopenharmony_ci if (offset < le64_to_cpu(out_data->file_offset)) 385762306a36Sopenharmony_ci goto lseek_exit; 385862306a36Sopenharmony_ci 385962306a36Sopenharmony_ci offset = le64_to_cpu(out_data->file_offset) + le64_to_cpu(out_data->length); 386062306a36Sopenharmony_ci 386162306a36Sopenharmony_ci lseek_exit: 386262306a36Sopenharmony_ci free_xid(xid); 386362306a36Sopenharmony_ci kfree(out_data); 386462306a36Sopenharmony_ci if (!rc) 386562306a36Sopenharmony_ci return vfs_setpos(file, offset, inode->i_sb->s_maxbytes); 386662306a36Sopenharmony_ci else 386762306a36Sopenharmony_ci return rc; 386862306a36Sopenharmony_ci} 386962306a36Sopenharmony_ci 387062306a36Sopenharmony_cistatic int smb3_fiemap(struct cifs_tcon *tcon, 387162306a36Sopenharmony_ci struct cifsFileInfo *cfile, 387262306a36Sopenharmony_ci struct fiemap_extent_info *fei, u64 start, u64 len) 387362306a36Sopenharmony_ci{ 387462306a36Sopenharmony_ci unsigned int xid; 387562306a36Sopenharmony_ci struct file_allocated_range_buffer in_data, *out_data; 387662306a36Sopenharmony_ci u32 out_data_len; 387762306a36Sopenharmony_ci int i, num, rc, flags, last_blob; 387862306a36Sopenharmony_ci u64 next; 387962306a36Sopenharmony_ci 388062306a36Sopenharmony_ci rc = fiemap_prep(d_inode(cfile->dentry), fei, start, &len, 0); 388162306a36Sopenharmony_ci if (rc) 388262306a36Sopenharmony_ci return rc; 388362306a36Sopenharmony_ci 388462306a36Sopenharmony_ci xid = get_xid(); 388562306a36Sopenharmony_ci again: 388662306a36Sopenharmony_ci in_data.file_offset = cpu_to_le64(start); 388762306a36Sopenharmony_ci in_data.length = cpu_to_le64(len); 388862306a36Sopenharmony_ci 388962306a36Sopenharmony_ci rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, 389062306a36Sopenharmony_ci cfile->fid.volatile_fid, 389162306a36Sopenharmony_ci FSCTL_QUERY_ALLOCATED_RANGES, 389262306a36Sopenharmony_ci (char *)&in_data, sizeof(in_data), 389362306a36Sopenharmony_ci 1024 * sizeof(struct file_allocated_range_buffer), 389462306a36Sopenharmony_ci (char **)&out_data, &out_data_len); 389562306a36Sopenharmony_ci if (rc == -E2BIG) { 389662306a36Sopenharmony_ci last_blob = 0; 389762306a36Sopenharmony_ci rc = 0; 389862306a36Sopenharmony_ci } else 389962306a36Sopenharmony_ci last_blob = 1; 390062306a36Sopenharmony_ci if (rc) 390162306a36Sopenharmony_ci goto out; 390262306a36Sopenharmony_ci 390362306a36Sopenharmony_ci if (out_data_len && out_data_len < sizeof(struct file_allocated_range_buffer)) { 390462306a36Sopenharmony_ci rc = -EINVAL; 390562306a36Sopenharmony_ci goto out; 390662306a36Sopenharmony_ci } 390762306a36Sopenharmony_ci if (out_data_len % sizeof(struct file_allocated_range_buffer)) { 390862306a36Sopenharmony_ci rc = -EINVAL; 390962306a36Sopenharmony_ci goto out; 391062306a36Sopenharmony_ci } 391162306a36Sopenharmony_ci 391262306a36Sopenharmony_ci num = out_data_len / sizeof(struct file_allocated_range_buffer); 391362306a36Sopenharmony_ci for (i = 0; i < num; i++) { 391462306a36Sopenharmony_ci flags = 0; 391562306a36Sopenharmony_ci if (i == num - 1 && last_blob) 391662306a36Sopenharmony_ci flags |= FIEMAP_EXTENT_LAST; 391762306a36Sopenharmony_ci 391862306a36Sopenharmony_ci rc = fiemap_fill_next_extent(fei, 391962306a36Sopenharmony_ci le64_to_cpu(out_data[i].file_offset), 392062306a36Sopenharmony_ci le64_to_cpu(out_data[i].file_offset), 392162306a36Sopenharmony_ci le64_to_cpu(out_data[i].length), 392262306a36Sopenharmony_ci flags); 392362306a36Sopenharmony_ci if (rc < 0) 392462306a36Sopenharmony_ci goto out; 392562306a36Sopenharmony_ci if (rc == 1) { 392662306a36Sopenharmony_ci rc = 0; 392762306a36Sopenharmony_ci goto out; 392862306a36Sopenharmony_ci } 392962306a36Sopenharmony_ci } 393062306a36Sopenharmony_ci 393162306a36Sopenharmony_ci if (!last_blob) { 393262306a36Sopenharmony_ci next = le64_to_cpu(out_data[num - 1].file_offset) + 393362306a36Sopenharmony_ci le64_to_cpu(out_data[num - 1].length); 393462306a36Sopenharmony_ci len = len - (next - start); 393562306a36Sopenharmony_ci start = next; 393662306a36Sopenharmony_ci goto again; 393762306a36Sopenharmony_ci } 393862306a36Sopenharmony_ci 393962306a36Sopenharmony_ci out: 394062306a36Sopenharmony_ci free_xid(xid); 394162306a36Sopenharmony_ci kfree(out_data); 394262306a36Sopenharmony_ci return rc; 394362306a36Sopenharmony_ci} 394462306a36Sopenharmony_ci 394562306a36Sopenharmony_cistatic long smb3_fallocate(struct file *file, struct cifs_tcon *tcon, int mode, 394662306a36Sopenharmony_ci loff_t off, loff_t len) 394762306a36Sopenharmony_ci{ 394862306a36Sopenharmony_ci /* KEEP_SIZE already checked for by do_fallocate */ 394962306a36Sopenharmony_ci if (mode & FALLOC_FL_PUNCH_HOLE) 395062306a36Sopenharmony_ci return smb3_punch_hole(file, tcon, off, len); 395162306a36Sopenharmony_ci else if (mode & FALLOC_FL_ZERO_RANGE) { 395262306a36Sopenharmony_ci if (mode & FALLOC_FL_KEEP_SIZE) 395362306a36Sopenharmony_ci return smb3_zero_range(file, tcon, off, len, true); 395462306a36Sopenharmony_ci return smb3_zero_range(file, tcon, off, len, false); 395562306a36Sopenharmony_ci } else if (mode == FALLOC_FL_KEEP_SIZE) 395662306a36Sopenharmony_ci return smb3_simple_falloc(file, tcon, off, len, true); 395762306a36Sopenharmony_ci else if (mode == FALLOC_FL_COLLAPSE_RANGE) 395862306a36Sopenharmony_ci return smb3_collapse_range(file, tcon, off, len); 395962306a36Sopenharmony_ci else if (mode == FALLOC_FL_INSERT_RANGE) 396062306a36Sopenharmony_ci return smb3_insert_range(file, tcon, off, len); 396162306a36Sopenharmony_ci else if (mode == 0) 396262306a36Sopenharmony_ci return smb3_simple_falloc(file, tcon, off, len, false); 396362306a36Sopenharmony_ci 396462306a36Sopenharmony_ci return -EOPNOTSUPP; 396562306a36Sopenharmony_ci} 396662306a36Sopenharmony_ci 396762306a36Sopenharmony_cistatic void 396862306a36Sopenharmony_cismb2_downgrade_oplock(struct TCP_Server_Info *server, 396962306a36Sopenharmony_ci struct cifsInodeInfo *cinode, __u32 oplock, 397062306a36Sopenharmony_ci unsigned int epoch, bool *purge_cache) 397162306a36Sopenharmony_ci{ 397262306a36Sopenharmony_ci server->ops->set_oplock_level(cinode, oplock, 0, NULL); 397362306a36Sopenharmony_ci} 397462306a36Sopenharmony_ci 397562306a36Sopenharmony_cistatic void 397662306a36Sopenharmony_cismb21_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, 397762306a36Sopenharmony_ci unsigned int epoch, bool *purge_cache); 397862306a36Sopenharmony_ci 397962306a36Sopenharmony_cistatic void 398062306a36Sopenharmony_cismb3_downgrade_oplock(struct TCP_Server_Info *server, 398162306a36Sopenharmony_ci struct cifsInodeInfo *cinode, __u32 oplock, 398262306a36Sopenharmony_ci unsigned int epoch, bool *purge_cache) 398362306a36Sopenharmony_ci{ 398462306a36Sopenharmony_ci unsigned int old_state = cinode->oplock; 398562306a36Sopenharmony_ci unsigned int old_epoch = cinode->epoch; 398662306a36Sopenharmony_ci unsigned int new_state; 398762306a36Sopenharmony_ci 398862306a36Sopenharmony_ci if (epoch > old_epoch) { 398962306a36Sopenharmony_ci smb21_set_oplock_level(cinode, oplock, 0, NULL); 399062306a36Sopenharmony_ci cinode->epoch = epoch; 399162306a36Sopenharmony_ci } 399262306a36Sopenharmony_ci 399362306a36Sopenharmony_ci new_state = cinode->oplock; 399462306a36Sopenharmony_ci *purge_cache = false; 399562306a36Sopenharmony_ci 399662306a36Sopenharmony_ci if ((old_state & CIFS_CACHE_READ_FLG) != 0 && 399762306a36Sopenharmony_ci (new_state & CIFS_CACHE_READ_FLG) == 0) 399862306a36Sopenharmony_ci *purge_cache = true; 399962306a36Sopenharmony_ci else if (old_state == new_state && (epoch - old_epoch > 1)) 400062306a36Sopenharmony_ci *purge_cache = true; 400162306a36Sopenharmony_ci} 400262306a36Sopenharmony_ci 400362306a36Sopenharmony_cistatic void 400462306a36Sopenharmony_cismb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, 400562306a36Sopenharmony_ci unsigned int epoch, bool *purge_cache) 400662306a36Sopenharmony_ci{ 400762306a36Sopenharmony_ci oplock &= 0xFF; 400862306a36Sopenharmony_ci cinode->lease_granted = false; 400962306a36Sopenharmony_ci if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE) 401062306a36Sopenharmony_ci return; 401162306a36Sopenharmony_ci if (oplock == SMB2_OPLOCK_LEVEL_BATCH) { 401262306a36Sopenharmony_ci cinode->oplock = CIFS_CACHE_RHW_FLG; 401362306a36Sopenharmony_ci cifs_dbg(FYI, "Batch Oplock granted on inode %p\n", 401462306a36Sopenharmony_ci &cinode->netfs.inode); 401562306a36Sopenharmony_ci } else if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) { 401662306a36Sopenharmony_ci cinode->oplock = CIFS_CACHE_RW_FLG; 401762306a36Sopenharmony_ci cifs_dbg(FYI, "Exclusive Oplock granted on inode %p\n", 401862306a36Sopenharmony_ci &cinode->netfs.inode); 401962306a36Sopenharmony_ci } else if (oplock == SMB2_OPLOCK_LEVEL_II) { 402062306a36Sopenharmony_ci cinode->oplock = CIFS_CACHE_READ_FLG; 402162306a36Sopenharmony_ci cifs_dbg(FYI, "Level II Oplock granted on inode %p\n", 402262306a36Sopenharmony_ci &cinode->netfs.inode); 402362306a36Sopenharmony_ci } else 402462306a36Sopenharmony_ci cinode->oplock = 0; 402562306a36Sopenharmony_ci} 402662306a36Sopenharmony_ci 402762306a36Sopenharmony_cistatic void 402862306a36Sopenharmony_cismb21_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, 402962306a36Sopenharmony_ci unsigned int epoch, bool *purge_cache) 403062306a36Sopenharmony_ci{ 403162306a36Sopenharmony_ci char message[5] = {0}; 403262306a36Sopenharmony_ci unsigned int new_oplock = 0; 403362306a36Sopenharmony_ci 403462306a36Sopenharmony_ci oplock &= 0xFF; 403562306a36Sopenharmony_ci cinode->lease_granted = true; 403662306a36Sopenharmony_ci if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE) 403762306a36Sopenharmony_ci return; 403862306a36Sopenharmony_ci 403962306a36Sopenharmony_ci /* Check if the server granted an oplock rather than a lease */ 404062306a36Sopenharmony_ci if (oplock & SMB2_OPLOCK_LEVEL_EXCLUSIVE) 404162306a36Sopenharmony_ci return smb2_set_oplock_level(cinode, oplock, epoch, 404262306a36Sopenharmony_ci purge_cache); 404362306a36Sopenharmony_ci 404462306a36Sopenharmony_ci if (oplock & SMB2_LEASE_READ_CACHING_HE) { 404562306a36Sopenharmony_ci new_oplock |= CIFS_CACHE_READ_FLG; 404662306a36Sopenharmony_ci strcat(message, "R"); 404762306a36Sopenharmony_ci } 404862306a36Sopenharmony_ci if (oplock & SMB2_LEASE_HANDLE_CACHING_HE) { 404962306a36Sopenharmony_ci new_oplock |= CIFS_CACHE_HANDLE_FLG; 405062306a36Sopenharmony_ci strcat(message, "H"); 405162306a36Sopenharmony_ci } 405262306a36Sopenharmony_ci if (oplock & SMB2_LEASE_WRITE_CACHING_HE) { 405362306a36Sopenharmony_ci new_oplock |= CIFS_CACHE_WRITE_FLG; 405462306a36Sopenharmony_ci strcat(message, "W"); 405562306a36Sopenharmony_ci } 405662306a36Sopenharmony_ci if (!new_oplock) 405762306a36Sopenharmony_ci strncpy(message, "None", sizeof(message)); 405862306a36Sopenharmony_ci 405962306a36Sopenharmony_ci cinode->oplock = new_oplock; 406062306a36Sopenharmony_ci cifs_dbg(FYI, "%s Lease granted on inode %p\n", message, 406162306a36Sopenharmony_ci &cinode->netfs.inode); 406262306a36Sopenharmony_ci} 406362306a36Sopenharmony_ci 406462306a36Sopenharmony_cistatic void 406562306a36Sopenharmony_cismb3_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, 406662306a36Sopenharmony_ci unsigned int epoch, bool *purge_cache) 406762306a36Sopenharmony_ci{ 406862306a36Sopenharmony_ci unsigned int old_oplock = cinode->oplock; 406962306a36Sopenharmony_ci 407062306a36Sopenharmony_ci smb21_set_oplock_level(cinode, oplock, epoch, purge_cache); 407162306a36Sopenharmony_ci 407262306a36Sopenharmony_ci if (purge_cache) { 407362306a36Sopenharmony_ci *purge_cache = false; 407462306a36Sopenharmony_ci if (old_oplock == CIFS_CACHE_READ_FLG) { 407562306a36Sopenharmony_ci if (cinode->oplock == CIFS_CACHE_READ_FLG && 407662306a36Sopenharmony_ci (epoch - cinode->epoch > 0)) 407762306a36Sopenharmony_ci *purge_cache = true; 407862306a36Sopenharmony_ci else if (cinode->oplock == CIFS_CACHE_RH_FLG && 407962306a36Sopenharmony_ci (epoch - cinode->epoch > 1)) 408062306a36Sopenharmony_ci *purge_cache = true; 408162306a36Sopenharmony_ci else if (cinode->oplock == CIFS_CACHE_RHW_FLG && 408262306a36Sopenharmony_ci (epoch - cinode->epoch > 1)) 408362306a36Sopenharmony_ci *purge_cache = true; 408462306a36Sopenharmony_ci else if (cinode->oplock == 0 && 408562306a36Sopenharmony_ci (epoch - cinode->epoch > 0)) 408662306a36Sopenharmony_ci *purge_cache = true; 408762306a36Sopenharmony_ci } else if (old_oplock == CIFS_CACHE_RH_FLG) { 408862306a36Sopenharmony_ci if (cinode->oplock == CIFS_CACHE_RH_FLG && 408962306a36Sopenharmony_ci (epoch - cinode->epoch > 0)) 409062306a36Sopenharmony_ci *purge_cache = true; 409162306a36Sopenharmony_ci else if (cinode->oplock == CIFS_CACHE_RHW_FLG && 409262306a36Sopenharmony_ci (epoch - cinode->epoch > 1)) 409362306a36Sopenharmony_ci *purge_cache = true; 409462306a36Sopenharmony_ci } 409562306a36Sopenharmony_ci cinode->epoch = epoch; 409662306a36Sopenharmony_ci } 409762306a36Sopenharmony_ci} 409862306a36Sopenharmony_ci 409962306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY 410062306a36Sopenharmony_cistatic bool 410162306a36Sopenharmony_cismb2_is_read_op(__u32 oplock) 410262306a36Sopenharmony_ci{ 410362306a36Sopenharmony_ci return oplock == SMB2_OPLOCK_LEVEL_II; 410462306a36Sopenharmony_ci} 410562306a36Sopenharmony_ci#endif /* CIFS_ALLOW_INSECURE_LEGACY */ 410662306a36Sopenharmony_ci 410762306a36Sopenharmony_cistatic bool 410862306a36Sopenharmony_cismb21_is_read_op(__u32 oplock) 410962306a36Sopenharmony_ci{ 411062306a36Sopenharmony_ci return (oplock & SMB2_LEASE_READ_CACHING_HE) && 411162306a36Sopenharmony_ci !(oplock & SMB2_LEASE_WRITE_CACHING_HE); 411262306a36Sopenharmony_ci} 411362306a36Sopenharmony_ci 411462306a36Sopenharmony_cistatic __le32 411562306a36Sopenharmony_cimap_oplock_to_lease(u8 oplock) 411662306a36Sopenharmony_ci{ 411762306a36Sopenharmony_ci if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) 411862306a36Sopenharmony_ci return SMB2_LEASE_WRITE_CACHING_LE | SMB2_LEASE_READ_CACHING_LE; 411962306a36Sopenharmony_ci else if (oplock == SMB2_OPLOCK_LEVEL_II) 412062306a36Sopenharmony_ci return SMB2_LEASE_READ_CACHING_LE; 412162306a36Sopenharmony_ci else if (oplock == SMB2_OPLOCK_LEVEL_BATCH) 412262306a36Sopenharmony_ci return SMB2_LEASE_HANDLE_CACHING_LE | SMB2_LEASE_READ_CACHING_LE | 412362306a36Sopenharmony_ci SMB2_LEASE_WRITE_CACHING_LE; 412462306a36Sopenharmony_ci return 0; 412562306a36Sopenharmony_ci} 412662306a36Sopenharmony_ci 412762306a36Sopenharmony_cistatic char * 412862306a36Sopenharmony_cismb2_create_lease_buf(u8 *lease_key, u8 oplock) 412962306a36Sopenharmony_ci{ 413062306a36Sopenharmony_ci struct create_lease *buf; 413162306a36Sopenharmony_ci 413262306a36Sopenharmony_ci buf = kzalloc(sizeof(struct create_lease), GFP_KERNEL); 413362306a36Sopenharmony_ci if (!buf) 413462306a36Sopenharmony_ci return NULL; 413562306a36Sopenharmony_ci 413662306a36Sopenharmony_ci memcpy(&buf->lcontext.LeaseKey, lease_key, SMB2_LEASE_KEY_SIZE); 413762306a36Sopenharmony_ci buf->lcontext.LeaseState = map_oplock_to_lease(oplock); 413862306a36Sopenharmony_ci 413962306a36Sopenharmony_ci buf->ccontext.DataOffset = cpu_to_le16(offsetof 414062306a36Sopenharmony_ci (struct create_lease, lcontext)); 414162306a36Sopenharmony_ci buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context)); 414262306a36Sopenharmony_ci buf->ccontext.NameOffset = cpu_to_le16(offsetof 414362306a36Sopenharmony_ci (struct create_lease, Name)); 414462306a36Sopenharmony_ci buf->ccontext.NameLength = cpu_to_le16(4); 414562306a36Sopenharmony_ci /* SMB2_CREATE_REQUEST_LEASE is "RqLs" */ 414662306a36Sopenharmony_ci buf->Name[0] = 'R'; 414762306a36Sopenharmony_ci buf->Name[1] = 'q'; 414862306a36Sopenharmony_ci buf->Name[2] = 'L'; 414962306a36Sopenharmony_ci buf->Name[3] = 's'; 415062306a36Sopenharmony_ci return (char *)buf; 415162306a36Sopenharmony_ci} 415262306a36Sopenharmony_ci 415362306a36Sopenharmony_cistatic char * 415462306a36Sopenharmony_cismb3_create_lease_buf(u8 *lease_key, u8 oplock) 415562306a36Sopenharmony_ci{ 415662306a36Sopenharmony_ci struct create_lease_v2 *buf; 415762306a36Sopenharmony_ci 415862306a36Sopenharmony_ci buf = kzalloc(sizeof(struct create_lease_v2), GFP_KERNEL); 415962306a36Sopenharmony_ci if (!buf) 416062306a36Sopenharmony_ci return NULL; 416162306a36Sopenharmony_ci 416262306a36Sopenharmony_ci memcpy(&buf->lcontext.LeaseKey, lease_key, SMB2_LEASE_KEY_SIZE); 416362306a36Sopenharmony_ci buf->lcontext.LeaseState = map_oplock_to_lease(oplock); 416462306a36Sopenharmony_ci 416562306a36Sopenharmony_ci buf->ccontext.DataOffset = cpu_to_le16(offsetof 416662306a36Sopenharmony_ci (struct create_lease_v2, lcontext)); 416762306a36Sopenharmony_ci buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context_v2)); 416862306a36Sopenharmony_ci buf->ccontext.NameOffset = cpu_to_le16(offsetof 416962306a36Sopenharmony_ci (struct create_lease_v2, Name)); 417062306a36Sopenharmony_ci buf->ccontext.NameLength = cpu_to_le16(4); 417162306a36Sopenharmony_ci /* SMB2_CREATE_REQUEST_LEASE is "RqLs" */ 417262306a36Sopenharmony_ci buf->Name[0] = 'R'; 417362306a36Sopenharmony_ci buf->Name[1] = 'q'; 417462306a36Sopenharmony_ci buf->Name[2] = 'L'; 417562306a36Sopenharmony_ci buf->Name[3] = 's'; 417662306a36Sopenharmony_ci return (char *)buf; 417762306a36Sopenharmony_ci} 417862306a36Sopenharmony_ci 417962306a36Sopenharmony_cistatic __u8 418062306a36Sopenharmony_cismb2_parse_lease_buf(void *buf, unsigned int *epoch, char *lease_key) 418162306a36Sopenharmony_ci{ 418262306a36Sopenharmony_ci struct create_lease *lc = (struct create_lease *)buf; 418362306a36Sopenharmony_ci 418462306a36Sopenharmony_ci *epoch = 0; /* not used */ 418562306a36Sopenharmony_ci if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE) 418662306a36Sopenharmony_ci return SMB2_OPLOCK_LEVEL_NOCHANGE; 418762306a36Sopenharmony_ci return le32_to_cpu(lc->lcontext.LeaseState); 418862306a36Sopenharmony_ci} 418962306a36Sopenharmony_ci 419062306a36Sopenharmony_cistatic __u8 419162306a36Sopenharmony_cismb3_parse_lease_buf(void *buf, unsigned int *epoch, char *lease_key) 419262306a36Sopenharmony_ci{ 419362306a36Sopenharmony_ci struct create_lease_v2 *lc = (struct create_lease_v2 *)buf; 419462306a36Sopenharmony_ci 419562306a36Sopenharmony_ci *epoch = le16_to_cpu(lc->lcontext.Epoch); 419662306a36Sopenharmony_ci if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE) 419762306a36Sopenharmony_ci return SMB2_OPLOCK_LEVEL_NOCHANGE; 419862306a36Sopenharmony_ci if (lease_key) 419962306a36Sopenharmony_ci memcpy(lease_key, &lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); 420062306a36Sopenharmony_ci return le32_to_cpu(lc->lcontext.LeaseState); 420162306a36Sopenharmony_ci} 420262306a36Sopenharmony_ci 420362306a36Sopenharmony_cistatic unsigned int 420462306a36Sopenharmony_cismb2_wp_retry_size(struct inode *inode) 420562306a36Sopenharmony_ci{ 420662306a36Sopenharmony_ci return min_t(unsigned int, CIFS_SB(inode->i_sb)->ctx->wsize, 420762306a36Sopenharmony_ci SMB2_MAX_BUFFER_SIZE); 420862306a36Sopenharmony_ci} 420962306a36Sopenharmony_ci 421062306a36Sopenharmony_cistatic bool 421162306a36Sopenharmony_cismb2_dir_needs_close(struct cifsFileInfo *cfile) 421262306a36Sopenharmony_ci{ 421362306a36Sopenharmony_ci return !cfile->invalidHandle; 421462306a36Sopenharmony_ci} 421562306a36Sopenharmony_ci 421662306a36Sopenharmony_cistatic void 421762306a36Sopenharmony_cifill_transform_hdr(struct smb2_transform_hdr *tr_hdr, unsigned int orig_len, 421862306a36Sopenharmony_ci struct smb_rqst *old_rq, __le16 cipher_type) 421962306a36Sopenharmony_ci{ 422062306a36Sopenharmony_ci struct smb2_hdr *shdr = 422162306a36Sopenharmony_ci (struct smb2_hdr *)old_rq->rq_iov[0].iov_base; 422262306a36Sopenharmony_ci 422362306a36Sopenharmony_ci memset(tr_hdr, 0, sizeof(struct smb2_transform_hdr)); 422462306a36Sopenharmony_ci tr_hdr->ProtocolId = SMB2_TRANSFORM_PROTO_NUM; 422562306a36Sopenharmony_ci tr_hdr->OriginalMessageSize = cpu_to_le32(orig_len); 422662306a36Sopenharmony_ci tr_hdr->Flags = cpu_to_le16(0x01); 422762306a36Sopenharmony_ci if ((cipher_type == SMB2_ENCRYPTION_AES128_GCM) || 422862306a36Sopenharmony_ci (cipher_type == SMB2_ENCRYPTION_AES256_GCM)) 422962306a36Sopenharmony_ci get_random_bytes(&tr_hdr->Nonce, SMB3_AES_GCM_NONCE); 423062306a36Sopenharmony_ci else 423162306a36Sopenharmony_ci get_random_bytes(&tr_hdr->Nonce, SMB3_AES_CCM_NONCE); 423262306a36Sopenharmony_ci memcpy(&tr_hdr->SessionId, &shdr->SessionId, 8); 423362306a36Sopenharmony_ci} 423462306a36Sopenharmony_ci 423562306a36Sopenharmony_cistatic void *smb2_aead_req_alloc(struct crypto_aead *tfm, const struct smb_rqst *rqst, 423662306a36Sopenharmony_ci int num_rqst, const u8 *sig, u8 **iv, 423762306a36Sopenharmony_ci struct aead_request **req, struct sg_table *sgt, 423862306a36Sopenharmony_ci unsigned int *num_sgs, size_t *sensitive_size) 423962306a36Sopenharmony_ci{ 424062306a36Sopenharmony_ci unsigned int req_size = sizeof(**req) + crypto_aead_reqsize(tfm); 424162306a36Sopenharmony_ci unsigned int iv_size = crypto_aead_ivsize(tfm); 424262306a36Sopenharmony_ci unsigned int len; 424362306a36Sopenharmony_ci u8 *p; 424462306a36Sopenharmony_ci 424562306a36Sopenharmony_ci *num_sgs = cifs_get_num_sgs(rqst, num_rqst, sig); 424662306a36Sopenharmony_ci if (IS_ERR_VALUE((long)(int)*num_sgs)) 424762306a36Sopenharmony_ci return ERR_PTR(*num_sgs); 424862306a36Sopenharmony_ci 424962306a36Sopenharmony_ci len = iv_size; 425062306a36Sopenharmony_ci len += crypto_aead_alignmask(tfm) & ~(crypto_tfm_ctx_alignment() - 1); 425162306a36Sopenharmony_ci len = ALIGN(len, crypto_tfm_ctx_alignment()); 425262306a36Sopenharmony_ci len += req_size; 425362306a36Sopenharmony_ci len = ALIGN(len, __alignof__(struct scatterlist)); 425462306a36Sopenharmony_ci len += array_size(*num_sgs, sizeof(struct scatterlist)); 425562306a36Sopenharmony_ci *sensitive_size = len; 425662306a36Sopenharmony_ci 425762306a36Sopenharmony_ci p = kvzalloc(len, GFP_NOFS); 425862306a36Sopenharmony_ci if (!p) 425962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 426062306a36Sopenharmony_ci 426162306a36Sopenharmony_ci *iv = (u8 *)PTR_ALIGN(p, crypto_aead_alignmask(tfm) + 1); 426262306a36Sopenharmony_ci *req = (struct aead_request *)PTR_ALIGN(*iv + iv_size, 426362306a36Sopenharmony_ci crypto_tfm_ctx_alignment()); 426462306a36Sopenharmony_ci sgt->sgl = (struct scatterlist *)PTR_ALIGN((u8 *)*req + req_size, 426562306a36Sopenharmony_ci __alignof__(struct scatterlist)); 426662306a36Sopenharmony_ci return p; 426762306a36Sopenharmony_ci} 426862306a36Sopenharmony_ci 426962306a36Sopenharmony_cistatic void *smb2_get_aead_req(struct crypto_aead *tfm, struct smb_rqst *rqst, 427062306a36Sopenharmony_ci int num_rqst, const u8 *sig, u8 **iv, 427162306a36Sopenharmony_ci struct aead_request **req, struct scatterlist **sgl, 427262306a36Sopenharmony_ci size_t *sensitive_size) 427362306a36Sopenharmony_ci{ 427462306a36Sopenharmony_ci struct sg_table sgtable = {}; 427562306a36Sopenharmony_ci unsigned int skip, num_sgs, i, j; 427662306a36Sopenharmony_ci ssize_t rc; 427762306a36Sopenharmony_ci void *p; 427862306a36Sopenharmony_ci 427962306a36Sopenharmony_ci p = smb2_aead_req_alloc(tfm, rqst, num_rqst, sig, iv, req, &sgtable, 428062306a36Sopenharmony_ci &num_sgs, sensitive_size); 428162306a36Sopenharmony_ci if (IS_ERR(p)) 428262306a36Sopenharmony_ci return ERR_CAST(p); 428362306a36Sopenharmony_ci 428462306a36Sopenharmony_ci sg_init_marker(sgtable.sgl, num_sgs); 428562306a36Sopenharmony_ci 428662306a36Sopenharmony_ci /* 428762306a36Sopenharmony_ci * The first rqst has a transform header where the 428862306a36Sopenharmony_ci * first 20 bytes are not part of the encrypted blob. 428962306a36Sopenharmony_ci */ 429062306a36Sopenharmony_ci skip = 20; 429162306a36Sopenharmony_ci 429262306a36Sopenharmony_ci for (i = 0; i < num_rqst; i++) { 429362306a36Sopenharmony_ci struct iov_iter *iter = &rqst[i].rq_iter; 429462306a36Sopenharmony_ci size_t count = iov_iter_count(iter); 429562306a36Sopenharmony_ci 429662306a36Sopenharmony_ci for (j = 0; j < rqst[i].rq_nvec; j++) { 429762306a36Sopenharmony_ci cifs_sg_set_buf(&sgtable, 429862306a36Sopenharmony_ci rqst[i].rq_iov[j].iov_base + skip, 429962306a36Sopenharmony_ci rqst[i].rq_iov[j].iov_len - skip); 430062306a36Sopenharmony_ci 430162306a36Sopenharmony_ci /* See the above comment on the 'skip' assignment */ 430262306a36Sopenharmony_ci skip = 0; 430362306a36Sopenharmony_ci } 430462306a36Sopenharmony_ci sgtable.orig_nents = sgtable.nents; 430562306a36Sopenharmony_ci 430662306a36Sopenharmony_ci rc = extract_iter_to_sg(iter, count, &sgtable, 430762306a36Sopenharmony_ci num_sgs - sgtable.nents, 0); 430862306a36Sopenharmony_ci iov_iter_revert(iter, rc); 430962306a36Sopenharmony_ci sgtable.orig_nents = sgtable.nents; 431062306a36Sopenharmony_ci } 431162306a36Sopenharmony_ci 431262306a36Sopenharmony_ci cifs_sg_set_buf(&sgtable, sig, SMB2_SIGNATURE_SIZE); 431362306a36Sopenharmony_ci sg_mark_end(&sgtable.sgl[sgtable.nents - 1]); 431462306a36Sopenharmony_ci *sgl = sgtable.sgl; 431562306a36Sopenharmony_ci return p; 431662306a36Sopenharmony_ci} 431762306a36Sopenharmony_ci 431862306a36Sopenharmony_cistatic int 431962306a36Sopenharmony_cismb2_get_enc_key(struct TCP_Server_Info *server, __u64 ses_id, int enc, u8 *key) 432062306a36Sopenharmony_ci{ 432162306a36Sopenharmony_ci struct TCP_Server_Info *pserver; 432262306a36Sopenharmony_ci struct cifs_ses *ses; 432362306a36Sopenharmony_ci u8 *ses_enc_key; 432462306a36Sopenharmony_ci 432562306a36Sopenharmony_ci /* If server is a channel, select the primary channel */ 432662306a36Sopenharmony_ci pserver = SERVER_IS_CHAN(server) ? server->primary_server : server; 432762306a36Sopenharmony_ci 432862306a36Sopenharmony_ci spin_lock(&cifs_tcp_ses_lock); 432962306a36Sopenharmony_ci list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { 433062306a36Sopenharmony_ci if (ses->Suid == ses_id) { 433162306a36Sopenharmony_ci spin_lock(&ses->ses_lock); 433262306a36Sopenharmony_ci ses_enc_key = enc ? ses->smb3encryptionkey : 433362306a36Sopenharmony_ci ses->smb3decryptionkey; 433462306a36Sopenharmony_ci memcpy(key, ses_enc_key, SMB3_ENC_DEC_KEY_SIZE); 433562306a36Sopenharmony_ci spin_unlock(&ses->ses_lock); 433662306a36Sopenharmony_ci spin_unlock(&cifs_tcp_ses_lock); 433762306a36Sopenharmony_ci return 0; 433862306a36Sopenharmony_ci } 433962306a36Sopenharmony_ci } 434062306a36Sopenharmony_ci spin_unlock(&cifs_tcp_ses_lock); 434162306a36Sopenharmony_ci 434262306a36Sopenharmony_ci trace_smb3_ses_not_found(ses_id); 434362306a36Sopenharmony_ci 434462306a36Sopenharmony_ci return -EAGAIN; 434562306a36Sopenharmony_ci} 434662306a36Sopenharmony_ci/* 434762306a36Sopenharmony_ci * Encrypt or decrypt @rqst message. @rqst[0] has the following format: 434862306a36Sopenharmony_ci * iov[0] - transform header (associate data), 434962306a36Sopenharmony_ci * iov[1-N] - SMB2 header and pages - data to encrypt. 435062306a36Sopenharmony_ci * On success return encrypted data in iov[1-N] and pages, leave iov[0] 435162306a36Sopenharmony_ci * untouched. 435262306a36Sopenharmony_ci */ 435362306a36Sopenharmony_cistatic int 435462306a36Sopenharmony_cicrypt_message(struct TCP_Server_Info *server, int num_rqst, 435562306a36Sopenharmony_ci struct smb_rqst *rqst, int enc) 435662306a36Sopenharmony_ci{ 435762306a36Sopenharmony_ci struct smb2_transform_hdr *tr_hdr = 435862306a36Sopenharmony_ci (struct smb2_transform_hdr *)rqst[0].rq_iov[0].iov_base; 435962306a36Sopenharmony_ci unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20; 436062306a36Sopenharmony_ci int rc = 0; 436162306a36Sopenharmony_ci struct scatterlist *sg; 436262306a36Sopenharmony_ci u8 sign[SMB2_SIGNATURE_SIZE] = {}; 436362306a36Sopenharmony_ci u8 key[SMB3_ENC_DEC_KEY_SIZE]; 436462306a36Sopenharmony_ci struct aead_request *req; 436562306a36Sopenharmony_ci u8 *iv; 436662306a36Sopenharmony_ci DECLARE_CRYPTO_WAIT(wait); 436762306a36Sopenharmony_ci struct crypto_aead *tfm; 436862306a36Sopenharmony_ci unsigned int crypt_len = le32_to_cpu(tr_hdr->OriginalMessageSize); 436962306a36Sopenharmony_ci void *creq; 437062306a36Sopenharmony_ci size_t sensitive_size; 437162306a36Sopenharmony_ci 437262306a36Sopenharmony_ci rc = smb2_get_enc_key(server, le64_to_cpu(tr_hdr->SessionId), enc, key); 437362306a36Sopenharmony_ci if (rc) { 437462306a36Sopenharmony_ci cifs_server_dbg(FYI, "%s: Could not get %scryption key. sid: 0x%llx\n", __func__, 437562306a36Sopenharmony_ci enc ? "en" : "de", le64_to_cpu(tr_hdr->SessionId)); 437662306a36Sopenharmony_ci return rc; 437762306a36Sopenharmony_ci } 437862306a36Sopenharmony_ci 437962306a36Sopenharmony_ci rc = smb3_crypto_aead_allocate(server); 438062306a36Sopenharmony_ci if (rc) { 438162306a36Sopenharmony_ci cifs_server_dbg(VFS, "%s: crypto alloc failed\n", __func__); 438262306a36Sopenharmony_ci return rc; 438362306a36Sopenharmony_ci } 438462306a36Sopenharmony_ci 438562306a36Sopenharmony_ci tfm = enc ? server->secmech.enc : server->secmech.dec; 438662306a36Sopenharmony_ci 438762306a36Sopenharmony_ci if ((server->cipher_type == SMB2_ENCRYPTION_AES256_CCM) || 438862306a36Sopenharmony_ci (server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) 438962306a36Sopenharmony_ci rc = crypto_aead_setkey(tfm, key, SMB3_GCM256_CRYPTKEY_SIZE); 439062306a36Sopenharmony_ci else 439162306a36Sopenharmony_ci rc = crypto_aead_setkey(tfm, key, SMB3_GCM128_CRYPTKEY_SIZE); 439262306a36Sopenharmony_ci 439362306a36Sopenharmony_ci if (rc) { 439462306a36Sopenharmony_ci cifs_server_dbg(VFS, "%s: Failed to set aead key %d\n", __func__, rc); 439562306a36Sopenharmony_ci return rc; 439662306a36Sopenharmony_ci } 439762306a36Sopenharmony_ci 439862306a36Sopenharmony_ci rc = crypto_aead_setauthsize(tfm, SMB2_SIGNATURE_SIZE); 439962306a36Sopenharmony_ci if (rc) { 440062306a36Sopenharmony_ci cifs_server_dbg(VFS, "%s: Failed to set authsize %d\n", __func__, rc); 440162306a36Sopenharmony_ci return rc; 440262306a36Sopenharmony_ci } 440362306a36Sopenharmony_ci 440462306a36Sopenharmony_ci creq = smb2_get_aead_req(tfm, rqst, num_rqst, sign, &iv, &req, &sg, 440562306a36Sopenharmony_ci &sensitive_size); 440662306a36Sopenharmony_ci if (IS_ERR(creq)) 440762306a36Sopenharmony_ci return PTR_ERR(creq); 440862306a36Sopenharmony_ci 440962306a36Sopenharmony_ci if (!enc) { 441062306a36Sopenharmony_ci memcpy(sign, &tr_hdr->Signature, SMB2_SIGNATURE_SIZE); 441162306a36Sopenharmony_ci crypt_len += SMB2_SIGNATURE_SIZE; 441262306a36Sopenharmony_ci } 441362306a36Sopenharmony_ci 441462306a36Sopenharmony_ci if ((server->cipher_type == SMB2_ENCRYPTION_AES128_GCM) || 441562306a36Sopenharmony_ci (server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) 441662306a36Sopenharmony_ci memcpy(iv, (char *)tr_hdr->Nonce, SMB3_AES_GCM_NONCE); 441762306a36Sopenharmony_ci else { 441862306a36Sopenharmony_ci iv[0] = 3; 441962306a36Sopenharmony_ci memcpy(iv + 1, (char *)tr_hdr->Nonce, SMB3_AES_CCM_NONCE); 442062306a36Sopenharmony_ci } 442162306a36Sopenharmony_ci 442262306a36Sopenharmony_ci aead_request_set_tfm(req, tfm); 442362306a36Sopenharmony_ci aead_request_set_crypt(req, sg, sg, crypt_len, iv); 442462306a36Sopenharmony_ci aead_request_set_ad(req, assoc_data_len); 442562306a36Sopenharmony_ci 442662306a36Sopenharmony_ci aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, 442762306a36Sopenharmony_ci crypto_req_done, &wait); 442862306a36Sopenharmony_ci 442962306a36Sopenharmony_ci rc = crypto_wait_req(enc ? crypto_aead_encrypt(req) 443062306a36Sopenharmony_ci : crypto_aead_decrypt(req), &wait); 443162306a36Sopenharmony_ci 443262306a36Sopenharmony_ci if (!rc && enc) 443362306a36Sopenharmony_ci memcpy(&tr_hdr->Signature, sign, SMB2_SIGNATURE_SIZE); 443462306a36Sopenharmony_ci 443562306a36Sopenharmony_ci kvfree_sensitive(creq, sensitive_size); 443662306a36Sopenharmony_ci return rc; 443762306a36Sopenharmony_ci} 443862306a36Sopenharmony_ci 443962306a36Sopenharmony_ci/* 444062306a36Sopenharmony_ci * Clear a read buffer, discarding the folios which have XA_MARK_0 set. 444162306a36Sopenharmony_ci */ 444262306a36Sopenharmony_cistatic void cifs_clear_xarray_buffer(struct xarray *buffer) 444362306a36Sopenharmony_ci{ 444462306a36Sopenharmony_ci struct folio *folio; 444562306a36Sopenharmony_ci 444662306a36Sopenharmony_ci XA_STATE(xas, buffer, 0); 444762306a36Sopenharmony_ci 444862306a36Sopenharmony_ci rcu_read_lock(); 444962306a36Sopenharmony_ci xas_for_each_marked(&xas, folio, ULONG_MAX, XA_MARK_0) { 445062306a36Sopenharmony_ci folio_put(folio); 445162306a36Sopenharmony_ci } 445262306a36Sopenharmony_ci rcu_read_unlock(); 445362306a36Sopenharmony_ci xa_destroy(buffer); 445462306a36Sopenharmony_ci} 445562306a36Sopenharmony_ci 445662306a36Sopenharmony_civoid 445762306a36Sopenharmony_cismb3_free_compound_rqst(int num_rqst, struct smb_rqst *rqst) 445862306a36Sopenharmony_ci{ 445962306a36Sopenharmony_ci int i; 446062306a36Sopenharmony_ci 446162306a36Sopenharmony_ci for (i = 0; i < num_rqst; i++) 446262306a36Sopenharmony_ci if (!xa_empty(&rqst[i].rq_buffer)) 446362306a36Sopenharmony_ci cifs_clear_xarray_buffer(&rqst[i].rq_buffer); 446462306a36Sopenharmony_ci} 446562306a36Sopenharmony_ci 446662306a36Sopenharmony_ci/* 446762306a36Sopenharmony_ci * This function will initialize new_rq and encrypt the content. 446862306a36Sopenharmony_ci * The first entry, new_rq[0], only contains a single iov which contains 446962306a36Sopenharmony_ci * a smb2_transform_hdr and is pre-allocated by the caller. 447062306a36Sopenharmony_ci * This function then populates new_rq[1+] with the content from olq_rq[0+]. 447162306a36Sopenharmony_ci * 447262306a36Sopenharmony_ci * The end result is an array of smb_rqst structures where the first structure 447362306a36Sopenharmony_ci * only contains a single iov for the transform header which we then can pass 447462306a36Sopenharmony_ci * to crypt_message(). 447562306a36Sopenharmony_ci * 447662306a36Sopenharmony_ci * new_rq[0].rq_iov[0] : smb2_transform_hdr pre-allocated by the caller 447762306a36Sopenharmony_ci * new_rq[1+].rq_iov[*] == old_rq[0+].rq_iov[*] : SMB2/3 requests 447862306a36Sopenharmony_ci */ 447962306a36Sopenharmony_cistatic int 448062306a36Sopenharmony_cismb3_init_transform_rq(struct TCP_Server_Info *server, int num_rqst, 448162306a36Sopenharmony_ci struct smb_rqst *new_rq, struct smb_rqst *old_rq) 448262306a36Sopenharmony_ci{ 448362306a36Sopenharmony_ci struct smb2_transform_hdr *tr_hdr = new_rq[0].rq_iov[0].iov_base; 448462306a36Sopenharmony_ci struct page *page; 448562306a36Sopenharmony_ci unsigned int orig_len = 0; 448662306a36Sopenharmony_ci int i, j; 448762306a36Sopenharmony_ci int rc = -ENOMEM; 448862306a36Sopenharmony_ci 448962306a36Sopenharmony_ci for (i = 1; i < num_rqst; i++) { 449062306a36Sopenharmony_ci struct smb_rqst *old = &old_rq[i - 1]; 449162306a36Sopenharmony_ci struct smb_rqst *new = &new_rq[i]; 449262306a36Sopenharmony_ci struct xarray *buffer = &new->rq_buffer; 449362306a36Sopenharmony_ci size_t size = iov_iter_count(&old->rq_iter), seg, copied = 0; 449462306a36Sopenharmony_ci 449562306a36Sopenharmony_ci orig_len += smb_rqst_len(server, old); 449662306a36Sopenharmony_ci new->rq_iov = old->rq_iov; 449762306a36Sopenharmony_ci new->rq_nvec = old->rq_nvec; 449862306a36Sopenharmony_ci 449962306a36Sopenharmony_ci xa_init(buffer); 450062306a36Sopenharmony_ci 450162306a36Sopenharmony_ci if (size > 0) { 450262306a36Sopenharmony_ci unsigned int npages = DIV_ROUND_UP(size, PAGE_SIZE); 450362306a36Sopenharmony_ci 450462306a36Sopenharmony_ci for (j = 0; j < npages; j++) { 450562306a36Sopenharmony_ci void *o; 450662306a36Sopenharmony_ci 450762306a36Sopenharmony_ci rc = -ENOMEM; 450862306a36Sopenharmony_ci page = alloc_page(GFP_KERNEL|__GFP_HIGHMEM); 450962306a36Sopenharmony_ci if (!page) 451062306a36Sopenharmony_ci goto err_free; 451162306a36Sopenharmony_ci page->index = j; 451262306a36Sopenharmony_ci o = xa_store(buffer, j, page, GFP_KERNEL); 451362306a36Sopenharmony_ci if (xa_is_err(o)) { 451462306a36Sopenharmony_ci rc = xa_err(o); 451562306a36Sopenharmony_ci put_page(page); 451662306a36Sopenharmony_ci goto err_free; 451762306a36Sopenharmony_ci } 451862306a36Sopenharmony_ci 451962306a36Sopenharmony_ci xa_set_mark(buffer, j, XA_MARK_0); 452062306a36Sopenharmony_ci 452162306a36Sopenharmony_ci seg = min_t(size_t, size - copied, PAGE_SIZE); 452262306a36Sopenharmony_ci if (copy_page_from_iter(page, 0, seg, &old->rq_iter) != seg) { 452362306a36Sopenharmony_ci rc = -EFAULT; 452462306a36Sopenharmony_ci goto err_free; 452562306a36Sopenharmony_ci } 452662306a36Sopenharmony_ci copied += seg; 452762306a36Sopenharmony_ci } 452862306a36Sopenharmony_ci iov_iter_xarray(&new->rq_iter, ITER_SOURCE, 452962306a36Sopenharmony_ci buffer, 0, size); 453062306a36Sopenharmony_ci new->rq_iter_size = size; 453162306a36Sopenharmony_ci } 453262306a36Sopenharmony_ci } 453362306a36Sopenharmony_ci 453462306a36Sopenharmony_ci /* fill the 1st iov with a transform header */ 453562306a36Sopenharmony_ci fill_transform_hdr(tr_hdr, orig_len, old_rq, server->cipher_type); 453662306a36Sopenharmony_ci 453762306a36Sopenharmony_ci rc = crypt_message(server, num_rqst, new_rq, 1); 453862306a36Sopenharmony_ci cifs_dbg(FYI, "Encrypt message returned %d\n", rc); 453962306a36Sopenharmony_ci if (rc) 454062306a36Sopenharmony_ci goto err_free; 454162306a36Sopenharmony_ci 454262306a36Sopenharmony_ci return rc; 454362306a36Sopenharmony_ci 454462306a36Sopenharmony_cierr_free: 454562306a36Sopenharmony_ci smb3_free_compound_rqst(num_rqst - 1, &new_rq[1]); 454662306a36Sopenharmony_ci return rc; 454762306a36Sopenharmony_ci} 454862306a36Sopenharmony_ci 454962306a36Sopenharmony_cistatic int 455062306a36Sopenharmony_cismb3_is_transform_hdr(void *buf) 455162306a36Sopenharmony_ci{ 455262306a36Sopenharmony_ci struct smb2_transform_hdr *trhdr = buf; 455362306a36Sopenharmony_ci 455462306a36Sopenharmony_ci return trhdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM; 455562306a36Sopenharmony_ci} 455662306a36Sopenharmony_ci 455762306a36Sopenharmony_cistatic int 455862306a36Sopenharmony_cidecrypt_raw_data(struct TCP_Server_Info *server, char *buf, 455962306a36Sopenharmony_ci unsigned int buf_data_size, struct iov_iter *iter, 456062306a36Sopenharmony_ci bool is_offloaded) 456162306a36Sopenharmony_ci{ 456262306a36Sopenharmony_ci struct kvec iov[2]; 456362306a36Sopenharmony_ci struct smb_rqst rqst = {NULL}; 456462306a36Sopenharmony_ci size_t iter_size = 0; 456562306a36Sopenharmony_ci int rc; 456662306a36Sopenharmony_ci 456762306a36Sopenharmony_ci iov[0].iov_base = buf; 456862306a36Sopenharmony_ci iov[0].iov_len = sizeof(struct smb2_transform_hdr); 456962306a36Sopenharmony_ci iov[1].iov_base = buf + sizeof(struct smb2_transform_hdr); 457062306a36Sopenharmony_ci iov[1].iov_len = buf_data_size; 457162306a36Sopenharmony_ci 457262306a36Sopenharmony_ci rqst.rq_iov = iov; 457362306a36Sopenharmony_ci rqst.rq_nvec = 2; 457462306a36Sopenharmony_ci if (iter) { 457562306a36Sopenharmony_ci rqst.rq_iter = *iter; 457662306a36Sopenharmony_ci rqst.rq_iter_size = iov_iter_count(iter); 457762306a36Sopenharmony_ci iter_size = iov_iter_count(iter); 457862306a36Sopenharmony_ci } 457962306a36Sopenharmony_ci 458062306a36Sopenharmony_ci rc = crypt_message(server, 1, &rqst, 0); 458162306a36Sopenharmony_ci cifs_dbg(FYI, "Decrypt message returned %d\n", rc); 458262306a36Sopenharmony_ci 458362306a36Sopenharmony_ci if (rc) 458462306a36Sopenharmony_ci return rc; 458562306a36Sopenharmony_ci 458662306a36Sopenharmony_ci memmove(buf, iov[1].iov_base, buf_data_size); 458762306a36Sopenharmony_ci 458862306a36Sopenharmony_ci if (!is_offloaded) 458962306a36Sopenharmony_ci server->total_read = buf_data_size + iter_size; 459062306a36Sopenharmony_ci 459162306a36Sopenharmony_ci return rc; 459262306a36Sopenharmony_ci} 459362306a36Sopenharmony_ci 459462306a36Sopenharmony_cistatic int 459562306a36Sopenharmony_cicifs_copy_pages_to_iter(struct xarray *pages, unsigned int data_size, 459662306a36Sopenharmony_ci unsigned int skip, struct iov_iter *iter) 459762306a36Sopenharmony_ci{ 459862306a36Sopenharmony_ci struct page *page; 459962306a36Sopenharmony_ci unsigned long index; 460062306a36Sopenharmony_ci 460162306a36Sopenharmony_ci xa_for_each(pages, index, page) { 460262306a36Sopenharmony_ci size_t n, len = min_t(unsigned int, PAGE_SIZE - skip, data_size); 460362306a36Sopenharmony_ci 460462306a36Sopenharmony_ci n = copy_page_to_iter(page, skip, len, iter); 460562306a36Sopenharmony_ci if (n != len) { 460662306a36Sopenharmony_ci cifs_dbg(VFS, "%s: something went wrong\n", __func__); 460762306a36Sopenharmony_ci return -EIO; 460862306a36Sopenharmony_ci } 460962306a36Sopenharmony_ci data_size -= n; 461062306a36Sopenharmony_ci skip = 0; 461162306a36Sopenharmony_ci } 461262306a36Sopenharmony_ci 461362306a36Sopenharmony_ci return 0; 461462306a36Sopenharmony_ci} 461562306a36Sopenharmony_ci 461662306a36Sopenharmony_cistatic int 461762306a36Sopenharmony_cihandle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid, 461862306a36Sopenharmony_ci char *buf, unsigned int buf_len, struct xarray *pages, 461962306a36Sopenharmony_ci unsigned int pages_len, bool is_offloaded) 462062306a36Sopenharmony_ci{ 462162306a36Sopenharmony_ci unsigned int data_offset; 462262306a36Sopenharmony_ci unsigned int data_len; 462362306a36Sopenharmony_ci unsigned int cur_off; 462462306a36Sopenharmony_ci unsigned int cur_page_idx; 462562306a36Sopenharmony_ci unsigned int pad_len; 462662306a36Sopenharmony_ci struct cifs_readdata *rdata = mid->callback_data; 462762306a36Sopenharmony_ci struct smb2_hdr *shdr = (struct smb2_hdr *)buf; 462862306a36Sopenharmony_ci int length; 462962306a36Sopenharmony_ci bool use_rdma_mr = false; 463062306a36Sopenharmony_ci 463162306a36Sopenharmony_ci if (shdr->Command != SMB2_READ) { 463262306a36Sopenharmony_ci cifs_server_dbg(VFS, "only big read responses are supported\n"); 463362306a36Sopenharmony_ci return -EOPNOTSUPP; 463462306a36Sopenharmony_ci } 463562306a36Sopenharmony_ci 463662306a36Sopenharmony_ci if (server->ops->is_session_expired && 463762306a36Sopenharmony_ci server->ops->is_session_expired(buf)) { 463862306a36Sopenharmony_ci if (!is_offloaded) 463962306a36Sopenharmony_ci cifs_reconnect(server, true); 464062306a36Sopenharmony_ci return -1; 464162306a36Sopenharmony_ci } 464262306a36Sopenharmony_ci 464362306a36Sopenharmony_ci if (server->ops->is_status_pending && 464462306a36Sopenharmony_ci server->ops->is_status_pending(buf, server)) 464562306a36Sopenharmony_ci return -1; 464662306a36Sopenharmony_ci 464762306a36Sopenharmony_ci /* set up first two iov to get credits */ 464862306a36Sopenharmony_ci rdata->iov[0].iov_base = buf; 464962306a36Sopenharmony_ci rdata->iov[0].iov_len = 0; 465062306a36Sopenharmony_ci rdata->iov[1].iov_base = buf; 465162306a36Sopenharmony_ci rdata->iov[1].iov_len = 465262306a36Sopenharmony_ci min_t(unsigned int, buf_len, server->vals->read_rsp_size); 465362306a36Sopenharmony_ci cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n", 465462306a36Sopenharmony_ci rdata->iov[0].iov_base, rdata->iov[0].iov_len); 465562306a36Sopenharmony_ci cifs_dbg(FYI, "1: iov_base=%p iov_len=%zu\n", 465662306a36Sopenharmony_ci rdata->iov[1].iov_base, rdata->iov[1].iov_len); 465762306a36Sopenharmony_ci 465862306a36Sopenharmony_ci rdata->result = server->ops->map_error(buf, true); 465962306a36Sopenharmony_ci if (rdata->result != 0) { 466062306a36Sopenharmony_ci cifs_dbg(FYI, "%s: server returned error %d\n", 466162306a36Sopenharmony_ci __func__, rdata->result); 466262306a36Sopenharmony_ci /* normal error on read response */ 466362306a36Sopenharmony_ci if (is_offloaded) 466462306a36Sopenharmony_ci mid->mid_state = MID_RESPONSE_RECEIVED; 466562306a36Sopenharmony_ci else 466662306a36Sopenharmony_ci dequeue_mid(mid, false); 466762306a36Sopenharmony_ci return 0; 466862306a36Sopenharmony_ci } 466962306a36Sopenharmony_ci 467062306a36Sopenharmony_ci data_offset = server->ops->read_data_offset(buf); 467162306a36Sopenharmony_ci#ifdef CONFIG_CIFS_SMB_DIRECT 467262306a36Sopenharmony_ci use_rdma_mr = rdata->mr; 467362306a36Sopenharmony_ci#endif 467462306a36Sopenharmony_ci data_len = server->ops->read_data_length(buf, use_rdma_mr); 467562306a36Sopenharmony_ci 467662306a36Sopenharmony_ci if (data_offset < server->vals->read_rsp_size) { 467762306a36Sopenharmony_ci /* 467862306a36Sopenharmony_ci * win2k8 sometimes sends an offset of 0 when the read 467962306a36Sopenharmony_ci * is beyond the EOF. Treat it as if the data starts just after 468062306a36Sopenharmony_ci * the header. 468162306a36Sopenharmony_ci */ 468262306a36Sopenharmony_ci cifs_dbg(FYI, "%s: data offset (%u) inside read response header\n", 468362306a36Sopenharmony_ci __func__, data_offset); 468462306a36Sopenharmony_ci data_offset = server->vals->read_rsp_size; 468562306a36Sopenharmony_ci } else if (data_offset > MAX_CIFS_SMALL_BUFFER_SIZE) { 468662306a36Sopenharmony_ci /* data_offset is beyond the end of smallbuf */ 468762306a36Sopenharmony_ci cifs_dbg(FYI, "%s: data offset (%u) beyond end of smallbuf\n", 468862306a36Sopenharmony_ci __func__, data_offset); 468962306a36Sopenharmony_ci rdata->result = -EIO; 469062306a36Sopenharmony_ci if (is_offloaded) 469162306a36Sopenharmony_ci mid->mid_state = MID_RESPONSE_MALFORMED; 469262306a36Sopenharmony_ci else 469362306a36Sopenharmony_ci dequeue_mid(mid, rdata->result); 469462306a36Sopenharmony_ci return 0; 469562306a36Sopenharmony_ci } 469662306a36Sopenharmony_ci 469762306a36Sopenharmony_ci pad_len = data_offset - server->vals->read_rsp_size; 469862306a36Sopenharmony_ci 469962306a36Sopenharmony_ci if (buf_len <= data_offset) { 470062306a36Sopenharmony_ci /* read response payload is in pages */ 470162306a36Sopenharmony_ci cur_page_idx = pad_len / PAGE_SIZE; 470262306a36Sopenharmony_ci cur_off = pad_len % PAGE_SIZE; 470362306a36Sopenharmony_ci 470462306a36Sopenharmony_ci if (cur_page_idx != 0) { 470562306a36Sopenharmony_ci /* data offset is beyond the 1st page of response */ 470662306a36Sopenharmony_ci cifs_dbg(FYI, "%s: data offset (%u) beyond 1st page of response\n", 470762306a36Sopenharmony_ci __func__, data_offset); 470862306a36Sopenharmony_ci rdata->result = -EIO; 470962306a36Sopenharmony_ci if (is_offloaded) 471062306a36Sopenharmony_ci mid->mid_state = MID_RESPONSE_MALFORMED; 471162306a36Sopenharmony_ci else 471262306a36Sopenharmony_ci dequeue_mid(mid, rdata->result); 471362306a36Sopenharmony_ci return 0; 471462306a36Sopenharmony_ci } 471562306a36Sopenharmony_ci 471662306a36Sopenharmony_ci if (data_len > pages_len - pad_len) { 471762306a36Sopenharmony_ci /* data_len is corrupt -- discard frame */ 471862306a36Sopenharmony_ci rdata->result = -EIO; 471962306a36Sopenharmony_ci if (is_offloaded) 472062306a36Sopenharmony_ci mid->mid_state = MID_RESPONSE_MALFORMED; 472162306a36Sopenharmony_ci else 472262306a36Sopenharmony_ci dequeue_mid(mid, rdata->result); 472362306a36Sopenharmony_ci return 0; 472462306a36Sopenharmony_ci } 472562306a36Sopenharmony_ci 472662306a36Sopenharmony_ci /* Copy the data to the output I/O iterator. */ 472762306a36Sopenharmony_ci rdata->result = cifs_copy_pages_to_iter(pages, pages_len, 472862306a36Sopenharmony_ci cur_off, &rdata->iter); 472962306a36Sopenharmony_ci if (rdata->result != 0) { 473062306a36Sopenharmony_ci if (is_offloaded) 473162306a36Sopenharmony_ci mid->mid_state = MID_RESPONSE_MALFORMED; 473262306a36Sopenharmony_ci else 473362306a36Sopenharmony_ci dequeue_mid(mid, rdata->result); 473462306a36Sopenharmony_ci return 0; 473562306a36Sopenharmony_ci } 473662306a36Sopenharmony_ci rdata->got_bytes = pages_len; 473762306a36Sopenharmony_ci 473862306a36Sopenharmony_ci } else if (buf_len >= data_offset + data_len) { 473962306a36Sopenharmony_ci /* read response payload is in buf */ 474062306a36Sopenharmony_ci WARN_ONCE(pages && !xa_empty(pages), 474162306a36Sopenharmony_ci "read data can be either in buf or in pages"); 474262306a36Sopenharmony_ci length = copy_to_iter(buf + data_offset, data_len, &rdata->iter); 474362306a36Sopenharmony_ci if (length < 0) 474462306a36Sopenharmony_ci return length; 474562306a36Sopenharmony_ci rdata->got_bytes = data_len; 474662306a36Sopenharmony_ci } else { 474762306a36Sopenharmony_ci /* read response payload cannot be in both buf and pages */ 474862306a36Sopenharmony_ci WARN_ONCE(1, "buf can not contain only a part of read data"); 474962306a36Sopenharmony_ci rdata->result = -EIO; 475062306a36Sopenharmony_ci if (is_offloaded) 475162306a36Sopenharmony_ci mid->mid_state = MID_RESPONSE_MALFORMED; 475262306a36Sopenharmony_ci else 475362306a36Sopenharmony_ci dequeue_mid(mid, rdata->result); 475462306a36Sopenharmony_ci return 0; 475562306a36Sopenharmony_ci } 475662306a36Sopenharmony_ci 475762306a36Sopenharmony_ci if (is_offloaded) 475862306a36Sopenharmony_ci mid->mid_state = MID_RESPONSE_RECEIVED; 475962306a36Sopenharmony_ci else 476062306a36Sopenharmony_ci dequeue_mid(mid, false); 476162306a36Sopenharmony_ci return 0; 476262306a36Sopenharmony_ci} 476362306a36Sopenharmony_ci 476462306a36Sopenharmony_cistruct smb2_decrypt_work { 476562306a36Sopenharmony_ci struct work_struct decrypt; 476662306a36Sopenharmony_ci struct TCP_Server_Info *server; 476762306a36Sopenharmony_ci struct xarray buffer; 476862306a36Sopenharmony_ci char *buf; 476962306a36Sopenharmony_ci unsigned int len; 477062306a36Sopenharmony_ci}; 477162306a36Sopenharmony_ci 477262306a36Sopenharmony_ci 477362306a36Sopenharmony_cistatic void smb2_decrypt_offload(struct work_struct *work) 477462306a36Sopenharmony_ci{ 477562306a36Sopenharmony_ci struct smb2_decrypt_work *dw = container_of(work, 477662306a36Sopenharmony_ci struct smb2_decrypt_work, decrypt); 477762306a36Sopenharmony_ci int rc; 477862306a36Sopenharmony_ci struct mid_q_entry *mid; 477962306a36Sopenharmony_ci struct iov_iter iter; 478062306a36Sopenharmony_ci 478162306a36Sopenharmony_ci iov_iter_xarray(&iter, ITER_DEST, &dw->buffer, 0, dw->len); 478262306a36Sopenharmony_ci rc = decrypt_raw_data(dw->server, dw->buf, dw->server->vals->read_rsp_size, 478362306a36Sopenharmony_ci &iter, true); 478462306a36Sopenharmony_ci if (rc) { 478562306a36Sopenharmony_ci cifs_dbg(VFS, "error decrypting rc=%d\n", rc); 478662306a36Sopenharmony_ci goto free_pages; 478762306a36Sopenharmony_ci } 478862306a36Sopenharmony_ci 478962306a36Sopenharmony_ci dw->server->lstrp = jiffies; 479062306a36Sopenharmony_ci mid = smb2_find_dequeue_mid(dw->server, dw->buf); 479162306a36Sopenharmony_ci if (mid == NULL) 479262306a36Sopenharmony_ci cifs_dbg(FYI, "mid not found\n"); 479362306a36Sopenharmony_ci else { 479462306a36Sopenharmony_ci mid->decrypted = true; 479562306a36Sopenharmony_ci rc = handle_read_data(dw->server, mid, dw->buf, 479662306a36Sopenharmony_ci dw->server->vals->read_rsp_size, 479762306a36Sopenharmony_ci &dw->buffer, dw->len, 479862306a36Sopenharmony_ci true); 479962306a36Sopenharmony_ci if (rc >= 0) { 480062306a36Sopenharmony_ci#ifdef CONFIG_CIFS_STATS2 480162306a36Sopenharmony_ci mid->when_received = jiffies; 480262306a36Sopenharmony_ci#endif 480362306a36Sopenharmony_ci if (dw->server->ops->is_network_name_deleted) 480462306a36Sopenharmony_ci dw->server->ops->is_network_name_deleted(dw->buf, 480562306a36Sopenharmony_ci dw->server); 480662306a36Sopenharmony_ci 480762306a36Sopenharmony_ci mid->callback(mid); 480862306a36Sopenharmony_ci } else { 480962306a36Sopenharmony_ci spin_lock(&dw->server->srv_lock); 481062306a36Sopenharmony_ci if (dw->server->tcpStatus == CifsNeedReconnect) { 481162306a36Sopenharmony_ci spin_lock(&dw->server->mid_lock); 481262306a36Sopenharmony_ci mid->mid_state = MID_RETRY_NEEDED; 481362306a36Sopenharmony_ci spin_unlock(&dw->server->mid_lock); 481462306a36Sopenharmony_ci spin_unlock(&dw->server->srv_lock); 481562306a36Sopenharmony_ci mid->callback(mid); 481662306a36Sopenharmony_ci } else { 481762306a36Sopenharmony_ci spin_lock(&dw->server->mid_lock); 481862306a36Sopenharmony_ci mid->mid_state = MID_REQUEST_SUBMITTED; 481962306a36Sopenharmony_ci mid->mid_flags &= ~(MID_DELETED); 482062306a36Sopenharmony_ci list_add_tail(&mid->qhead, 482162306a36Sopenharmony_ci &dw->server->pending_mid_q); 482262306a36Sopenharmony_ci spin_unlock(&dw->server->mid_lock); 482362306a36Sopenharmony_ci spin_unlock(&dw->server->srv_lock); 482462306a36Sopenharmony_ci } 482562306a36Sopenharmony_ci } 482662306a36Sopenharmony_ci release_mid(mid); 482762306a36Sopenharmony_ci } 482862306a36Sopenharmony_ci 482962306a36Sopenharmony_cifree_pages: 483062306a36Sopenharmony_ci cifs_clear_xarray_buffer(&dw->buffer); 483162306a36Sopenharmony_ci cifs_small_buf_release(dw->buf); 483262306a36Sopenharmony_ci kfree(dw); 483362306a36Sopenharmony_ci} 483462306a36Sopenharmony_ci 483562306a36Sopenharmony_ci 483662306a36Sopenharmony_cistatic int 483762306a36Sopenharmony_cireceive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid, 483862306a36Sopenharmony_ci int *num_mids) 483962306a36Sopenharmony_ci{ 484062306a36Sopenharmony_ci struct page *page; 484162306a36Sopenharmony_ci char *buf = server->smallbuf; 484262306a36Sopenharmony_ci struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)buf; 484362306a36Sopenharmony_ci struct iov_iter iter; 484462306a36Sopenharmony_ci unsigned int len, npages; 484562306a36Sopenharmony_ci unsigned int buflen = server->pdu_size; 484662306a36Sopenharmony_ci int rc; 484762306a36Sopenharmony_ci int i = 0; 484862306a36Sopenharmony_ci struct smb2_decrypt_work *dw; 484962306a36Sopenharmony_ci 485062306a36Sopenharmony_ci dw = kzalloc(sizeof(struct smb2_decrypt_work), GFP_KERNEL); 485162306a36Sopenharmony_ci if (!dw) 485262306a36Sopenharmony_ci return -ENOMEM; 485362306a36Sopenharmony_ci xa_init(&dw->buffer); 485462306a36Sopenharmony_ci INIT_WORK(&dw->decrypt, smb2_decrypt_offload); 485562306a36Sopenharmony_ci dw->server = server; 485662306a36Sopenharmony_ci 485762306a36Sopenharmony_ci *num_mids = 1; 485862306a36Sopenharmony_ci len = min_t(unsigned int, buflen, server->vals->read_rsp_size + 485962306a36Sopenharmony_ci sizeof(struct smb2_transform_hdr)) - HEADER_SIZE(server) + 1; 486062306a36Sopenharmony_ci 486162306a36Sopenharmony_ci rc = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1, len); 486262306a36Sopenharmony_ci if (rc < 0) 486362306a36Sopenharmony_ci goto free_dw; 486462306a36Sopenharmony_ci server->total_read += rc; 486562306a36Sopenharmony_ci 486662306a36Sopenharmony_ci len = le32_to_cpu(tr_hdr->OriginalMessageSize) - 486762306a36Sopenharmony_ci server->vals->read_rsp_size; 486862306a36Sopenharmony_ci dw->len = len; 486962306a36Sopenharmony_ci npages = DIV_ROUND_UP(len, PAGE_SIZE); 487062306a36Sopenharmony_ci 487162306a36Sopenharmony_ci rc = -ENOMEM; 487262306a36Sopenharmony_ci for (; i < npages; i++) { 487362306a36Sopenharmony_ci void *old; 487462306a36Sopenharmony_ci 487562306a36Sopenharmony_ci page = alloc_page(GFP_KERNEL|__GFP_HIGHMEM); 487662306a36Sopenharmony_ci if (!page) 487762306a36Sopenharmony_ci goto discard_data; 487862306a36Sopenharmony_ci page->index = i; 487962306a36Sopenharmony_ci old = xa_store(&dw->buffer, i, page, GFP_KERNEL); 488062306a36Sopenharmony_ci if (xa_is_err(old)) { 488162306a36Sopenharmony_ci rc = xa_err(old); 488262306a36Sopenharmony_ci put_page(page); 488362306a36Sopenharmony_ci goto discard_data; 488462306a36Sopenharmony_ci } 488562306a36Sopenharmony_ci xa_set_mark(&dw->buffer, i, XA_MARK_0); 488662306a36Sopenharmony_ci } 488762306a36Sopenharmony_ci 488862306a36Sopenharmony_ci iov_iter_xarray(&iter, ITER_DEST, &dw->buffer, 0, npages * PAGE_SIZE); 488962306a36Sopenharmony_ci 489062306a36Sopenharmony_ci /* Read the data into the buffer and clear excess bufferage. */ 489162306a36Sopenharmony_ci rc = cifs_read_iter_from_socket(server, &iter, dw->len); 489262306a36Sopenharmony_ci if (rc < 0) 489362306a36Sopenharmony_ci goto discard_data; 489462306a36Sopenharmony_ci 489562306a36Sopenharmony_ci server->total_read += rc; 489662306a36Sopenharmony_ci if (rc < npages * PAGE_SIZE) 489762306a36Sopenharmony_ci iov_iter_zero(npages * PAGE_SIZE - rc, &iter); 489862306a36Sopenharmony_ci iov_iter_revert(&iter, npages * PAGE_SIZE); 489962306a36Sopenharmony_ci iov_iter_truncate(&iter, dw->len); 490062306a36Sopenharmony_ci 490162306a36Sopenharmony_ci rc = cifs_discard_remaining_data(server); 490262306a36Sopenharmony_ci if (rc) 490362306a36Sopenharmony_ci goto free_pages; 490462306a36Sopenharmony_ci 490562306a36Sopenharmony_ci /* 490662306a36Sopenharmony_ci * For large reads, offload to different thread for better performance, 490762306a36Sopenharmony_ci * use more cores decrypting which can be expensive 490862306a36Sopenharmony_ci */ 490962306a36Sopenharmony_ci 491062306a36Sopenharmony_ci if ((server->min_offload) && (server->in_flight > 1) && 491162306a36Sopenharmony_ci (server->pdu_size >= server->min_offload)) { 491262306a36Sopenharmony_ci dw->buf = server->smallbuf; 491362306a36Sopenharmony_ci server->smallbuf = (char *)cifs_small_buf_get(); 491462306a36Sopenharmony_ci 491562306a36Sopenharmony_ci queue_work(decrypt_wq, &dw->decrypt); 491662306a36Sopenharmony_ci *num_mids = 0; /* worker thread takes care of finding mid */ 491762306a36Sopenharmony_ci return -1; 491862306a36Sopenharmony_ci } 491962306a36Sopenharmony_ci 492062306a36Sopenharmony_ci rc = decrypt_raw_data(server, buf, server->vals->read_rsp_size, 492162306a36Sopenharmony_ci &iter, false); 492262306a36Sopenharmony_ci if (rc) 492362306a36Sopenharmony_ci goto free_pages; 492462306a36Sopenharmony_ci 492562306a36Sopenharmony_ci *mid = smb2_find_mid(server, buf); 492662306a36Sopenharmony_ci if (*mid == NULL) { 492762306a36Sopenharmony_ci cifs_dbg(FYI, "mid not found\n"); 492862306a36Sopenharmony_ci } else { 492962306a36Sopenharmony_ci cifs_dbg(FYI, "mid found\n"); 493062306a36Sopenharmony_ci (*mid)->decrypted = true; 493162306a36Sopenharmony_ci rc = handle_read_data(server, *mid, buf, 493262306a36Sopenharmony_ci server->vals->read_rsp_size, 493362306a36Sopenharmony_ci &dw->buffer, dw->len, false); 493462306a36Sopenharmony_ci if (rc >= 0) { 493562306a36Sopenharmony_ci if (server->ops->is_network_name_deleted) { 493662306a36Sopenharmony_ci server->ops->is_network_name_deleted(buf, 493762306a36Sopenharmony_ci server); 493862306a36Sopenharmony_ci } 493962306a36Sopenharmony_ci } 494062306a36Sopenharmony_ci } 494162306a36Sopenharmony_ci 494262306a36Sopenharmony_cifree_pages: 494362306a36Sopenharmony_ci cifs_clear_xarray_buffer(&dw->buffer); 494462306a36Sopenharmony_cifree_dw: 494562306a36Sopenharmony_ci kfree(dw); 494662306a36Sopenharmony_ci return rc; 494762306a36Sopenharmony_cidiscard_data: 494862306a36Sopenharmony_ci cifs_discard_remaining_data(server); 494962306a36Sopenharmony_ci goto free_pages; 495062306a36Sopenharmony_ci} 495162306a36Sopenharmony_ci 495262306a36Sopenharmony_cistatic int 495362306a36Sopenharmony_cireceive_encrypted_standard(struct TCP_Server_Info *server, 495462306a36Sopenharmony_ci struct mid_q_entry **mids, char **bufs, 495562306a36Sopenharmony_ci int *num_mids) 495662306a36Sopenharmony_ci{ 495762306a36Sopenharmony_ci int ret, length; 495862306a36Sopenharmony_ci char *buf = server->smallbuf; 495962306a36Sopenharmony_ci struct smb2_hdr *shdr; 496062306a36Sopenharmony_ci unsigned int pdu_length = server->pdu_size; 496162306a36Sopenharmony_ci unsigned int buf_size; 496262306a36Sopenharmony_ci unsigned int next_cmd; 496362306a36Sopenharmony_ci struct mid_q_entry *mid_entry; 496462306a36Sopenharmony_ci int next_is_large; 496562306a36Sopenharmony_ci char *next_buffer = NULL; 496662306a36Sopenharmony_ci 496762306a36Sopenharmony_ci *num_mids = 0; 496862306a36Sopenharmony_ci 496962306a36Sopenharmony_ci /* switch to large buffer if too big for a small one */ 497062306a36Sopenharmony_ci if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE) { 497162306a36Sopenharmony_ci server->large_buf = true; 497262306a36Sopenharmony_ci memcpy(server->bigbuf, buf, server->total_read); 497362306a36Sopenharmony_ci buf = server->bigbuf; 497462306a36Sopenharmony_ci } 497562306a36Sopenharmony_ci 497662306a36Sopenharmony_ci /* now read the rest */ 497762306a36Sopenharmony_ci length = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1, 497862306a36Sopenharmony_ci pdu_length - HEADER_SIZE(server) + 1); 497962306a36Sopenharmony_ci if (length < 0) 498062306a36Sopenharmony_ci return length; 498162306a36Sopenharmony_ci server->total_read += length; 498262306a36Sopenharmony_ci 498362306a36Sopenharmony_ci buf_size = pdu_length - sizeof(struct smb2_transform_hdr); 498462306a36Sopenharmony_ci length = decrypt_raw_data(server, buf, buf_size, NULL, false); 498562306a36Sopenharmony_ci if (length) 498662306a36Sopenharmony_ci return length; 498762306a36Sopenharmony_ci 498862306a36Sopenharmony_ci next_is_large = server->large_buf; 498962306a36Sopenharmony_cione_more: 499062306a36Sopenharmony_ci shdr = (struct smb2_hdr *)buf; 499162306a36Sopenharmony_ci next_cmd = le32_to_cpu(shdr->NextCommand); 499262306a36Sopenharmony_ci if (next_cmd) { 499362306a36Sopenharmony_ci if (WARN_ON_ONCE(next_cmd > pdu_length)) 499462306a36Sopenharmony_ci return -1; 499562306a36Sopenharmony_ci if (next_is_large) 499662306a36Sopenharmony_ci next_buffer = (char *)cifs_buf_get(); 499762306a36Sopenharmony_ci else 499862306a36Sopenharmony_ci next_buffer = (char *)cifs_small_buf_get(); 499962306a36Sopenharmony_ci memcpy(next_buffer, buf + next_cmd, pdu_length - next_cmd); 500062306a36Sopenharmony_ci } 500162306a36Sopenharmony_ci 500262306a36Sopenharmony_ci mid_entry = smb2_find_mid(server, buf); 500362306a36Sopenharmony_ci if (mid_entry == NULL) 500462306a36Sopenharmony_ci cifs_dbg(FYI, "mid not found\n"); 500562306a36Sopenharmony_ci else { 500662306a36Sopenharmony_ci cifs_dbg(FYI, "mid found\n"); 500762306a36Sopenharmony_ci mid_entry->decrypted = true; 500862306a36Sopenharmony_ci mid_entry->resp_buf_size = server->pdu_size; 500962306a36Sopenharmony_ci } 501062306a36Sopenharmony_ci 501162306a36Sopenharmony_ci if (*num_mids >= MAX_COMPOUND) { 501262306a36Sopenharmony_ci cifs_server_dbg(VFS, "too many PDUs in compound\n"); 501362306a36Sopenharmony_ci return -1; 501462306a36Sopenharmony_ci } 501562306a36Sopenharmony_ci bufs[*num_mids] = buf; 501662306a36Sopenharmony_ci mids[(*num_mids)++] = mid_entry; 501762306a36Sopenharmony_ci 501862306a36Sopenharmony_ci if (mid_entry && mid_entry->handle) 501962306a36Sopenharmony_ci ret = mid_entry->handle(server, mid_entry); 502062306a36Sopenharmony_ci else 502162306a36Sopenharmony_ci ret = cifs_handle_standard(server, mid_entry); 502262306a36Sopenharmony_ci 502362306a36Sopenharmony_ci if (ret == 0 && next_cmd) { 502462306a36Sopenharmony_ci pdu_length -= next_cmd; 502562306a36Sopenharmony_ci server->large_buf = next_is_large; 502662306a36Sopenharmony_ci if (next_is_large) 502762306a36Sopenharmony_ci server->bigbuf = buf = next_buffer; 502862306a36Sopenharmony_ci else 502962306a36Sopenharmony_ci server->smallbuf = buf = next_buffer; 503062306a36Sopenharmony_ci goto one_more; 503162306a36Sopenharmony_ci } else if (ret != 0) { 503262306a36Sopenharmony_ci /* 503362306a36Sopenharmony_ci * ret != 0 here means that we didn't get to handle_mid() thus 503462306a36Sopenharmony_ci * server->smallbuf and server->bigbuf are still valid. We need 503562306a36Sopenharmony_ci * to free next_buffer because it is not going to be used 503662306a36Sopenharmony_ci * anywhere. 503762306a36Sopenharmony_ci */ 503862306a36Sopenharmony_ci if (next_is_large) 503962306a36Sopenharmony_ci free_rsp_buf(CIFS_LARGE_BUFFER, next_buffer); 504062306a36Sopenharmony_ci else 504162306a36Sopenharmony_ci free_rsp_buf(CIFS_SMALL_BUFFER, next_buffer); 504262306a36Sopenharmony_ci } 504362306a36Sopenharmony_ci 504462306a36Sopenharmony_ci return ret; 504562306a36Sopenharmony_ci} 504662306a36Sopenharmony_ci 504762306a36Sopenharmony_cistatic int 504862306a36Sopenharmony_cismb3_receive_transform(struct TCP_Server_Info *server, 504962306a36Sopenharmony_ci struct mid_q_entry **mids, char **bufs, int *num_mids) 505062306a36Sopenharmony_ci{ 505162306a36Sopenharmony_ci char *buf = server->smallbuf; 505262306a36Sopenharmony_ci unsigned int pdu_length = server->pdu_size; 505362306a36Sopenharmony_ci struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)buf; 505462306a36Sopenharmony_ci unsigned int orig_len = le32_to_cpu(tr_hdr->OriginalMessageSize); 505562306a36Sopenharmony_ci 505662306a36Sopenharmony_ci if (pdu_length < sizeof(struct smb2_transform_hdr) + 505762306a36Sopenharmony_ci sizeof(struct smb2_hdr)) { 505862306a36Sopenharmony_ci cifs_server_dbg(VFS, "Transform message is too small (%u)\n", 505962306a36Sopenharmony_ci pdu_length); 506062306a36Sopenharmony_ci cifs_reconnect(server, true); 506162306a36Sopenharmony_ci return -ECONNABORTED; 506262306a36Sopenharmony_ci } 506362306a36Sopenharmony_ci 506462306a36Sopenharmony_ci if (pdu_length < orig_len + sizeof(struct smb2_transform_hdr)) { 506562306a36Sopenharmony_ci cifs_server_dbg(VFS, "Transform message is broken\n"); 506662306a36Sopenharmony_ci cifs_reconnect(server, true); 506762306a36Sopenharmony_ci return -ECONNABORTED; 506862306a36Sopenharmony_ci } 506962306a36Sopenharmony_ci 507062306a36Sopenharmony_ci /* TODO: add support for compounds containing READ. */ 507162306a36Sopenharmony_ci if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server)) { 507262306a36Sopenharmony_ci return receive_encrypted_read(server, &mids[0], num_mids); 507362306a36Sopenharmony_ci } 507462306a36Sopenharmony_ci 507562306a36Sopenharmony_ci return receive_encrypted_standard(server, mids, bufs, num_mids); 507662306a36Sopenharmony_ci} 507762306a36Sopenharmony_ci 507862306a36Sopenharmony_ciint 507962306a36Sopenharmony_cismb3_handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid) 508062306a36Sopenharmony_ci{ 508162306a36Sopenharmony_ci char *buf = server->large_buf ? server->bigbuf : server->smallbuf; 508262306a36Sopenharmony_ci 508362306a36Sopenharmony_ci return handle_read_data(server, mid, buf, server->pdu_size, 508462306a36Sopenharmony_ci NULL, 0, false); 508562306a36Sopenharmony_ci} 508662306a36Sopenharmony_ci 508762306a36Sopenharmony_cistatic int smb2_next_header(struct TCP_Server_Info *server, char *buf, 508862306a36Sopenharmony_ci unsigned int *noff) 508962306a36Sopenharmony_ci{ 509062306a36Sopenharmony_ci struct smb2_hdr *hdr = (struct smb2_hdr *)buf; 509162306a36Sopenharmony_ci struct smb2_transform_hdr *t_hdr = (struct smb2_transform_hdr *)buf; 509262306a36Sopenharmony_ci 509362306a36Sopenharmony_ci if (hdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) { 509462306a36Sopenharmony_ci *noff = le32_to_cpu(t_hdr->OriginalMessageSize); 509562306a36Sopenharmony_ci if (unlikely(check_add_overflow(*noff, sizeof(*t_hdr), noff))) 509662306a36Sopenharmony_ci return -EINVAL; 509762306a36Sopenharmony_ci } else { 509862306a36Sopenharmony_ci *noff = le32_to_cpu(hdr->NextCommand); 509962306a36Sopenharmony_ci } 510062306a36Sopenharmony_ci if (unlikely(*noff && *noff < MID_HEADER_SIZE(server))) 510162306a36Sopenharmony_ci return -EINVAL; 510262306a36Sopenharmony_ci return 0; 510362306a36Sopenharmony_ci} 510462306a36Sopenharmony_ci 510562306a36Sopenharmony_cistatic int 510662306a36Sopenharmony_cismb2_make_node(unsigned int xid, struct inode *inode, 510762306a36Sopenharmony_ci struct dentry *dentry, struct cifs_tcon *tcon, 510862306a36Sopenharmony_ci const char *full_path, umode_t mode, dev_t dev) 510962306a36Sopenharmony_ci{ 511062306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); 511162306a36Sopenharmony_ci int rc = -EPERM; 511262306a36Sopenharmony_ci struct cifs_open_info_data buf = {}; 511362306a36Sopenharmony_ci struct cifs_io_parms io_parms = {0}; 511462306a36Sopenharmony_ci __u32 oplock = 0; 511562306a36Sopenharmony_ci struct cifs_fid fid; 511662306a36Sopenharmony_ci struct cifs_open_parms oparms; 511762306a36Sopenharmony_ci unsigned int bytes_written; 511862306a36Sopenharmony_ci struct win_dev *pdev; 511962306a36Sopenharmony_ci struct kvec iov[2]; 512062306a36Sopenharmony_ci 512162306a36Sopenharmony_ci /* 512262306a36Sopenharmony_ci * Check if mounted with mount parm 'sfu' mount parm. 512362306a36Sopenharmony_ci * SFU emulation should work with all servers, but only 512462306a36Sopenharmony_ci * supports block and char device (no socket & fifo), 512562306a36Sopenharmony_ci * and was used by default in earlier versions of Windows 512662306a36Sopenharmony_ci */ 512762306a36Sopenharmony_ci if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)) 512862306a36Sopenharmony_ci return rc; 512962306a36Sopenharmony_ci 513062306a36Sopenharmony_ci /* 513162306a36Sopenharmony_ci * TODO: Add ability to create instead via reparse point. Windows (e.g. 513262306a36Sopenharmony_ci * their current NFS server) uses this approach to expose special files 513362306a36Sopenharmony_ci * over SMB2/SMB3 and Samba will do this with SMB3.1.1 POSIX Extensions 513462306a36Sopenharmony_ci */ 513562306a36Sopenharmony_ci 513662306a36Sopenharmony_ci if (!S_ISCHR(mode) && !S_ISBLK(mode) && !S_ISFIFO(mode)) 513762306a36Sopenharmony_ci return rc; 513862306a36Sopenharmony_ci 513962306a36Sopenharmony_ci cifs_dbg(FYI, "sfu compat create special file\n"); 514062306a36Sopenharmony_ci 514162306a36Sopenharmony_ci oparms = (struct cifs_open_parms) { 514262306a36Sopenharmony_ci .tcon = tcon, 514362306a36Sopenharmony_ci .cifs_sb = cifs_sb, 514462306a36Sopenharmony_ci .desired_access = GENERIC_WRITE, 514562306a36Sopenharmony_ci .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR | 514662306a36Sopenharmony_ci CREATE_OPTION_SPECIAL), 514762306a36Sopenharmony_ci .disposition = FILE_CREATE, 514862306a36Sopenharmony_ci .path = full_path, 514962306a36Sopenharmony_ci .fid = &fid, 515062306a36Sopenharmony_ci }; 515162306a36Sopenharmony_ci 515262306a36Sopenharmony_ci if (tcon->ses->server->oplocks) 515362306a36Sopenharmony_ci oplock = REQ_OPLOCK; 515462306a36Sopenharmony_ci else 515562306a36Sopenharmony_ci oplock = 0; 515662306a36Sopenharmony_ci rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, &buf); 515762306a36Sopenharmony_ci if (rc) 515862306a36Sopenharmony_ci return rc; 515962306a36Sopenharmony_ci 516062306a36Sopenharmony_ci /* 516162306a36Sopenharmony_ci * BB Do not bother to decode buf since no local inode yet to put 516262306a36Sopenharmony_ci * timestamps in, but we can reuse it safely. 516362306a36Sopenharmony_ci */ 516462306a36Sopenharmony_ci 516562306a36Sopenharmony_ci pdev = (struct win_dev *)&buf.fi; 516662306a36Sopenharmony_ci io_parms.pid = current->tgid; 516762306a36Sopenharmony_ci io_parms.tcon = tcon; 516862306a36Sopenharmony_ci io_parms.offset = 0; 516962306a36Sopenharmony_ci io_parms.length = sizeof(struct win_dev); 517062306a36Sopenharmony_ci iov[1].iov_base = &buf.fi; 517162306a36Sopenharmony_ci iov[1].iov_len = sizeof(struct win_dev); 517262306a36Sopenharmony_ci if (S_ISCHR(mode)) { 517362306a36Sopenharmony_ci memcpy(pdev->type, "IntxCHR", 8); 517462306a36Sopenharmony_ci pdev->major = cpu_to_le64(MAJOR(dev)); 517562306a36Sopenharmony_ci pdev->minor = cpu_to_le64(MINOR(dev)); 517662306a36Sopenharmony_ci rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms, 517762306a36Sopenharmony_ci &bytes_written, iov, 1); 517862306a36Sopenharmony_ci } else if (S_ISBLK(mode)) { 517962306a36Sopenharmony_ci memcpy(pdev->type, "IntxBLK", 8); 518062306a36Sopenharmony_ci pdev->major = cpu_to_le64(MAJOR(dev)); 518162306a36Sopenharmony_ci pdev->minor = cpu_to_le64(MINOR(dev)); 518262306a36Sopenharmony_ci rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms, 518362306a36Sopenharmony_ci &bytes_written, iov, 1); 518462306a36Sopenharmony_ci } else if (S_ISFIFO(mode)) { 518562306a36Sopenharmony_ci memcpy(pdev->type, "LnxFIFO", 8); 518662306a36Sopenharmony_ci pdev->major = 0; 518762306a36Sopenharmony_ci pdev->minor = 0; 518862306a36Sopenharmony_ci rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms, 518962306a36Sopenharmony_ci &bytes_written, iov, 1); 519062306a36Sopenharmony_ci } 519162306a36Sopenharmony_ci tcon->ses->server->ops->close(xid, tcon, &fid); 519262306a36Sopenharmony_ci d_drop(dentry); 519362306a36Sopenharmony_ci 519462306a36Sopenharmony_ci /* FIXME: add code here to set EAs */ 519562306a36Sopenharmony_ci 519662306a36Sopenharmony_ci cifs_free_open_info(&buf); 519762306a36Sopenharmony_ci return rc; 519862306a36Sopenharmony_ci} 519962306a36Sopenharmony_ci 520062306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY 520162306a36Sopenharmony_cistruct smb_version_operations smb20_operations = { 520262306a36Sopenharmony_ci .compare_fids = smb2_compare_fids, 520362306a36Sopenharmony_ci .setup_request = smb2_setup_request, 520462306a36Sopenharmony_ci .setup_async_request = smb2_setup_async_request, 520562306a36Sopenharmony_ci .check_receive = smb2_check_receive, 520662306a36Sopenharmony_ci .add_credits = smb2_add_credits, 520762306a36Sopenharmony_ci .set_credits = smb2_set_credits, 520862306a36Sopenharmony_ci .get_credits_field = smb2_get_credits_field, 520962306a36Sopenharmony_ci .get_credits = smb2_get_credits, 521062306a36Sopenharmony_ci .wait_mtu_credits = cifs_wait_mtu_credits, 521162306a36Sopenharmony_ci .get_next_mid = smb2_get_next_mid, 521262306a36Sopenharmony_ci .revert_current_mid = smb2_revert_current_mid, 521362306a36Sopenharmony_ci .read_data_offset = smb2_read_data_offset, 521462306a36Sopenharmony_ci .read_data_length = smb2_read_data_length, 521562306a36Sopenharmony_ci .map_error = map_smb2_to_linux_error, 521662306a36Sopenharmony_ci .find_mid = smb2_find_mid, 521762306a36Sopenharmony_ci .check_message = smb2_check_message, 521862306a36Sopenharmony_ci .dump_detail = smb2_dump_detail, 521962306a36Sopenharmony_ci .clear_stats = smb2_clear_stats, 522062306a36Sopenharmony_ci .print_stats = smb2_print_stats, 522162306a36Sopenharmony_ci .is_oplock_break = smb2_is_valid_oplock_break, 522262306a36Sopenharmony_ci .handle_cancelled_mid = smb2_handle_cancelled_mid, 522362306a36Sopenharmony_ci .downgrade_oplock = smb2_downgrade_oplock, 522462306a36Sopenharmony_ci .need_neg = smb2_need_neg, 522562306a36Sopenharmony_ci .negotiate = smb2_negotiate, 522662306a36Sopenharmony_ci .negotiate_wsize = smb2_negotiate_wsize, 522762306a36Sopenharmony_ci .negotiate_rsize = smb2_negotiate_rsize, 522862306a36Sopenharmony_ci .sess_setup = SMB2_sess_setup, 522962306a36Sopenharmony_ci .logoff = SMB2_logoff, 523062306a36Sopenharmony_ci .tree_connect = SMB2_tcon, 523162306a36Sopenharmony_ci .tree_disconnect = SMB2_tdis, 523262306a36Sopenharmony_ci .qfs_tcon = smb2_qfs_tcon, 523362306a36Sopenharmony_ci .is_path_accessible = smb2_is_path_accessible, 523462306a36Sopenharmony_ci .can_echo = smb2_can_echo, 523562306a36Sopenharmony_ci .echo = SMB2_echo, 523662306a36Sopenharmony_ci .query_path_info = smb2_query_path_info, 523762306a36Sopenharmony_ci .query_reparse_point = smb2_query_reparse_point, 523862306a36Sopenharmony_ci .get_srv_inum = smb2_get_srv_inum, 523962306a36Sopenharmony_ci .query_file_info = smb2_query_file_info, 524062306a36Sopenharmony_ci .set_path_size = smb2_set_path_size, 524162306a36Sopenharmony_ci .set_file_size = smb2_set_file_size, 524262306a36Sopenharmony_ci .set_file_info = smb2_set_file_info, 524362306a36Sopenharmony_ci .set_compression = smb2_set_compression, 524462306a36Sopenharmony_ci .mkdir = smb2_mkdir, 524562306a36Sopenharmony_ci .mkdir_setinfo = smb2_mkdir_setinfo, 524662306a36Sopenharmony_ci .rmdir = smb2_rmdir, 524762306a36Sopenharmony_ci .unlink = smb2_unlink, 524862306a36Sopenharmony_ci .rename = smb2_rename_path, 524962306a36Sopenharmony_ci .create_hardlink = smb2_create_hardlink, 525062306a36Sopenharmony_ci .parse_reparse_point = smb2_parse_reparse_point, 525162306a36Sopenharmony_ci .query_mf_symlink = smb3_query_mf_symlink, 525262306a36Sopenharmony_ci .create_mf_symlink = smb3_create_mf_symlink, 525362306a36Sopenharmony_ci .open = smb2_open_file, 525462306a36Sopenharmony_ci .set_fid = smb2_set_fid, 525562306a36Sopenharmony_ci .close = smb2_close_file, 525662306a36Sopenharmony_ci .flush = smb2_flush_file, 525762306a36Sopenharmony_ci .async_readv = smb2_async_readv, 525862306a36Sopenharmony_ci .async_writev = smb2_async_writev, 525962306a36Sopenharmony_ci .sync_read = smb2_sync_read, 526062306a36Sopenharmony_ci .sync_write = smb2_sync_write, 526162306a36Sopenharmony_ci .query_dir_first = smb2_query_dir_first, 526262306a36Sopenharmony_ci .query_dir_next = smb2_query_dir_next, 526362306a36Sopenharmony_ci .close_dir = smb2_close_dir, 526462306a36Sopenharmony_ci .calc_smb_size = smb2_calc_size, 526562306a36Sopenharmony_ci .is_status_pending = smb2_is_status_pending, 526662306a36Sopenharmony_ci .is_session_expired = smb2_is_session_expired, 526762306a36Sopenharmony_ci .oplock_response = smb2_oplock_response, 526862306a36Sopenharmony_ci .queryfs = smb2_queryfs, 526962306a36Sopenharmony_ci .mand_lock = smb2_mand_lock, 527062306a36Sopenharmony_ci .mand_unlock_range = smb2_unlock_range, 527162306a36Sopenharmony_ci .push_mand_locks = smb2_push_mandatory_locks, 527262306a36Sopenharmony_ci .get_lease_key = smb2_get_lease_key, 527362306a36Sopenharmony_ci .set_lease_key = smb2_set_lease_key, 527462306a36Sopenharmony_ci .new_lease_key = smb2_new_lease_key, 527562306a36Sopenharmony_ci .calc_signature = smb2_calc_signature, 527662306a36Sopenharmony_ci .is_read_op = smb2_is_read_op, 527762306a36Sopenharmony_ci .set_oplock_level = smb2_set_oplock_level, 527862306a36Sopenharmony_ci .create_lease_buf = smb2_create_lease_buf, 527962306a36Sopenharmony_ci .parse_lease_buf = smb2_parse_lease_buf, 528062306a36Sopenharmony_ci .copychunk_range = smb2_copychunk_range, 528162306a36Sopenharmony_ci .wp_retry_size = smb2_wp_retry_size, 528262306a36Sopenharmony_ci .dir_needs_close = smb2_dir_needs_close, 528362306a36Sopenharmony_ci .get_dfs_refer = smb2_get_dfs_refer, 528462306a36Sopenharmony_ci .select_sectype = smb2_select_sectype, 528562306a36Sopenharmony_ci#ifdef CONFIG_CIFS_XATTR 528662306a36Sopenharmony_ci .query_all_EAs = smb2_query_eas, 528762306a36Sopenharmony_ci .set_EA = smb2_set_ea, 528862306a36Sopenharmony_ci#endif /* CIFS_XATTR */ 528962306a36Sopenharmony_ci .get_acl = get_smb2_acl, 529062306a36Sopenharmony_ci .get_acl_by_fid = get_smb2_acl_by_fid, 529162306a36Sopenharmony_ci .set_acl = set_smb2_acl, 529262306a36Sopenharmony_ci .next_header = smb2_next_header, 529362306a36Sopenharmony_ci .ioctl_query_info = smb2_ioctl_query_info, 529462306a36Sopenharmony_ci .make_node = smb2_make_node, 529562306a36Sopenharmony_ci .fiemap = smb3_fiemap, 529662306a36Sopenharmony_ci .llseek = smb3_llseek, 529762306a36Sopenharmony_ci .is_status_io_timeout = smb2_is_status_io_timeout, 529862306a36Sopenharmony_ci .is_network_name_deleted = smb2_is_network_name_deleted, 529962306a36Sopenharmony_ci}; 530062306a36Sopenharmony_ci#endif /* CIFS_ALLOW_INSECURE_LEGACY */ 530162306a36Sopenharmony_ci 530262306a36Sopenharmony_cistruct smb_version_operations smb21_operations = { 530362306a36Sopenharmony_ci .compare_fids = smb2_compare_fids, 530462306a36Sopenharmony_ci .setup_request = smb2_setup_request, 530562306a36Sopenharmony_ci .setup_async_request = smb2_setup_async_request, 530662306a36Sopenharmony_ci .check_receive = smb2_check_receive, 530762306a36Sopenharmony_ci .add_credits = smb2_add_credits, 530862306a36Sopenharmony_ci .set_credits = smb2_set_credits, 530962306a36Sopenharmony_ci .get_credits_field = smb2_get_credits_field, 531062306a36Sopenharmony_ci .get_credits = smb2_get_credits, 531162306a36Sopenharmony_ci .wait_mtu_credits = smb2_wait_mtu_credits, 531262306a36Sopenharmony_ci .adjust_credits = smb2_adjust_credits, 531362306a36Sopenharmony_ci .get_next_mid = smb2_get_next_mid, 531462306a36Sopenharmony_ci .revert_current_mid = smb2_revert_current_mid, 531562306a36Sopenharmony_ci .read_data_offset = smb2_read_data_offset, 531662306a36Sopenharmony_ci .read_data_length = smb2_read_data_length, 531762306a36Sopenharmony_ci .map_error = map_smb2_to_linux_error, 531862306a36Sopenharmony_ci .find_mid = smb2_find_mid, 531962306a36Sopenharmony_ci .check_message = smb2_check_message, 532062306a36Sopenharmony_ci .dump_detail = smb2_dump_detail, 532162306a36Sopenharmony_ci .clear_stats = smb2_clear_stats, 532262306a36Sopenharmony_ci .print_stats = smb2_print_stats, 532362306a36Sopenharmony_ci .is_oplock_break = smb2_is_valid_oplock_break, 532462306a36Sopenharmony_ci .handle_cancelled_mid = smb2_handle_cancelled_mid, 532562306a36Sopenharmony_ci .downgrade_oplock = smb2_downgrade_oplock, 532662306a36Sopenharmony_ci .need_neg = smb2_need_neg, 532762306a36Sopenharmony_ci .negotiate = smb2_negotiate, 532862306a36Sopenharmony_ci .negotiate_wsize = smb2_negotiate_wsize, 532962306a36Sopenharmony_ci .negotiate_rsize = smb2_negotiate_rsize, 533062306a36Sopenharmony_ci .sess_setup = SMB2_sess_setup, 533162306a36Sopenharmony_ci .logoff = SMB2_logoff, 533262306a36Sopenharmony_ci .tree_connect = SMB2_tcon, 533362306a36Sopenharmony_ci .tree_disconnect = SMB2_tdis, 533462306a36Sopenharmony_ci .qfs_tcon = smb2_qfs_tcon, 533562306a36Sopenharmony_ci .is_path_accessible = smb2_is_path_accessible, 533662306a36Sopenharmony_ci .can_echo = smb2_can_echo, 533762306a36Sopenharmony_ci .echo = SMB2_echo, 533862306a36Sopenharmony_ci .query_path_info = smb2_query_path_info, 533962306a36Sopenharmony_ci .query_reparse_point = smb2_query_reparse_point, 534062306a36Sopenharmony_ci .get_srv_inum = smb2_get_srv_inum, 534162306a36Sopenharmony_ci .query_file_info = smb2_query_file_info, 534262306a36Sopenharmony_ci .set_path_size = smb2_set_path_size, 534362306a36Sopenharmony_ci .set_file_size = smb2_set_file_size, 534462306a36Sopenharmony_ci .set_file_info = smb2_set_file_info, 534562306a36Sopenharmony_ci .set_compression = smb2_set_compression, 534662306a36Sopenharmony_ci .mkdir = smb2_mkdir, 534762306a36Sopenharmony_ci .mkdir_setinfo = smb2_mkdir_setinfo, 534862306a36Sopenharmony_ci .rmdir = smb2_rmdir, 534962306a36Sopenharmony_ci .unlink = smb2_unlink, 535062306a36Sopenharmony_ci .rename = smb2_rename_path, 535162306a36Sopenharmony_ci .create_hardlink = smb2_create_hardlink, 535262306a36Sopenharmony_ci .parse_reparse_point = smb2_parse_reparse_point, 535362306a36Sopenharmony_ci .query_mf_symlink = smb3_query_mf_symlink, 535462306a36Sopenharmony_ci .create_mf_symlink = smb3_create_mf_symlink, 535562306a36Sopenharmony_ci .open = smb2_open_file, 535662306a36Sopenharmony_ci .set_fid = smb2_set_fid, 535762306a36Sopenharmony_ci .close = smb2_close_file, 535862306a36Sopenharmony_ci .flush = smb2_flush_file, 535962306a36Sopenharmony_ci .async_readv = smb2_async_readv, 536062306a36Sopenharmony_ci .async_writev = smb2_async_writev, 536162306a36Sopenharmony_ci .sync_read = smb2_sync_read, 536262306a36Sopenharmony_ci .sync_write = smb2_sync_write, 536362306a36Sopenharmony_ci .query_dir_first = smb2_query_dir_first, 536462306a36Sopenharmony_ci .query_dir_next = smb2_query_dir_next, 536562306a36Sopenharmony_ci .close_dir = smb2_close_dir, 536662306a36Sopenharmony_ci .calc_smb_size = smb2_calc_size, 536762306a36Sopenharmony_ci .is_status_pending = smb2_is_status_pending, 536862306a36Sopenharmony_ci .is_session_expired = smb2_is_session_expired, 536962306a36Sopenharmony_ci .oplock_response = smb2_oplock_response, 537062306a36Sopenharmony_ci .queryfs = smb2_queryfs, 537162306a36Sopenharmony_ci .mand_lock = smb2_mand_lock, 537262306a36Sopenharmony_ci .mand_unlock_range = smb2_unlock_range, 537362306a36Sopenharmony_ci .push_mand_locks = smb2_push_mandatory_locks, 537462306a36Sopenharmony_ci .get_lease_key = smb2_get_lease_key, 537562306a36Sopenharmony_ci .set_lease_key = smb2_set_lease_key, 537662306a36Sopenharmony_ci .new_lease_key = smb2_new_lease_key, 537762306a36Sopenharmony_ci .calc_signature = smb2_calc_signature, 537862306a36Sopenharmony_ci .is_read_op = smb21_is_read_op, 537962306a36Sopenharmony_ci .set_oplock_level = smb21_set_oplock_level, 538062306a36Sopenharmony_ci .create_lease_buf = smb2_create_lease_buf, 538162306a36Sopenharmony_ci .parse_lease_buf = smb2_parse_lease_buf, 538262306a36Sopenharmony_ci .copychunk_range = smb2_copychunk_range, 538362306a36Sopenharmony_ci .wp_retry_size = smb2_wp_retry_size, 538462306a36Sopenharmony_ci .dir_needs_close = smb2_dir_needs_close, 538562306a36Sopenharmony_ci .enum_snapshots = smb3_enum_snapshots, 538662306a36Sopenharmony_ci .notify = smb3_notify, 538762306a36Sopenharmony_ci .get_dfs_refer = smb2_get_dfs_refer, 538862306a36Sopenharmony_ci .select_sectype = smb2_select_sectype, 538962306a36Sopenharmony_ci#ifdef CONFIG_CIFS_XATTR 539062306a36Sopenharmony_ci .query_all_EAs = smb2_query_eas, 539162306a36Sopenharmony_ci .set_EA = smb2_set_ea, 539262306a36Sopenharmony_ci#endif /* CIFS_XATTR */ 539362306a36Sopenharmony_ci .get_acl = get_smb2_acl, 539462306a36Sopenharmony_ci .get_acl_by_fid = get_smb2_acl_by_fid, 539562306a36Sopenharmony_ci .set_acl = set_smb2_acl, 539662306a36Sopenharmony_ci .next_header = smb2_next_header, 539762306a36Sopenharmony_ci .ioctl_query_info = smb2_ioctl_query_info, 539862306a36Sopenharmony_ci .make_node = smb2_make_node, 539962306a36Sopenharmony_ci .fiemap = smb3_fiemap, 540062306a36Sopenharmony_ci .llseek = smb3_llseek, 540162306a36Sopenharmony_ci .is_status_io_timeout = smb2_is_status_io_timeout, 540262306a36Sopenharmony_ci .is_network_name_deleted = smb2_is_network_name_deleted, 540362306a36Sopenharmony_ci}; 540462306a36Sopenharmony_ci 540562306a36Sopenharmony_cistruct smb_version_operations smb30_operations = { 540662306a36Sopenharmony_ci .compare_fids = smb2_compare_fids, 540762306a36Sopenharmony_ci .setup_request = smb2_setup_request, 540862306a36Sopenharmony_ci .setup_async_request = smb2_setup_async_request, 540962306a36Sopenharmony_ci .check_receive = smb2_check_receive, 541062306a36Sopenharmony_ci .add_credits = smb2_add_credits, 541162306a36Sopenharmony_ci .set_credits = smb2_set_credits, 541262306a36Sopenharmony_ci .get_credits_field = smb2_get_credits_field, 541362306a36Sopenharmony_ci .get_credits = smb2_get_credits, 541462306a36Sopenharmony_ci .wait_mtu_credits = smb2_wait_mtu_credits, 541562306a36Sopenharmony_ci .adjust_credits = smb2_adjust_credits, 541662306a36Sopenharmony_ci .get_next_mid = smb2_get_next_mid, 541762306a36Sopenharmony_ci .revert_current_mid = smb2_revert_current_mid, 541862306a36Sopenharmony_ci .read_data_offset = smb2_read_data_offset, 541962306a36Sopenharmony_ci .read_data_length = smb2_read_data_length, 542062306a36Sopenharmony_ci .map_error = map_smb2_to_linux_error, 542162306a36Sopenharmony_ci .find_mid = smb2_find_mid, 542262306a36Sopenharmony_ci .check_message = smb2_check_message, 542362306a36Sopenharmony_ci .dump_detail = smb2_dump_detail, 542462306a36Sopenharmony_ci .clear_stats = smb2_clear_stats, 542562306a36Sopenharmony_ci .print_stats = smb2_print_stats, 542662306a36Sopenharmony_ci .dump_share_caps = smb2_dump_share_caps, 542762306a36Sopenharmony_ci .is_oplock_break = smb2_is_valid_oplock_break, 542862306a36Sopenharmony_ci .handle_cancelled_mid = smb2_handle_cancelled_mid, 542962306a36Sopenharmony_ci .downgrade_oplock = smb3_downgrade_oplock, 543062306a36Sopenharmony_ci .need_neg = smb2_need_neg, 543162306a36Sopenharmony_ci .negotiate = smb2_negotiate, 543262306a36Sopenharmony_ci .negotiate_wsize = smb3_negotiate_wsize, 543362306a36Sopenharmony_ci .negotiate_rsize = smb3_negotiate_rsize, 543462306a36Sopenharmony_ci .sess_setup = SMB2_sess_setup, 543562306a36Sopenharmony_ci .logoff = SMB2_logoff, 543662306a36Sopenharmony_ci .tree_connect = SMB2_tcon, 543762306a36Sopenharmony_ci .tree_disconnect = SMB2_tdis, 543862306a36Sopenharmony_ci .qfs_tcon = smb3_qfs_tcon, 543962306a36Sopenharmony_ci .is_path_accessible = smb2_is_path_accessible, 544062306a36Sopenharmony_ci .can_echo = smb2_can_echo, 544162306a36Sopenharmony_ci .echo = SMB2_echo, 544262306a36Sopenharmony_ci .query_path_info = smb2_query_path_info, 544362306a36Sopenharmony_ci /* WSL tags introduced long after smb2.1, enable for SMB3, 3.11 only */ 544462306a36Sopenharmony_ci .query_reparse_point = smb2_query_reparse_point, 544562306a36Sopenharmony_ci .get_srv_inum = smb2_get_srv_inum, 544662306a36Sopenharmony_ci .query_file_info = smb2_query_file_info, 544762306a36Sopenharmony_ci .set_path_size = smb2_set_path_size, 544862306a36Sopenharmony_ci .set_file_size = smb2_set_file_size, 544962306a36Sopenharmony_ci .set_file_info = smb2_set_file_info, 545062306a36Sopenharmony_ci .set_compression = smb2_set_compression, 545162306a36Sopenharmony_ci .mkdir = smb2_mkdir, 545262306a36Sopenharmony_ci .mkdir_setinfo = smb2_mkdir_setinfo, 545362306a36Sopenharmony_ci .rmdir = smb2_rmdir, 545462306a36Sopenharmony_ci .unlink = smb2_unlink, 545562306a36Sopenharmony_ci .rename = smb2_rename_path, 545662306a36Sopenharmony_ci .create_hardlink = smb2_create_hardlink, 545762306a36Sopenharmony_ci .parse_reparse_point = smb2_parse_reparse_point, 545862306a36Sopenharmony_ci .query_mf_symlink = smb3_query_mf_symlink, 545962306a36Sopenharmony_ci .create_mf_symlink = smb3_create_mf_symlink, 546062306a36Sopenharmony_ci .open = smb2_open_file, 546162306a36Sopenharmony_ci .set_fid = smb2_set_fid, 546262306a36Sopenharmony_ci .close = smb2_close_file, 546362306a36Sopenharmony_ci .close_getattr = smb2_close_getattr, 546462306a36Sopenharmony_ci .flush = smb2_flush_file, 546562306a36Sopenharmony_ci .async_readv = smb2_async_readv, 546662306a36Sopenharmony_ci .async_writev = smb2_async_writev, 546762306a36Sopenharmony_ci .sync_read = smb2_sync_read, 546862306a36Sopenharmony_ci .sync_write = smb2_sync_write, 546962306a36Sopenharmony_ci .query_dir_first = smb2_query_dir_first, 547062306a36Sopenharmony_ci .query_dir_next = smb2_query_dir_next, 547162306a36Sopenharmony_ci .close_dir = smb2_close_dir, 547262306a36Sopenharmony_ci .calc_smb_size = smb2_calc_size, 547362306a36Sopenharmony_ci .is_status_pending = smb2_is_status_pending, 547462306a36Sopenharmony_ci .is_session_expired = smb2_is_session_expired, 547562306a36Sopenharmony_ci .oplock_response = smb2_oplock_response, 547662306a36Sopenharmony_ci .queryfs = smb2_queryfs, 547762306a36Sopenharmony_ci .mand_lock = smb2_mand_lock, 547862306a36Sopenharmony_ci .mand_unlock_range = smb2_unlock_range, 547962306a36Sopenharmony_ci .push_mand_locks = smb2_push_mandatory_locks, 548062306a36Sopenharmony_ci .get_lease_key = smb2_get_lease_key, 548162306a36Sopenharmony_ci .set_lease_key = smb2_set_lease_key, 548262306a36Sopenharmony_ci .new_lease_key = smb2_new_lease_key, 548362306a36Sopenharmony_ci .generate_signingkey = generate_smb30signingkey, 548462306a36Sopenharmony_ci .calc_signature = smb3_calc_signature, 548562306a36Sopenharmony_ci .set_integrity = smb3_set_integrity, 548662306a36Sopenharmony_ci .is_read_op = smb21_is_read_op, 548762306a36Sopenharmony_ci .set_oplock_level = smb3_set_oplock_level, 548862306a36Sopenharmony_ci .create_lease_buf = smb3_create_lease_buf, 548962306a36Sopenharmony_ci .parse_lease_buf = smb3_parse_lease_buf, 549062306a36Sopenharmony_ci .copychunk_range = smb2_copychunk_range, 549162306a36Sopenharmony_ci .duplicate_extents = smb2_duplicate_extents, 549262306a36Sopenharmony_ci .validate_negotiate = smb3_validate_negotiate, 549362306a36Sopenharmony_ci .wp_retry_size = smb2_wp_retry_size, 549462306a36Sopenharmony_ci .dir_needs_close = smb2_dir_needs_close, 549562306a36Sopenharmony_ci .fallocate = smb3_fallocate, 549662306a36Sopenharmony_ci .enum_snapshots = smb3_enum_snapshots, 549762306a36Sopenharmony_ci .notify = smb3_notify, 549862306a36Sopenharmony_ci .init_transform_rq = smb3_init_transform_rq, 549962306a36Sopenharmony_ci .is_transform_hdr = smb3_is_transform_hdr, 550062306a36Sopenharmony_ci .receive_transform = smb3_receive_transform, 550162306a36Sopenharmony_ci .get_dfs_refer = smb2_get_dfs_refer, 550262306a36Sopenharmony_ci .select_sectype = smb2_select_sectype, 550362306a36Sopenharmony_ci#ifdef CONFIG_CIFS_XATTR 550462306a36Sopenharmony_ci .query_all_EAs = smb2_query_eas, 550562306a36Sopenharmony_ci .set_EA = smb2_set_ea, 550662306a36Sopenharmony_ci#endif /* CIFS_XATTR */ 550762306a36Sopenharmony_ci .get_acl = get_smb2_acl, 550862306a36Sopenharmony_ci .get_acl_by_fid = get_smb2_acl_by_fid, 550962306a36Sopenharmony_ci .set_acl = set_smb2_acl, 551062306a36Sopenharmony_ci .next_header = smb2_next_header, 551162306a36Sopenharmony_ci .ioctl_query_info = smb2_ioctl_query_info, 551262306a36Sopenharmony_ci .make_node = smb2_make_node, 551362306a36Sopenharmony_ci .fiemap = smb3_fiemap, 551462306a36Sopenharmony_ci .llseek = smb3_llseek, 551562306a36Sopenharmony_ci .is_status_io_timeout = smb2_is_status_io_timeout, 551662306a36Sopenharmony_ci .is_network_name_deleted = smb2_is_network_name_deleted, 551762306a36Sopenharmony_ci}; 551862306a36Sopenharmony_ci 551962306a36Sopenharmony_cistruct smb_version_operations smb311_operations = { 552062306a36Sopenharmony_ci .compare_fids = smb2_compare_fids, 552162306a36Sopenharmony_ci .setup_request = smb2_setup_request, 552262306a36Sopenharmony_ci .setup_async_request = smb2_setup_async_request, 552362306a36Sopenharmony_ci .check_receive = smb2_check_receive, 552462306a36Sopenharmony_ci .add_credits = smb2_add_credits, 552562306a36Sopenharmony_ci .set_credits = smb2_set_credits, 552662306a36Sopenharmony_ci .get_credits_field = smb2_get_credits_field, 552762306a36Sopenharmony_ci .get_credits = smb2_get_credits, 552862306a36Sopenharmony_ci .wait_mtu_credits = smb2_wait_mtu_credits, 552962306a36Sopenharmony_ci .adjust_credits = smb2_adjust_credits, 553062306a36Sopenharmony_ci .get_next_mid = smb2_get_next_mid, 553162306a36Sopenharmony_ci .revert_current_mid = smb2_revert_current_mid, 553262306a36Sopenharmony_ci .read_data_offset = smb2_read_data_offset, 553362306a36Sopenharmony_ci .read_data_length = smb2_read_data_length, 553462306a36Sopenharmony_ci .map_error = map_smb2_to_linux_error, 553562306a36Sopenharmony_ci .find_mid = smb2_find_mid, 553662306a36Sopenharmony_ci .check_message = smb2_check_message, 553762306a36Sopenharmony_ci .dump_detail = smb2_dump_detail, 553862306a36Sopenharmony_ci .clear_stats = smb2_clear_stats, 553962306a36Sopenharmony_ci .print_stats = smb2_print_stats, 554062306a36Sopenharmony_ci .dump_share_caps = smb2_dump_share_caps, 554162306a36Sopenharmony_ci .is_oplock_break = smb2_is_valid_oplock_break, 554262306a36Sopenharmony_ci .handle_cancelled_mid = smb2_handle_cancelled_mid, 554362306a36Sopenharmony_ci .downgrade_oplock = smb3_downgrade_oplock, 554462306a36Sopenharmony_ci .need_neg = smb2_need_neg, 554562306a36Sopenharmony_ci .negotiate = smb2_negotiate, 554662306a36Sopenharmony_ci .negotiate_wsize = smb3_negotiate_wsize, 554762306a36Sopenharmony_ci .negotiate_rsize = smb3_negotiate_rsize, 554862306a36Sopenharmony_ci .sess_setup = SMB2_sess_setup, 554962306a36Sopenharmony_ci .logoff = SMB2_logoff, 555062306a36Sopenharmony_ci .tree_connect = SMB2_tcon, 555162306a36Sopenharmony_ci .tree_disconnect = SMB2_tdis, 555262306a36Sopenharmony_ci .qfs_tcon = smb3_qfs_tcon, 555362306a36Sopenharmony_ci .is_path_accessible = smb2_is_path_accessible, 555462306a36Sopenharmony_ci .can_echo = smb2_can_echo, 555562306a36Sopenharmony_ci .echo = SMB2_echo, 555662306a36Sopenharmony_ci .query_path_info = smb2_query_path_info, 555762306a36Sopenharmony_ci .query_reparse_point = smb2_query_reparse_point, 555862306a36Sopenharmony_ci .get_srv_inum = smb2_get_srv_inum, 555962306a36Sopenharmony_ci .query_file_info = smb2_query_file_info, 556062306a36Sopenharmony_ci .set_path_size = smb2_set_path_size, 556162306a36Sopenharmony_ci .set_file_size = smb2_set_file_size, 556262306a36Sopenharmony_ci .set_file_info = smb2_set_file_info, 556362306a36Sopenharmony_ci .set_compression = smb2_set_compression, 556462306a36Sopenharmony_ci .mkdir = smb2_mkdir, 556562306a36Sopenharmony_ci .mkdir_setinfo = smb2_mkdir_setinfo, 556662306a36Sopenharmony_ci .posix_mkdir = smb311_posix_mkdir, 556762306a36Sopenharmony_ci .rmdir = smb2_rmdir, 556862306a36Sopenharmony_ci .unlink = smb2_unlink, 556962306a36Sopenharmony_ci .rename = smb2_rename_path, 557062306a36Sopenharmony_ci .create_hardlink = smb2_create_hardlink, 557162306a36Sopenharmony_ci .parse_reparse_point = smb2_parse_reparse_point, 557262306a36Sopenharmony_ci .query_mf_symlink = smb3_query_mf_symlink, 557362306a36Sopenharmony_ci .create_mf_symlink = smb3_create_mf_symlink, 557462306a36Sopenharmony_ci .open = smb2_open_file, 557562306a36Sopenharmony_ci .set_fid = smb2_set_fid, 557662306a36Sopenharmony_ci .close = smb2_close_file, 557762306a36Sopenharmony_ci .close_getattr = smb2_close_getattr, 557862306a36Sopenharmony_ci .flush = smb2_flush_file, 557962306a36Sopenharmony_ci .async_readv = smb2_async_readv, 558062306a36Sopenharmony_ci .async_writev = smb2_async_writev, 558162306a36Sopenharmony_ci .sync_read = smb2_sync_read, 558262306a36Sopenharmony_ci .sync_write = smb2_sync_write, 558362306a36Sopenharmony_ci .query_dir_first = smb2_query_dir_first, 558462306a36Sopenharmony_ci .query_dir_next = smb2_query_dir_next, 558562306a36Sopenharmony_ci .close_dir = smb2_close_dir, 558662306a36Sopenharmony_ci .calc_smb_size = smb2_calc_size, 558762306a36Sopenharmony_ci .is_status_pending = smb2_is_status_pending, 558862306a36Sopenharmony_ci .is_session_expired = smb2_is_session_expired, 558962306a36Sopenharmony_ci .oplock_response = smb2_oplock_response, 559062306a36Sopenharmony_ci .queryfs = smb311_queryfs, 559162306a36Sopenharmony_ci .mand_lock = smb2_mand_lock, 559262306a36Sopenharmony_ci .mand_unlock_range = smb2_unlock_range, 559362306a36Sopenharmony_ci .push_mand_locks = smb2_push_mandatory_locks, 559462306a36Sopenharmony_ci .get_lease_key = smb2_get_lease_key, 559562306a36Sopenharmony_ci .set_lease_key = smb2_set_lease_key, 559662306a36Sopenharmony_ci .new_lease_key = smb2_new_lease_key, 559762306a36Sopenharmony_ci .generate_signingkey = generate_smb311signingkey, 559862306a36Sopenharmony_ci .calc_signature = smb3_calc_signature, 559962306a36Sopenharmony_ci .set_integrity = smb3_set_integrity, 560062306a36Sopenharmony_ci .is_read_op = smb21_is_read_op, 560162306a36Sopenharmony_ci .set_oplock_level = smb3_set_oplock_level, 560262306a36Sopenharmony_ci .create_lease_buf = smb3_create_lease_buf, 560362306a36Sopenharmony_ci .parse_lease_buf = smb3_parse_lease_buf, 560462306a36Sopenharmony_ci .copychunk_range = smb2_copychunk_range, 560562306a36Sopenharmony_ci .duplicate_extents = smb2_duplicate_extents, 560662306a36Sopenharmony_ci/* .validate_negotiate = smb3_validate_negotiate, */ /* not used in 3.11 */ 560762306a36Sopenharmony_ci .wp_retry_size = smb2_wp_retry_size, 560862306a36Sopenharmony_ci .dir_needs_close = smb2_dir_needs_close, 560962306a36Sopenharmony_ci .fallocate = smb3_fallocate, 561062306a36Sopenharmony_ci .enum_snapshots = smb3_enum_snapshots, 561162306a36Sopenharmony_ci .notify = smb3_notify, 561262306a36Sopenharmony_ci .init_transform_rq = smb3_init_transform_rq, 561362306a36Sopenharmony_ci .is_transform_hdr = smb3_is_transform_hdr, 561462306a36Sopenharmony_ci .receive_transform = smb3_receive_transform, 561562306a36Sopenharmony_ci .get_dfs_refer = smb2_get_dfs_refer, 561662306a36Sopenharmony_ci .select_sectype = smb2_select_sectype, 561762306a36Sopenharmony_ci#ifdef CONFIG_CIFS_XATTR 561862306a36Sopenharmony_ci .query_all_EAs = smb2_query_eas, 561962306a36Sopenharmony_ci .set_EA = smb2_set_ea, 562062306a36Sopenharmony_ci#endif /* CIFS_XATTR */ 562162306a36Sopenharmony_ci .get_acl = get_smb2_acl, 562262306a36Sopenharmony_ci .get_acl_by_fid = get_smb2_acl_by_fid, 562362306a36Sopenharmony_ci .set_acl = set_smb2_acl, 562462306a36Sopenharmony_ci .next_header = smb2_next_header, 562562306a36Sopenharmony_ci .ioctl_query_info = smb2_ioctl_query_info, 562662306a36Sopenharmony_ci .make_node = smb2_make_node, 562762306a36Sopenharmony_ci .fiemap = smb3_fiemap, 562862306a36Sopenharmony_ci .llseek = smb3_llseek, 562962306a36Sopenharmony_ci .is_status_io_timeout = smb2_is_status_io_timeout, 563062306a36Sopenharmony_ci .is_network_name_deleted = smb2_is_network_name_deleted, 563162306a36Sopenharmony_ci}; 563262306a36Sopenharmony_ci 563362306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY 563462306a36Sopenharmony_cistruct smb_version_values smb20_values = { 563562306a36Sopenharmony_ci .version_string = SMB20_VERSION_STRING, 563662306a36Sopenharmony_ci .protocol_id = SMB20_PROT_ID, 563762306a36Sopenharmony_ci .req_capabilities = 0, /* MBZ */ 563862306a36Sopenharmony_ci .large_lock_type = 0, 563962306a36Sopenharmony_ci .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, 564062306a36Sopenharmony_ci .shared_lock_type = SMB2_LOCKFLAG_SHARED, 564162306a36Sopenharmony_ci .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, 564262306a36Sopenharmony_ci .header_size = sizeof(struct smb2_hdr), 564362306a36Sopenharmony_ci .header_preamble_size = 0, 564462306a36Sopenharmony_ci .max_header_size = MAX_SMB2_HDR_SIZE, 564562306a36Sopenharmony_ci .read_rsp_size = sizeof(struct smb2_read_rsp), 564662306a36Sopenharmony_ci .lock_cmd = SMB2_LOCK, 564762306a36Sopenharmony_ci .cap_unix = 0, 564862306a36Sopenharmony_ci .cap_nt_find = SMB2_NT_FIND, 564962306a36Sopenharmony_ci .cap_large_files = SMB2_LARGE_FILES, 565062306a36Sopenharmony_ci .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, 565162306a36Sopenharmony_ci .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, 565262306a36Sopenharmony_ci .create_lease_size = sizeof(struct create_lease), 565362306a36Sopenharmony_ci}; 565462306a36Sopenharmony_ci#endif /* ALLOW_INSECURE_LEGACY */ 565562306a36Sopenharmony_ci 565662306a36Sopenharmony_cistruct smb_version_values smb21_values = { 565762306a36Sopenharmony_ci .version_string = SMB21_VERSION_STRING, 565862306a36Sopenharmony_ci .protocol_id = SMB21_PROT_ID, 565962306a36Sopenharmony_ci .req_capabilities = 0, /* MBZ on negotiate req until SMB3 dialect */ 566062306a36Sopenharmony_ci .large_lock_type = 0, 566162306a36Sopenharmony_ci .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, 566262306a36Sopenharmony_ci .shared_lock_type = SMB2_LOCKFLAG_SHARED, 566362306a36Sopenharmony_ci .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, 566462306a36Sopenharmony_ci .header_size = sizeof(struct smb2_hdr), 566562306a36Sopenharmony_ci .header_preamble_size = 0, 566662306a36Sopenharmony_ci .max_header_size = MAX_SMB2_HDR_SIZE, 566762306a36Sopenharmony_ci .read_rsp_size = sizeof(struct smb2_read_rsp), 566862306a36Sopenharmony_ci .lock_cmd = SMB2_LOCK, 566962306a36Sopenharmony_ci .cap_unix = 0, 567062306a36Sopenharmony_ci .cap_nt_find = SMB2_NT_FIND, 567162306a36Sopenharmony_ci .cap_large_files = SMB2_LARGE_FILES, 567262306a36Sopenharmony_ci .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, 567362306a36Sopenharmony_ci .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, 567462306a36Sopenharmony_ci .create_lease_size = sizeof(struct create_lease), 567562306a36Sopenharmony_ci}; 567662306a36Sopenharmony_ci 567762306a36Sopenharmony_cistruct smb_version_values smb3any_values = { 567862306a36Sopenharmony_ci .version_string = SMB3ANY_VERSION_STRING, 567962306a36Sopenharmony_ci .protocol_id = SMB302_PROT_ID, /* doesn't matter, send protocol array */ 568062306a36Sopenharmony_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, 568162306a36Sopenharmony_ci .large_lock_type = 0, 568262306a36Sopenharmony_ci .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, 568362306a36Sopenharmony_ci .shared_lock_type = SMB2_LOCKFLAG_SHARED, 568462306a36Sopenharmony_ci .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, 568562306a36Sopenharmony_ci .header_size = sizeof(struct smb2_hdr), 568662306a36Sopenharmony_ci .header_preamble_size = 0, 568762306a36Sopenharmony_ci .max_header_size = MAX_SMB2_HDR_SIZE, 568862306a36Sopenharmony_ci .read_rsp_size = sizeof(struct smb2_read_rsp), 568962306a36Sopenharmony_ci .lock_cmd = SMB2_LOCK, 569062306a36Sopenharmony_ci .cap_unix = 0, 569162306a36Sopenharmony_ci .cap_nt_find = SMB2_NT_FIND, 569262306a36Sopenharmony_ci .cap_large_files = SMB2_LARGE_FILES, 569362306a36Sopenharmony_ci .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, 569462306a36Sopenharmony_ci .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, 569562306a36Sopenharmony_ci .create_lease_size = sizeof(struct create_lease_v2), 569662306a36Sopenharmony_ci}; 569762306a36Sopenharmony_ci 569862306a36Sopenharmony_cistruct smb_version_values smbdefault_values = { 569962306a36Sopenharmony_ci .version_string = SMBDEFAULT_VERSION_STRING, 570062306a36Sopenharmony_ci .protocol_id = SMB302_PROT_ID, /* doesn't matter, send protocol array */ 570162306a36Sopenharmony_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, 570262306a36Sopenharmony_ci .large_lock_type = 0, 570362306a36Sopenharmony_ci .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, 570462306a36Sopenharmony_ci .shared_lock_type = SMB2_LOCKFLAG_SHARED, 570562306a36Sopenharmony_ci .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, 570662306a36Sopenharmony_ci .header_size = sizeof(struct smb2_hdr), 570762306a36Sopenharmony_ci .header_preamble_size = 0, 570862306a36Sopenharmony_ci .max_header_size = MAX_SMB2_HDR_SIZE, 570962306a36Sopenharmony_ci .read_rsp_size = sizeof(struct smb2_read_rsp), 571062306a36Sopenharmony_ci .lock_cmd = SMB2_LOCK, 571162306a36Sopenharmony_ci .cap_unix = 0, 571262306a36Sopenharmony_ci .cap_nt_find = SMB2_NT_FIND, 571362306a36Sopenharmony_ci .cap_large_files = SMB2_LARGE_FILES, 571462306a36Sopenharmony_ci .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, 571562306a36Sopenharmony_ci .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, 571662306a36Sopenharmony_ci .create_lease_size = sizeof(struct create_lease_v2), 571762306a36Sopenharmony_ci}; 571862306a36Sopenharmony_ci 571962306a36Sopenharmony_cistruct smb_version_values smb30_values = { 572062306a36Sopenharmony_ci .version_string = SMB30_VERSION_STRING, 572162306a36Sopenharmony_ci .protocol_id = SMB30_PROT_ID, 572262306a36Sopenharmony_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, 572362306a36Sopenharmony_ci .large_lock_type = 0, 572462306a36Sopenharmony_ci .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, 572562306a36Sopenharmony_ci .shared_lock_type = SMB2_LOCKFLAG_SHARED, 572662306a36Sopenharmony_ci .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, 572762306a36Sopenharmony_ci .header_size = sizeof(struct smb2_hdr), 572862306a36Sopenharmony_ci .header_preamble_size = 0, 572962306a36Sopenharmony_ci .max_header_size = MAX_SMB2_HDR_SIZE, 573062306a36Sopenharmony_ci .read_rsp_size = sizeof(struct smb2_read_rsp), 573162306a36Sopenharmony_ci .lock_cmd = SMB2_LOCK, 573262306a36Sopenharmony_ci .cap_unix = 0, 573362306a36Sopenharmony_ci .cap_nt_find = SMB2_NT_FIND, 573462306a36Sopenharmony_ci .cap_large_files = SMB2_LARGE_FILES, 573562306a36Sopenharmony_ci .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, 573662306a36Sopenharmony_ci .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, 573762306a36Sopenharmony_ci .create_lease_size = sizeof(struct create_lease_v2), 573862306a36Sopenharmony_ci}; 573962306a36Sopenharmony_ci 574062306a36Sopenharmony_cistruct smb_version_values smb302_values = { 574162306a36Sopenharmony_ci .version_string = SMB302_VERSION_STRING, 574262306a36Sopenharmony_ci .protocol_id = SMB302_PROT_ID, 574362306a36Sopenharmony_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, 574462306a36Sopenharmony_ci .large_lock_type = 0, 574562306a36Sopenharmony_ci .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, 574662306a36Sopenharmony_ci .shared_lock_type = SMB2_LOCKFLAG_SHARED, 574762306a36Sopenharmony_ci .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, 574862306a36Sopenharmony_ci .header_size = sizeof(struct smb2_hdr), 574962306a36Sopenharmony_ci .header_preamble_size = 0, 575062306a36Sopenharmony_ci .max_header_size = MAX_SMB2_HDR_SIZE, 575162306a36Sopenharmony_ci .read_rsp_size = sizeof(struct smb2_read_rsp), 575262306a36Sopenharmony_ci .lock_cmd = SMB2_LOCK, 575362306a36Sopenharmony_ci .cap_unix = 0, 575462306a36Sopenharmony_ci .cap_nt_find = SMB2_NT_FIND, 575562306a36Sopenharmony_ci .cap_large_files = SMB2_LARGE_FILES, 575662306a36Sopenharmony_ci .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, 575762306a36Sopenharmony_ci .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, 575862306a36Sopenharmony_ci .create_lease_size = sizeof(struct create_lease_v2), 575962306a36Sopenharmony_ci}; 576062306a36Sopenharmony_ci 576162306a36Sopenharmony_cistruct smb_version_values smb311_values = { 576262306a36Sopenharmony_ci .version_string = SMB311_VERSION_STRING, 576362306a36Sopenharmony_ci .protocol_id = SMB311_PROT_ID, 576462306a36Sopenharmony_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, 576562306a36Sopenharmony_ci .large_lock_type = 0, 576662306a36Sopenharmony_ci .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, 576762306a36Sopenharmony_ci .shared_lock_type = SMB2_LOCKFLAG_SHARED, 576862306a36Sopenharmony_ci .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, 576962306a36Sopenharmony_ci .header_size = sizeof(struct smb2_hdr), 577062306a36Sopenharmony_ci .header_preamble_size = 0, 577162306a36Sopenharmony_ci .max_header_size = MAX_SMB2_HDR_SIZE, 577262306a36Sopenharmony_ci .read_rsp_size = sizeof(struct smb2_read_rsp), 577362306a36Sopenharmony_ci .lock_cmd = SMB2_LOCK, 577462306a36Sopenharmony_ci .cap_unix = 0, 577562306a36Sopenharmony_ci .cap_nt_find = SMB2_NT_FIND, 577662306a36Sopenharmony_ci .cap_large_files = SMB2_LARGE_FILES, 577762306a36Sopenharmony_ci .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, 577862306a36Sopenharmony_ci .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, 577962306a36Sopenharmony_ci .create_lease_size = sizeof(struct create_lease_v2), 578062306a36Sopenharmony_ci}; 5781