162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2016 Namjae Jeon <linkinjeon@kernel.org> 462306a36Sopenharmony_ci * Copyright (C) 2018 Samsung Electronics Co., Ltd. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/inetdevice.h> 862306a36Sopenharmony_ci#include <net/addrconf.h> 962306a36Sopenharmony_ci#include <linux/syscalls.h> 1062306a36Sopenharmony_ci#include <linux/namei.h> 1162306a36Sopenharmony_ci#include <linux/statfs.h> 1262306a36Sopenharmony_ci#include <linux/ethtool.h> 1362306a36Sopenharmony_ci#include <linux/falloc.h> 1462306a36Sopenharmony_ci#include <linux/mount.h> 1562306a36Sopenharmony_ci#include <linux/filelock.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "glob.h" 1862306a36Sopenharmony_ci#include "smbfsctl.h" 1962306a36Sopenharmony_ci#include "oplock.h" 2062306a36Sopenharmony_ci#include "smbacl.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include "auth.h" 2362306a36Sopenharmony_ci#include "asn1.h" 2462306a36Sopenharmony_ci#include "connection.h" 2562306a36Sopenharmony_ci#include "transport_ipc.h" 2662306a36Sopenharmony_ci#include "transport_rdma.h" 2762306a36Sopenharmony_ci#include "vfs.h" 2862306a36Sopenharmony_ci#include "vfs_cache.h" 2962306a36Sopenharmony_ci#include "misc.h" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#include "server.h" 3262306a36Sopenharmony_ci#include "smb_common.h" 3362306a36Sopenharmony_ci#include "smbstatus.h" 3462306a36Sopenharmony_ci#include "ksmbd_work.h" 3562306a36Sopenharmony_ci#include "mgmt/user_config.h" 3662306a36Sopenharmony_ci#include "mgmt/share_config.h" 3762306a36Sopenharmony_ci#include "mgmt/tree_connect.h" 3862306a36Sopenharmony_ci#include "mgmt/user_session.h" 3962306a36Sopenharmony_ci#include "mgmt/ksmbd_ida.h" 4062306a36Sopenharmony_ci#include "ndr.h" 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic void __wbuf(struct ksmbd_work *work, void **req, void **rsp) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci if (work->next_smb2_rcv_hdr_off) { 4562306a36Sopenharmony_ci *req = ksmbd_req_buf_next(work); 4662306a36Sopenharmony_ci *rsp = ksmbd_resp_buf_next(work); 4762306a36Sopenharmony_ci } else { 4862306a36Sopenharmony_ci *req = smb2_get_msg(work->request_buf); 4962306a36Sopenharmony_ci *rsp = smb2_get_msg(work->response_buf); 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#define WORK_BUFFERS(w, rq, rs) __wbuf((w), (void **)&(rq), (void **)&(rs)) 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/** 5662306a36Sopenharmony_ci * check_session_id() - check for valid session id in smb header 5762306a36Sopenharmony_ci * @conn: connection instance 5862306a36Sopenharmony_ci * @id: session id from smb header 5962306a36Sopenharmony_ci * 6062306a36Sopenharmony_ci * Return: 1 if valid session id, otherwise 0 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_cistatic inline bool check_session_id(struct ksmbd_conn *conn, u64 id) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci struct ksmbd_session *sess; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci if (id == 0 || id == -1) 6762306a36Sopenharmony_ci return false; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci sess = ksmbd_session_lookup_all(conn, id); 7062306a36Sopenharmony_ci if (sess) 7162306a36Sopenharmony_ci return true; 7262306a36Sopenharmony_ci pr_err("Invalid user session id: %llu\n", id); 7362306a36Sopenharmony_ci return false; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistruct channel *lookup_chann_list(struct ksmbd_session *sess, struct ksmbd_conn *conn) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci return xa_load(&sess->ksmbd_chann_list, (long)conn); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci/** 8262306a36Sopenharmony_ci * smb2_get_ksmbd_tcon() - get tree connection information using a tree id. 8362306a36Sopenharmony_ci * @work: smb work 8462306a36Sopenharmony_ci * 8562306a36Sopenharmony_ci * Return: 0 if there is a tree connection matched or these are 8662306a36Sopenharmony_ci * skipable commands, otherwise error 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_ciint smb2_get_ksmbd_tcon(struct ksmbd_work *work) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci struct smb2_hdr *req_hdr = ksmbd_req_buf_next(work); 9162306a36Sopenharmony_ci unsigned int cmd = le16_to_cpu(req_hdr->Command); 9262306a36Sopenharmony_ci unsigned int tree_id; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci if (cmd == SMB2_TREE_CONNECT_HE || 9562306a36Sopenharmony_ci cmd == SMB2_CANCEL_HE || 9662306a36Sopenharmony_ci cmd == SMB2_LOGOFF_HE) { 9762306a36Sopenharmony_ci ksmbd_debug(SMB, "skip to check tree connect request\n"); 9862306a36Sopenharmony_ci return 0; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (xa_empty(&work->sess->tree_conns)) { 10262306a36Sopenharmony_ci ksmbd_debug(SMB, "NO tree connected\n"); 10362306a36Sopenharmony_ci return -ENOENT; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci tree_id = le32_to_cpu(req_hdr->Id.SyncId.TreeId); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /* 10962306a36Sopenharmony_ci * If request is not the first in Compound request, 11062306a36Sopenharmony_ci * Just validate tree id in header with work->tcon->id. 11162306a36Sopenharmony_ci */ 11262306a36Sopenharmony_ci if (work->next_smb2_rcv_hdr_off) { 11362306a36Sopenharmony_ci if (!work->tcon) { 11462306a36Sopenharmony_ci pr_err("The first operation in the compound does not have tcon\n"); 11562306a36Sopenharmony_ci return -EINVAL; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci if (tree_id != UINT_MAX && work->tcon->id != tree_id) { 11862306a36Sopenharmony_ci pr_err("tree id(%u) is different with id(%u) in first operation\n", 11962306a36Sopenharmony_ci tree_id, work->tcon->id); 12062306a36Sopenharmony_ci return -EINVAL; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci return 1; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci work->tcon = ksmbd_tree_conn_lookup(work->sess, tree_id); 12662306a36Sopenharmony_ci if (!work->tcon) { 12762306a36Sopenharmony_ci pr_err("Invalid tid %d\n", tree_id); 12862306a36Sopenharmony_ci return -ENOENT; 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci return 1; 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci/** 13562306a36Sopenharmony_ci * smb2_set_err_rsp() - set error response code on smb response 13662306a36Sopenharmony_ci * @work: smb work containing response buffer 13762306a36Sopenharmony_ci */ 13862306a36Sopenharmony_civoid smb2_set_err_rsp(struct ksmbd_work *work) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci struct smb2_err_rsp *err_rsp; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (work->next_smb2_rcv_hdr_off) 14362306a36Sopenharmony_ci err_rsp = ksmbd_resp_buf_next(work); 14462306a36Sopenharmony_ci else 14562306a36Sopenharmony_ci err_rsp = smb2_get_msg(work->response_buf); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (err_rsp->hdr.Status != STATUS_STOPPED_ON_SYMLINK) { 14862306a36Sopenharmony_ci int err; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci err_rsp->StructureSize = SMB2_ERROR_STRUCTURE_SIZE2_LE; 15162306a36Sopenharmony_ci err_rsp->ErrorContextCount = 0; 15262306a36Sopenharmony_ci err_rsp->Reserved = 0; 15362306a36Sopenharmony_ci err_rsp->ByteCount = 0; 15462306a36Sopenharmony_ci err_rsp->ErrorData[0] = 0; 15562306a36Sopenharmony_ci err = ksmbd_iov_pin_rsp(work, (void *)err_rsp, 15662306a36Sopenharmony_ci __SMB2_HEADER_STRUCTURE_SIZE + 15762306a36Sopenharmony_ci SMB2_ERROR_STRUCTURE_SIZE2); 15862306a36Sopenharmony_ci if (err) 15962306a36Sopenharmony_ci work->send_no_response = 1; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci/** 16462306a36Sopenharmony_ci * is_smb2_neg_cmd() - is it smb2 negotiation command 16562306a36Sopenharmony_ci * @work: smb work containing smb header 16662306a36Sopenharmony_ci * 16762306a36Sopenharmony_ci * Return: true if smb2 negotiation command, otherwise false 16862306a36Sopenharmony_ci */ 16962306a36Sopenharmony_cibool is_smb2_neg_cmd(struct ksmbd_work *work) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci struct smb2_hdr *hdr = smb2_get_msg(work->request_buf); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* is it SMB2 header ? */ 17462306a36Sopenharmony_ci if (hdr->ProtocolId != SMB2_PROTO_NUMBER) 17562306a36Sopenharmony_ci return false; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci /* make sure it is request not response message */ 17862306a36Sopenharmony_ci if (hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR) 17962306a36Sopenharmony_ci return false; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (hdr->Command != SMB2_NEGOTIATE) 18262306a36Sopenharmony_ci return false; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci return true; 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci/** 18862306a36Sopenharmony_ci * is_smb2_rsp() - is it smb2 response 18962306a36Sopenharmony_ci * @work: smb work containing smb response buffer 19062306a36Sopenharmony_ci * 19162306a36Sopenharmony_ci * Return: true if smb2 response, otherwise false 19262306a36Sopenharmony_ci */ 19362306a36Sopenharmony_cibool is_smb2_rsp(struct ksmbd_work *work) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci struct smb2_hdr *hdr = smb2_get_msg(work->response_buf); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* is it SMB2 header ? */ 19862306a36Sopenharmony_ci if (hdr->ProtocolId != SMB2_PROTO_NUMBER) 19962306a36Sopenharmony_ci return false; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* make sure it is response not request message */ 20262306a36Sopenharmony_ci if (!(hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR)) 20362306a36Sopenharmony_ci return false; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci return true; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci/** 20962306a36Sopenharmony_ci * get_smb2_cmd_val() - get smb command code from smb header 21062306a36Sopenharmony_ci * @work: smb work containing smb request buffer 21162306a36Sopenharmony_ci * 21262306a36Sopenharmony_ci * Return: smb2 request command value 21362306a36Sopenharmony_ci */ 21462306a36Sopenharmony_ciu16 get_smb2_cmd_val(struct ksmbd_work *work) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci struct smb2_hdr *rcv_hdr; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (work->next_smb2_rcv_hdr_off) 21962306a36Sopenharmony_ci rcv_hdr = ksmbd_req_buf_next(work); 22062306a36Sopenharmony_ci else 22162306a36Sopenharmony_ci rcv_hdr = smb2_get_msg(work->request_buf); 22262306a36Sopenharmony_ci return le16_to_cpu(rcv_hdr->Command); 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci/** 22662306a36Sopenharmony_ci * set_smb2_rsp_status() - set error response code on smb2 header 22762306a36Sopenharmony_ci * @work: smb work containing response buffer 22862306a36Sopenharmony_ci * @err: error response code 22962306a36Sopenharmony_ci */ 23062306a36Sopenharmony_civoid set_smb2_rsp_status(struct ksmbd_work *work, __le32 err) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci struct smb2_hdr *rsp_hdr; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci rsp_hdr = smb2_get_msg(work->response_buf); 23562306a36Sopenharmony_ci rsp_hdr->Status = err; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci work->iov_idx = 0; 23862306a36Sopenharmony_ci work->iov_cnt = 0; 23962306a36Sopenharmony_ci work->next_smb2_rcv_hdr_off = 0; 24062306a36Sopenharmony_ci smb2_set_err_rsp(work); 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci/** 24462306a36Sopenharmony_ci * init_smb2_neg_rsp() - initialize smb2 response for negotiate command 24562306a36Sopenharmony_ci * @work: smb work containing smb request buffer 24662306a36Sopenharmony_ci * 24762306a36Sopenharmony_ci * smb2 negotiate response is sent in reply of smb1 negotiate command for 24862306a36Sopenharmony_ci * dialect auto-negotiation. 24962306a36Sopenharmony_ci */ 25062306a36Sopenharmony_ciint init_smb2_neg_rsp(struct ksmbd_work *work) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci struct smb2_hdr *rsp_hdr; 25362306a36Sopenharmony_ci struct smb2_negotiate_rsp *rsp; 25462306a36Sopenharmony_ci struct ksmbd_conn *conn = work->conn; 25562306a36Sopenharmony_ci int err; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci rsp_hdr = smb2_get_msg(work->response_buf); 25862306a36Sopenharmony_ci memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); 25962306a36Sopenharmony_ci rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; 26062306a36Sopenharmony_ci rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; 26162306a36Sopenharmony_ci rsp_hdr->CreditRequest = cpu_to_le16(2); 26262306a36Sopenharmony_ci rsp_hdr->Command = SMB2_NEGOTIATE; 26362306a36Sopenharmony_ci rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR); 26462306a36Sopenharmony_ci rsp_hdr->NextCommand = 0; 26562306a36Sopenharmony_ci rsp_hdr->MessageId = 0; 26662306a36Sopenharmony_ci rsp_hdr->Id.SyncId.ProcessId = 0; 26762306a36Sopenharmony_ci rsp_hdr->Id.SyncId.TreeId = 0; 26862306a36Sopenharmony_ci rsp_hdr->SessionId = 0; 26962306a36Sopenharmony_ci memset(rsp_hdr->Signature, 0, 16); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci rsp = smb2_get_msg(work->response_buf); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci WARN_ON(ksmbd_conn_good(conn)); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci rsp->StructureSize = cpu_to_le16(65); 27662306a36Sopenharmony_ci ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect); 27762306a36Sopenharmony_ci rsp->DialectRevision = cpu_to_le16(conn->dialect); 27862306a36Sopenharmony_ci /* Not setting conn guid rsp->ServerGUID, as it 27962306a36Sopenharmony_ci * not used by client for identifying connection 28062306a36Sopenharmony_ci */ 28162306a36Sopenharmony_ci rsp->Capabilities = cpu_to_le32(conn->vals->capabilities); 28262306a36Sopenharmony_ci /* Default Max Message Size till SMB2.0, 64K*/ 28362306a36Sopenharmony_ci rsp->MaxTransactSize = cpu_to_le32(conn->vals->max_trans_size); 28462306a36Sopenharmony_ci rsp->MaxReadSize = cpu_to_le32(conn->vals->max_read_size); 28562306a36Sopenharmony_ci rsp->MaxWriteSize = cpu_to_le32(conn->vals->max_write_size); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci rsp->SystemTime = cpu_to_le64(ksmbd_systime()); 28862306a36Sopenharmony_ci rsp->ServerStartTime = 0; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci rsp->SecurityBufferOffset = cpu_to_le16(128); 29162306a36Sopenharmony_ci rsp->SecurityBufferLength = cpu_to_le16(AUTH_GSS_LENGTH); 29262306a36Sopenharmony_ci ksmbd_copy_gss_neg_header((char *)(&rsp->hdr) + 29362306a36Sopenharmony_ci le16_to_cpu(rsp->SecurityBufferOffset)); 29462306a36Sopenharmony_ci rsp->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED_LE; 29562306a36Sopenharmony_ci if (server_conf.signing == KSMBD_CONFIG_OPT_MANDATORY) 29662306a36Sopenharmony_ci rsp->SecurityMode |= SMB2_NEGOTIATE_SIGNING_REQUIRED_LE; 29762306a36Sopenharmony_ci err = ksmbd_iov_pin_rsp(work, rsp, 29862306a36Sopenharmony_ci sizeof(struct smb2_negotiate_rsp) + AUTH_GSS_LENGTH); 29962306a36Sopenharmony_ci if (err) 30062306a36Sopenharmony_ci return err; 30162306a36Sopenharmony_ci conn->use_spnego = true; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci ksmbd_conn_set_need_negotiate(conn); 30462306a36Sopenharmony_ci return 0; 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci/** 30862306a36Sopenharmony_ci * smb2_set_rsp_credits() - set number of credits in response buffer 30962306a36Sopenharmony_ci * @work: smb work containing smb response buffer 31062306a36Sopenharmony_ci */ 31162306a36Sopenharmony_ciint smb2_set_rsp_credits(struct ksmbd_work *work) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci struct smb2_hdr *req_hdr = ksmbd_req_buf_next(work); 31462306a36Sopenharmony_ci struct smb2_hdr *hdr = ksmbd_resp_buf_next(work); 31562306a36Sopenharmony_ci struct ksmbd_conn *conn = work->conn; 31662306a36Sopenharmony_ci unsigned short credits_requested, aux_max; 31762306a36Sopenharmony_ci unsigned short credit_charge, credits_granted = 0; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (work->send_no_response) 32062306a36Sopenharmony_ci return 0; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci hdr->CreditCharge = req_hdr->CreditCharge; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (conn->total_credits > conn->vals->max_credits) { 32562306a36Sopenharmony_ci hdr->CreditRequest = 0; 32662306a36Sopenharmony_ci pr_err("Total credits overflow: %d\n", conn->total_credits); 32762306a36Sopenharmony_ci return -EINVAL; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci credit_charge = max_t(unsigned short, 33162306a36Sopenharmony_ci le16_to_cpu(req_hdr->CreditCharge), 1); 33262306a36Sopenharmony_ci if (credit_charge > conn->total_credits) { 33362306a36Sopenharmony_ci ksmbd_debug(SMB, "Insufficient credits granted, given: %u, granted: %u\n", 33462306a36Sopenharmony_ci credit_charge, conn->total_credits); 33562306a36Sopenharmony_ci return -EINVAL; 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci conn->total_credits -= credit_charge; 33962306a36Sopenharmony_ci conn->outstanding_credits -= credit_charge; 34062306a36Sopenharmony_ci credits_requested = max_t(unsigned short, 34162306a36Sopenharmony_ci le16_to_cpu(req_hdr->CreditRequest), 1); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci /* according to smb2.credits smbtorture, Windows server 34462306a36Sopenharmony_ci * 2016 or later grant up to 8192 credits at once. 34562306a36Sopenharmony_ci * 34662306a36Sopenharmony_ci * TODO: Need to adjuct CreditRequest value according to 34762306a36Sopenharmony_ci * current cpu load 34862306a36Sopenharmony_ci */ 34962306a36Sopenharmony_ci if (hdr->Command == SMB2_NEGOTIATE) 35062306a36Sopenharmony_ci aux_max = 1; 35162306a36Sopenharmony_ci else 35262306a36Sopenharmony_ci aux_max = conn->vals->max_credits - conn->total_credits; 35362306a36Sopenharmony_ci credits_granted = min_t(unsigned short, credits_requested, aux_max); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci conn->total_credits += credits_granted; 35662306a36Sopenharmony_ci work->credits_granted += credits_granted; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (!req_hdr->NextCommand) { 35962306a36Sopenharmony_ci /* Update CreditRequest in last request */ 36062306a36Sopenharmony_ci hdr->CreditRequest = cpu_to_le16(work->credits_granted); 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci ksmbd_debug(SMB, 36362306a36Sopenharmony_ci "credits: requested[%d] granted[%d] total_granted[%d]\n", 36462306a36Sopenharmony_ci credits_requested, credits_granted, 36562306a36Sopenharmony_ci conn->total_credits); 36662306a36Sopenharmony_ci return 0; 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci/** 37062306a36Sopenharmony_ci * init_chained_smb2_rsp() - initialize smb2 chained response 37162306a36Sopenharmony_ci * @work: smb work containing smb response buffer 37262306a36Sopenharmony_ci */ 37362306a36Sopenharmony_cistatic void init_chained_smb2_rsp(struct ksmbd_work *work) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci struct smb2_hdr *req = ksmbd_req_buf_next(work); 37662306a36Sopenharmony_ci struct smb2_hdr *rsp = ksmbd_resp_buf_next(work); 37762306a36Sopenharmony_ci struct smb2_hdr *rsp_hdr; 37862306a36Sopenharmony_ci struct smb2_hdr *rcv_hdr; 37962306a36Sopenharmony_ci int next_hdr_offset = 0; 38062306a36Sopenharmony_ci int len, new_len; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci /* Len of this response = updated RFC len - offset of previous cmd 38362306a36Sopenharmony_ci * in the compound rsp 38462306a36Sopenharmony_ci */ 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci /* Storing the current local FID which may be needed by subsequent 38762306a36Sopenharmony_ci * command in the compound request 38862306a36Sopenharmony_ci */ 38962306a36Sopenharmony_ci if (req->Command == SMB2_CREATE && rsp->Status == STATUS_SUCCESS) { 39062306a36Sopenharmony_ci work->compound_fid = ((struct smb2_create_rsp *)rsp)->VolatileFileId; 39162306a36Sopenharmony_ci work->compound_pfid = ((struct smb2_create_rsp *)rsp)->PersistentFileId; 39262306a36Sopenharmony_ci work->compound_sid = le64_to_cpu(rsp->SessionId); 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci len = get_rfc1002_len(work->response_buf) - work->next_smb2_rsp_hdr_off; 39662306a36Sopenharmony_ci next_hdr_offset = le32_to_cpu(req->NextCommand); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci new_len = ALIGN(len, 8); 39962306a36Sopenharmony_ci work->iov[work->iov_idx].iov_len += (new_len - len); 40062306a36Sopenharmony_ci inc_rfc1001_len(work->response_buf, new_len - len); 40162306a36Sopenharmony_ci rsp->NextCommand = cpu_to_le32(new_len); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci work->next_smb2_rcv_hdr_off += next_hdr_offset; 40462306a36Sopenharmony_ci work->curr_smb2_rsp_hdr_off = work->next_smb2_rsp_hdr_off; 40562306a36Sopenharmony_ci work->next_smb2_rsp_hdr_off += new_len; 40662306a36Sopenharmony_ci ksmbd_debug(SMB, 40762306a36Sopenharmony_ci "Compound req new_len = %d rcv off = %d rsp off = %d\n", 40862306a36Sopenharmony_ci new_len, work->next_smb2_rcv_hdr_off, 40962306a36Sopenharmony_ci work->next_smb2_rsp_hdr_off); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci rsp_hdr = ksmbd_resp_buf_next(work); 41262306a36Sopenharmony_ci rcv_hdr = ksmbd_req_buf_next(work); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci if (!(rcv_hdr->Flags & SMB2_FLAGS_RELATED_OPERATIONS)) { 41562306a36Sopenharmony_ci ksmbd_debug(SMB, "related flag should be set\n"); 41662306a36Sopenharmony_ci work->compound_fid = KSMBD_NO_FID; 41762306a36Sopenharmony_ci work->compound_pfid = KSMBD_NO_FID; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci memset((char *)rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); 42062306a36Sopenharmony_ci rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; 42162306a36Sopenharmony_ci rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; 42262306a36Sopenharmony_ci rsp_hdr->Command = rcv_hdr->Command; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci /* 42562306a36Sopenharmony_ci * Message is response. We don't grant oplock yet. 42662306a36Sopenharmony_ci */ 42762306a36Sopenharmony_ci rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR | 42862306a36Sopenharmony_ci SMB2_FLAGS_RELATED_OPERATIONS); 42962306a36Sopenharmony_ci rsp_hdr->NextCommand = 0; 43062306a36Sopenharmony_ci rsp_hdr->MessageId = rcv_hdr->MessageId; 43162306a36Sopenharmony_ci rsp_hdr->Id.SyncId.ProcessId = rcv_hdr->Id.SyncId.ProcessId; 43262306a36Sopenharmony_ci rsp_hdr->Id.SyncId.TreeId = rcv_hdr->Id.SyncId.TreeId; 43362306a36Sopenharmony_ci rsp_hdr->SessionId = rcv_hdr->SessionId; 43462306a36Sopenharmony_ci memcpy(rsp_hdr->Signature, rcv_hdr->Signature, 16); 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci/** 43862306a36Sopenharmony_ci * is_chained_smb2_message() - check for chained command 43962306a36Sopenharmony_ci * @work: smb work containing smb request buffer 44062306a36Sopenharmony_ci * 44162306a36Sopenharmony_ci * Return: true if chained request, otherwise false 44262306a36Sopenharmony_ci */ 44362306a36Sopenharmony_cibool is_chained_smb2_message(struct ksmbd_work *work) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci struct smb2_hdr *hdr = smb2_get_msg(work->request_buf); 44662306a36Sopenharmony_ci unsigned int len, next_cmd; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (hdr->ProtocolId != SMB2_PROTO_NUMBER) 44962306a36Sopenharmony_ci return false; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci hdr = ksmbd_req_buf_next(work); 45262306a36Sopenharmony_ci next_cmd = le32_to_cpu(hdr->NextCommand); 45362306a36Sopenharmony_ci if (next_cmd > 0) { 45462306a36Sopenharmony_ci if ((u64)work->next_smb2_rcv_hdr_off + next_cmd + 45562306a36Sopenharmony_ci __SMB2_HEADER_STRUCTURE_SIZE > 45662306a36Sopenharmony_ci get_rfc1002_len(work->request_buf)) { 45762306a36Sopenharmony_ci pr_err("next command(%u) offset exceeds smb msg size\n", 45862306a36Sopenharmony_ci next_cmd); 45962306a36Sopenharmony_ci return false; 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci if ((u64)get_rfc1002_len(work->response_buf) + MAX_CIFS_SMALL_BUFFER_SIZE > 46362306a36Sopenharmony_ci work->response_sz) { 46462306a36Sopenharmony_ci pr_err("next response offset exceeds response buffer size\n"); 46562306a36Sopenharmony_ci return false; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci ksmbd_debug(SMB, "got SMB2 chained command\n"); 46962306a36Sopenharmony_ci init_chained_smb2_rsp(work); 47062306a36Sopenharmony_ci return true; 47162306a36Sopenharmony_ci } else if (work->next_smb2_rcv_hdr_off) { 47262306a36Sopenharmony_ci /* 47362306a36Sopenharmony_ci * This is last request in chained command, 47462306a36Sopenharmony_ci * align response to 8 byte 47562306a36Sopenharmony_ci */ 47662306a36Sopenharmony_ci len = ALIGN(get_rfc1002_len(work->response_buf), 8); 47762306a36Sopenharmony_ci len = len - get_rfc1002_len(work->response_buf); 47862306a36Sopenharmony_ci if (len) { 47962306a36Sopenharmony_ci ksmbd_debug(SMB, "padding len %u\n", len); 48062306a36Sopenharmony_ci work->iov[work->iov_idx].iov_len += len; 48162306a36Sopenharmony_ci inc_rfc1001_len(work->response_buf, len); 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci work->curr_smb2_rsp_hdr_off = work->next_smb2_rsp_hdr_off; 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci return false; 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci/** 48962306a36Sopenharmony_ci * init_smb2_rsp_hdr() - initialize smb2 response 49062306a36Sopenharmony_ci * @work: smb work containing smb request buffer 49162306a36Sopenharmony_ci * 49262306a36Sopenharmony_ci * Return: 0 49362306a36Sopenharmony_ci */ 49462306a36Sopenharmony_ciint init_smb2_rsp_hdr(struct ksmbd_work *work) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci struct smb2_hdr *rsp_hdr = smb2_get_msg(work->response_buf); 49762306a36Sopenharmony_ci struct smb2_hdr *rcv_hdr = smb2_get_msg(work->request_buf); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); 50062306a36Sopenharmony_ci rsp_hdr->ProtocolId = rcv_hdr->ProtocolId; 50162306a36Sopenharmony_ci rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; 50262306a36Sopenharmony_ci rsp_hdr->Command = rcv_hdr->Command; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci /* 50562306a36Sopenharmony_ci * Message is response. We don't grant oplock yet. 50662306a36Sopenharmony_ci */ 50762306a36Sopenharmony_ci rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR); 50862306a36Sopenharmony_ci rsp_hdr->NextCommand = 0; 50962306a36Sopenharmony_ci rsp_hdr->MessageId = rcv_hdr->MessageId; 51062306a36Sopenharmony_ci rsp_hdr->Id.SyncId.ProcessId = rcv_hdr->Id.SyncId.ProcessId; 51162306a36Sopenharmony_ci rsp_hdr->Id.SyncId.TreeId = rcv_hdr->Id.SyncId.TreeId; 51262306a36Sopenharmony_ci rsp_hdr->SessionId = rcv_hdr->SessionId; 51362306a36Sopenharmony_ci memcpy(rsp_hdr->Signature, rcv_hdr->Signature, 16); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci return 0; 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci/** 51962306a36Sopenharmony_ci * smb2_allocate_rsp_buf() - allocate smb2 response buffer 52062306a36Sopenharmony_ci * @work: smb work containing smb request buffer 52162306a36Sopenharmony_ci * 52262306a36Sopenharmony_ci * Return: 0 on success, otherwise -ENOMEM 52362306a36Sopenharmony_ci */ 52462306a36Sopenharmony_ciint smb2_allocate_rsp_buf(struct ksmbd_work *work) 52562306a36Sopenharmony_ci{ 52662306a36Sopenharmony_ci struct smb2_hdr *hdr = smb2_get_msg(work->request_buf); 52762306a36Sopenharmony_ci size_t small_sz = MAX_CIFS_SMALL_BUFFER_SIZE; 52862306a36Sopenharmony_ci size_t large_sz = small_sz + work->conn->vals->max_trans_size; 52962306a36Sopenharmony_ci size_t sz = small_sz; 53062306a36Sopenharmony_ci int cmd = le16_to_cpu(hdr->Command); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci if (cmd == SMB2_IOCTL_HE || cmd == SMB2_QUERY_DIRECTORY_HE) 53362306a36Sopenharmony_ci sz = large_sz; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci if (cmd == SMB2_QUERY_INFO_HE) { 53662306a36Sopenharmony_ci struct smb2_query_info_req *req; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci req = smb2_get_msg(work->request_buf); 53962306a36Sopenharmony_ci if ((req->InfoType == SMB2_O_INFO_FILE && 54062306a36Sopenharmony_ci (req->FileInfoClass == FILE_FULL_EA_INFORMATION || 54162306a36Sopenharmony_ci req->FileInfoClass == FILE_ALL_INFORMATION)) || 54262306a36Sopenharmony_ci req->InfoType == SMB2_O_INFO_SECURITY) 54362306a36Sopenharmony_ci sz = large_sz; 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci /* allocate large response buf for chained commands */ 54762306a36Sopenharmony_ci if (le32_to_cpu(hdr->NextCommand) > 0) 54862306a36Sopenharmony_ci sz = large_sz; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci work->response_buf = kvzalloc(sz, GFP_KERNEL); 55162306a36Sopenharmony_ci if (!work->response_buf) 55262306a36Sopenharmony_ci return -ENOMEM; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci work->response_sz = sz; 55562306a36Sopenharmony_ci return 0; 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci/** 55962306a36Sopenharmony_ci * smb2_check_user_session() - check for valid session for a user 56062306a36Sopenharmony_ci * @work: smb work containing smb request buffer 56162306a36Sopenharmony_ci * 56262306a36Sopenharmony_ci * Return: 0 on success, otherwise error 56362306a36Sopenharmony_ci */ 56462306a36Sopenharmony_ciint smb2_check_user_session(struct ksmbd_work *work) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci struct smb2_hdr *req_hdr = ksmbd_req_buf_next(work); 56762306a36Sopenharmony_ci struct ksmbd_conn *conn = work->conn; 56862306a36Sopenharmony_ci unsigned int cmd = le16_to_cpu(req_hdr->Command); 56962306a36Sopenharmony_ci unsigned long long sess_id; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci /* 57262306a36Sopenharmony_ci * SMB2_ECHO, SMB2_NEGOTIATE, SMB2_SESSION_SETUP command do not 57362306a36Sopenharmony_ci * require a session id, so no need to validate user session's for 57462306a36Sopenharmony_ci * these commands. 57562306a36Sopenharmony_ci */ 57662306a36Sopenharmony_ci if (cmd == SMB2_ECHO_HE || cmd == SMB2_NEGOTIATE_HE || 57762306a36Sopenharmony_ci cmd == SMB2_SESSION_SETUP_HE) 57862306a36Sopenharmony_ci return 0; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci if (!ksmbd_conn_good(conn)) 58162306a36Sopenharmony_ci return -EIO; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci sess_id = le64_to_cpu(req_hdr->SessionId); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci /* 58662306a36Sopenharmony_ci * If request is not the first in Compound request, 58762306a36Sopenharmony_ci * Just validate session id in header with work->sess->id. 58862306a36Sopenharmony_ci */ 58962306a36Sopenharmony_ci if (work->next_smb2_rcv_hdr_off) { 59062306a36Sopenharmony_ci if (!work->sess) { 59162306a36Sopenharmony_ci pr_err("The first operation in the compound does not have sess\n"); 59262306a36Sopenharmony_ci return -EINVAL; 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci if (sess_id != ULLONG_MAX && work->sess->id != sess_id) { 59562306a36Sopenharmony_ci pr_err("session id(%llu) is different with the first operation(%lld)\n", 59662306a36Sopenharmony_ci sess_id, work->sess->id); 59762306a36Sopenharmony_ci return -EINVAL; 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci return 1; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci /* Check for validity of user session */ 60362306a36Sopenharmony_ci work->sess = ksmbd_session_lookup_all(conn, sess_id); 60462306a36Sopenharmony_ci if (work->sess) 60562306a36Sopenharmony_ci return 1; 60662306a36Sopenharmony_ci ksmbd_debug(SMB, "Invalid user session, Uid %llu\n", sess_id); 60762306a36Sopenharmony_ci return -ENOENT; 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cistatic void destroy_previous_session(struct ksmbd_conn *conn, 61162306a36Sopenharmony_ci struct ksmbd_user *user, u64 id) 61262306a36Sopenharmony_ci{ 61362306a36Sopenharmony_ci struct ksmbd_session *prev_sess = ksmbd_session_lookup_slowpath(id); 61462306a36Sopenharmony_ci struct ksmbd_user *prev_user; 61562306a36Sopenharmony_ci struct channel *chann; 61662306a36Sopenharmony_ci long index; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci if (!prev_sess) 61962306a36Sopenharmony_ci return; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci prev_user = prev_sess->user; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci if (!prev_user || 62462306a36Sopenharmony_ci strcmp(user->name, prev_user->name) || 62562306a36Sopenharmony_ci user->passkey_sz != prev_user->passkey_sz || 62662306a36Sopenharmony_ci memcmp(user->passkey, prev_user->passkey, user->passkey_sz)) 62762306a36Sopenharmony_ci return; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci prev_sess->state = SMB2_SESSION_EXPIRED; 63062306a36Sopenharmony_ci xa_for_each(&prev_sess->ksmbd_chann_list, index, chann) 63162306a36Sopenharmony_ci ksmbd_conn_set_exiting(chann->conn); 63262306a36Sopenharmony_ci} 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci/** 63562306a36Sopenharmony_ci * smb2_get_name() - get filename string from on the wire smb format 63662306a36Sopenharmony_ci * @src: source buffer 63762306a36Sopenharmony_ci * @maxlen: maxlen of source string 63862306a36Sopenharmony_ci * @local_nls: nls_table pointer 63962306a36Sopenharmony_ci * 64062306a36Sopenharmony_ci * Return: matching converted filename on success, otherwise error ptr 64162306a36Sopenharmony_ci */ 64262306a36Sopenharmony_cistatic char * 64362306a36Sopenharmony_cismb2_get_name(const char *src, const int maxlen, struct nls_table *local_nls) 64462306a36Sopenharmony_ci{ 64562306a36Sopenharmony_ci char *name; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci name = smb_strndup_from_utf16(src, maxlen, 1, local_nls); 64862306a36Sopenharmony_ci if (IS_ERR(name)) { 64962306a36Sopenharmony_ci pr_err("failed to get name %ld\n", PTR_ERR(name)); 65062306a36Sopenharmony_ci return name; 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci ksmbd_conv_path_to_unix(name); 65462306a36Sopenharmony_ci ksmbd_strip_last_slash(name); 65562306a36Sopenharmony_ci return name; 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ciint setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci struct ksmbd_conn *conn = work->conn; 66162306a36Sopenharmony_ci int id; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci id = ksmbd_acquire_async_msg_id(&conn->async_ida); 66462306a36Sopenharmony_ci if (id < 0) { 66562306a36Sopenharmony_ci pr_err("Failed to alloc async message id\n"); 66662306a36Sopenharmony_ci return id; 66762306a36Sopenharmony_ci } 66862306a36Sopenharmony_ci work->asynchronous = true; 66962306a36Sopenharmony_ci work->async_id = id; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci ksmbd_debug(SMB, 67262306a36Sopenharmony_ci "Send interim Response to inform async request id : %d\n", 67362306a36Sopenharmony_ci work->async_id); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci work->cancel_fn = fn; 67662306a36Sopenharmony_ci work->cancel_argv = arg; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci if (list_empty(&work->async_request_entry)) { 67962306a36Sopenharmony_ci spin_lock(&conn->request_lock); 68062306a36Sopenharmony_ci list_add_tail(&work->async_request_entry, &conn->async_requests); 68162306a36Sopenharmony_ci spin_unlock(&conn->request_lock); 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci return 0; 68562306a36Sopenharmony_ci} 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_civoid release_async_work(struct ksmbd_work *work) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci struct ksmbd_conn *conn = work->conn; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci spin_lock(&conn->request_lock); 69262306a36Sopenharmony_ci list_del_init(&work->async_request_entry); 69362306a36Sopenharmony_ci spin_unlock(&conn->request_lock); 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci work->asynchronous = 0; 69662306a36Sopenharmony_ci work->cancel_fn = NULL; 69762306a36Sopenharmony_ci kfree(work->cancel_argv); 69862306a36Sopenharmony_ci work->cancel_argv = NULL; 69962306a36Sopenharmony_ci if (work->async_id) { 70062306a36Sopenharmony_ci ksmbd_release_id(&conn->async_ida, work->async_id); 70162306a36Sopenharmony_ci work->async_id = 0; 70262306a36Sopenharmony_ci } 70362306a36Sopenharmony_ci} 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_civoid smb2_send_interim_resp(struct ksmbd_work *work, __le32 status) 70662306a36Sopenharmony_ci{ 70762306a36Sopenharmony_ci struct smb2_hdr *rsp_hdr; 70862306a36Sopenharmony_ci struct ksmbd_work *in_work = ksmbd_alloc_work_struct(); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci if (allocate_interim_rsp_buf(in_work)) { 71162306a36Sopenharmony_ci pr_err("smb_allocate_rsp_buf failed!\n"); 71262306a36Sopenharmony_ci ksmbd_free_work_struct(in_work); 71362306a36Sopenharmony_ci return; 71462306a36Sopenharmony_ci } 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci in_work->conn = work->conn; 71762306a36Sopenharmony_ci memcpy(smb2_get_msg(in_work->response_buf), ksmbd_resp_buf_next(work), 71862306a36Sopenharmony_ci __SMB2_HEADER_STRUCTURE_SIZE); 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci rsp_hdr = smb2_get_msg(in_work->response_buf); 72162306a36Sopenharmony_ci rsp_hdr->Flags |= SMB2_FLAGS_ASYNC_COMMAND; 72262306a36Sopenharmony_ci rsp_hdr->Id.AsyncId = cpu_to_le64(work->async_id); 72362306a36Sopenharmony_ci smb2_set_err_rsp(in_work); 72462306a36Sopenharmony_ci rsp_hdr->Status = status; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci ksmbd_conn_write(in_work); 72762306a36Sopenharmony_ci ksmbd_free_work_struct(in_work); 72862306a36Sopenharmony_ci} 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_cistatic __le32 smb2_get_reparse_tag_special_file(umode_t mode) 73162306a36Sopenharmony_ci{ 73262306a36Sopenharmony_ci if (S_ISDIR(mode) || S_ISREG(mode)) 73362306a36Sopenharmony_ci return 0; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci if (S_ISLNK(mode)) 73662306a36Sopenharmony_ci return IO_REPARSE_TAG_LX_SYMLINK_LE; 73762306a36Sopenharmony_ci else if (S_ISFIFO(mode)) 73862306a36Sopenharmony_ci return IO_REPARSE_TAG_LX_FIFO_LE; 73962306a36Sopenharmony_ci else if (S_ISSOCK(mode)) 74062306a36Sopenharmony_ci return IO_REPARSE_TAG_AF_UNIX_LE; 74162306a36Sopenharmony_ci else if (S_ISCHR(mode)) 74262306a36Sopenharmony_ci return IO_REPARSE_TAG_LX_CHR_LE; 74362306a36Sopenharmony_ci else if (S_ISBLK(mode)) 74462306a36Sopenharmony_ci return IO_REPARSE_TAG_LX_BLK_LE; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci return 0; 74762306a36Sopenharmony_ci} 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci/** 75062306a36Sopenharmony_ci * smb2_get_dos_mode() - get file mode in dos format from unix mode 75162306a36Sopenharmony_ci * @stat: kstat containing file mode 75262306a36Sopenharmony_ci * @attribute: attribute flags 75362306a36Sopenharmony_ci * 75462306a36Sopenharmony_ci * Return: converted dos mode 75562306a36Sopenharmony_ci */ 75662306a36Sopenharmony_cistatic int smb2_get_dos_mode(struct kstat *stat, int attribute) 75762306a36Sopenharmony_ci{ 75862306a36Sopenharmony_ci int attr = 0; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci if (S_ISDIR(stat->mode)) { 76162306a36Sopenharmony_ci attr = FILE_ATTRIBUTE_DIRECTORY | 76262306a36Sopenharmony_ci (attribute & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)); 76362306a36Sopenharmony_ci } else { 76462306a36Sopenharmony_ci attr = (attribute & 0x00005137) | FILE_ATTRIBUTE_ARCHIVE; 76562306a36Sopenharmony_ci attr &= ~(FILE_ATTRIBUTE_DIRECTORY); 76662306a36Sopenharmony_ci if (S_ISREG(stat->mode) && (server_conf.share_fake_fscaps & 76762306a36Sopenharmony_ci FILE_SUPPORTS_SPARSE_FILES)) 76862306a36Sopenharmony_ci attr |= FILE_ATTRIBUTE_SPARSE_FILE; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci if (smb2_get_reparse_tag_special_file(stat->mode)) 77162306a36Sopenharmony_ci attr |= FILE_ATTRIBUTE_REPARSE_POINT; 77262306a36Sopenharmony_ci } 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci return attr; 77562306a36Sopenharmony_ci} 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_cistatic void build_preauth_ctxt(struct smb2_preauth_neg_context *pneg_ctxt, 77862306a36Sopenharmony_ci __le16 hash_id) 77962306a36Sopenharmony_ci{ 78062306a36Sopenharmony_ci pneg_ctxt->ContextType = SMB2_PREAUTH_INTEGRITY_CAPABILITIES; 78162306a36Sopenharmony_ci pneg_ctxt->DataLength = cpu_to_le16(38); 78262306a36Sopenharmony_ci pneg_ctxt->HashAlgorithmCount = cpu_to_le16(1); 78362306a36Sopenharmony_ci pneg_ctxt->Reserved = cpu_to_le32(0); 78462306a36Sopenharmony_ci pneg_ctxt->SaltLength = cpu_to_le16(SMB311_SALT_SIZE); 78562306a36Sopenharmony_ci get_random_bytes(pneg_ctxt->Salt, SMB311_SALT_SIZE); 78662306a36Sopenharmony_ci pneg_ctxt->HashAlgorithms = hash_id; 78762306a36Sopenharmony_ci} 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_cistatic void build_encrypt_ctxt(struct smb2_encryption_neg_context *pneg_ctxt, 79062306a36Sopenharmony_ci __le16 cipher_type) 79162306a36Sopenharmony_ci{ 79262306a36Sopenharmony_ci pneg_ctxt->ContextType = SMB2_ENCRYPTION_CAPABILITIES; 79362306a36Sopenharmony_ci pneg_ctxt->DataLength = cpu_to_le16(4); 79462306a36Sopenharmony_ci pneg_ctxt->Reserved = cpu_to_le32(0); 79562306a36Sopenharmony_ci pneg_ctxt->CipherCount = cpu_to_le16(1); 79662306a36Sopenharmony_ci pneg_ctxt->Ciphers[0] = cipher_type; 79762306a36Sopenharmony_ci} 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_cistatic void build_sign_cap_ctxt(struct smb2_signing_capabilities *pneg_ctxt, 80062306a36Sopenharmony_ci __le16 sign_algo) 80162306a36Sopenharmony_ci{ 80262306a36Sopenharmony_ci pneg_ctxt->ContextType = SMB2_SIGNING_CAPABILITIES; 80362306a36Sopenharmony_ci pneg_ctxt->DataLength = 80462306a36Sopenharmony_ci cpu_to_le16((sizeof(struct smb2_signing_capabilities) + 2) 80562306a36Sopenharmony_ci - sizeof(struct smb2_neg_context)); 80662306a36Sopenharmony_ci pneg_ctxt->Reserved = cpu_to_le32(0); 80762306a36Sopenharmony_ci pneg_ctxt->SigningAlgorithmCount = cpu_to_le16(1); 80862306a36Sopenharmony_ci pneg_ctxt->SigningAlgorithms[0] = sign_algo; 80962306a36Sopenharmony_ci} 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_cistatic void build_posix_ctxt(struct smb2_posix_neg_context *pneg_ctxt) 81262306a36Sopenharmony_ci{ 81362306a36Sopenharmony_ci pneg_ctxt->ContextType = SMB2_POSIX_EXTENSIONS_AVAILABLE; 81462306a36Sopenharmony_ci pneg_ctxt->DataLength = cpu_to_le16(POSIX_CTXT_DATA_LEN); 81562306a36Sopenharmony_ci /* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */ 81662306a36Sopenharmony_ci pneg_ctxt->Name[0] = 0x93; 81762306a36Sopenharmony_ci pneg_ctxt->Name[1] = 0xAD; 81862306a36Sopenharmony_ci pneg_ctxt->Name[2] = 0x25; 81962306a36Sopenharmony_ci pneg_ctxt->Name[3] = 0x50; 82062306a36Sopenharmony_ci pneg_ctxt->Name[4] = 0x9C; 82162306a36Sopenharmony_ci pneg_ctxt->Name[5] = 0xB4; 82262306a36Sopenharmony_ci pneg_ctxt->Name[6] = 0x11; 82362306a36Sopenharmony_ci pneg_ctxt->Name[7] = 0xE7; 82462306a36Sopenharmony_ci pneg_ctxt->Name[8] = 0xB4; 82562306a36Sopenharmony_ci pneg_ctxt->Name[9] = 0x23; 82662306a36Sopenharmony_ci pneg_ctxt->Name[10] = 0x83; 82762306a36Sopenharmony_ci pneg_ctxt->Name[11] = 0xDE; 82862306a36Sopenharmony_ci pneg_ctxt->Name[12] = 0x96; 82962306a36Sopenharmony_ci pneg_ctxt->Name[13] = 0x8B; 83062306a36Sopenharmony_ci pneg_ctxt->Name[14] = 0xCD; 83162306a36Sopenharmony_ci pneg_ctxt->Name[15] = 0x7C; 83262306a36Sopenharmony_ci} 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_cistatic unsigned int assemble_neg_contexts(struct ksmbd_conn *conn, 83562306a36Sopenharmony_ci struct smb2_negotiate_rsp *rsp) 83662306a36Sopenharmony_ci{ 83762306a36Sopenharmony_ci char * const pneg_ctxt = (char *)rsp + 83862306a36Sopenharmony_ci le32_to_cpu(rsp->NegotiateContextOffset); 83962306a36Sopenharmony_ci int neg_ctxt_cnt = 1; 84062306a36Sopenharmony_ci int ctxt_size; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci ksmbd_debug(SMB, 84362306a36Sopenharmony_ci "assemble SMB2_PREAUTH_INTEGRITY_CAPABILITIES context\n"); 84462306a36Sopenharmony_ci build_preauth_ctxt((struct smb2_preauth_neg_context *)pneg_ctxt, 84562306a36Sopenharmony_ci conn->preauth_info->Preauth_HashId); 84662306a36Sopenharmony_ci ctxt_size = sizeof(struct smb2_preauth_neg_context); 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci if (conn->cipher_type) { 84962306a36Sopenharmony_ci /* Round to 8 byte boundary */ 85062306a36Sopenharmony_ci ctxt_size = round_up(ctxt_size, 8); 85162306a36Sopenharmony_ci ksmbd_debug(SMB, 85262306a36Sopenharmony_ci "assemble SMB2_ENCRYPTION_CAPABILITIES context\n"); 85362306a36Sopenharmony_ci build_encrypt_ctxt((struct smb2_encryption_neg_context *) 85462306a36Sopenharmony_ci (pneg_ctxt + ctxt_size), 85562306a36Sopenharmony_ci conn->cipher_type); 85662306a36Sopenharmony_ci neg_ctxt_cnt++; 85762306a36Sopenharmony_ci ctxt_size += sizeof(struct smb2_encryption_neg_context) + 2; 85862306a36Sopenharmony_ci } 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci /* compression context not yet supported */ 86162306a36Sopenharmony_ci WARN_ON(conn->compress_algorithm != SMB3_COMPRESS_NONE); 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci if (conn->posix_ext_supported) { 86462306a36Sopenharmony_ci ctxt_size = round_up(ctxt_size, 8); 86562306a36Sopenharmony_ci ksmbd_debug(SMB, 86662306a36Sopenharmony_ci "assemble SMB2_POSIX_EXTENSIONS_AVAILABLE context\n"); 86762306a36Sopenharmony_ci build_posix_ctxt((struct smb2_posix_neg_context *) 86862306a36Sopenharmony_ci (pneg_ctxt + ctxt_size)); 86962306a36Sopenharmony_ci neg_ctxt_cnt++; 87062306a36Sopenharmony_ci ctxt_size += sizeof(struct smb2_posix_neg_context); 87162306a36Sopenharmony_ci } 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci if (conn->signing_negotiated) { 87462306a36Sopenharmony_ci ctxt_size = round_up(ctxt_size, 8); 87562306a36Sopenharmony_ci ksmbd_debug(SMB, 87662306a36Sopenharmony_ci "assemble SMB2_SIGNING_CAPABILITIES context\n"); 87762306a36Sopenharmony_ci build_sign_cap_ctxt((struct smb2_signing_capabilities *) 87862306a36Sopenharmony_ci (pneg_ctxt + ctxt_size), 87962306a36Sopenharmony_ci conn->signing_algorithm); 88062306a36Sopenharmony_ci neg_ctxt_cnt++; 88162306a36Sopenharmony_ci ctxt_size += sizeof(struct smb2_signing_capabilities) + 2; 88262306a36Sopenharmony_ci } 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci rsp->NegotiateContextCount = cpu_to_le16(neg_ctxt_cnt); 88562306a36Sopenharmony_ci return ctxt_size + AUTH_GSS_PADDING; 88662306a36Sopenharmony_ci} 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_cistatic __le32 decode_preauth_ctxt(struct ksmbd_conn *conn, 88962306a36Sopenharmony_ci struct smb2_preauth_neg_context *pneg_ctxt, 89062306a36Sopenharmony_ci int ctxt_len) 89162306a36Sopenharmony_ci{ 89262306a36Sopenharmony_ci /* 89362306a36Sopenharmony_ci * sizeof(smb2_preauth_neg_context) assumes SMB311_SALT_SIZE Salt, 89462306a36Sopenharmony_ci * which may not be present. Only check for used HashAlgorithms[1]. 89562306a36Sopenharmony_ci */ 89662306a36Sopenharmony_ci if (ctxt_len < 89762306a36Sopenharmony_ci sizeof(struct smb2_neg_context) + MIN_PREAUTH_CTXT_DATA_LEN) 89862306a36Sopenharmony_ci return STATUS_INVALID_PARAMETER; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci if (pneg_ctxt->HashAlgorithms != SMB2_PREAUTH_INTEGRITY_SHA512) 90162306a36Sopenharmony_ci return STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci conn->preauth_info->Preauth_HashId = SMB2_PREAUTH_INTEGRITY_SHA512; 90462306a36Sopenharmony_ci return STATUS_SUCCESS; 90562306a36Sopenharmony_ci} 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_cistatic void decode_encrypt_ctxt(struct ksmbd_conn *conn, 90862306a36Sopenharmony_ci struct smb2_encryption_neg_context *pneg_ctxt, 90962306a36Sopenharmony_ci int ctxt_len) 91062306a36Sopenharmony_ci{ 91162306a36Sopenharmony_ci int cph_cnt; 91262306a36Sopenharmony_ci int i, cphs_size; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci if (sizeof(struct smb2_encryption_neg_context) > ctxt_len) { 91562306a36Sopenharmony_ci pr_err("Invalid SMB2_ENCRYPTION_CAPABILITIES context size\n"); 91662306a36Sopenharmony_ci return; 91762306a36Sopenharmony_ci } 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci conn->cipher_type = 0; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci cph_cnt = le16_to_cpu(pneg_ctxt->CipherCount); 92262306a36Sopenharmony_ci cphs_size = cph_cnt * sizeof(__le16); 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci if (sizeof(struct smb2_encryption_neg_context) + cphs_size > 92562306a36Sopenharmony_ci ctxt_len) { 92662306a36Sopenharmony_ci pr_err("Invalid cipher count(%d)\n", cph_cnt); 92762306a36Sopenharmony_ci return; 92862306a36Sopenharmony_ci } 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF) 93162306a36Sopenharmony_ci return; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci for (i = 0; i < cph_cnt; i++) { 93462306a36Sopenharmony_ci if (pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES128_GCM || 93562306a36Sopenharmony_ci pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES128_CCM || 93662306a36Sopenharmony_ci pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES256_CCM || 93762306a36Sopenharmony_ci pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES256_GCM) { 93862306a36Sopenharmony_ci ksmbd_debug(SMB, "Cipher ID = 0x%x\n", 93962306a36Sopenharmony_ci pneg_ctxt->Ciphers[i]); 94062306a36Sopenharmony_ci conn->cipher_type = pneg_ctxt->Ciphers[i]; 94162306a36Sopenharmony_ci break; 94262306a36Sopenharmony_ci } 94362306a36Sopenharmony_ci } 94462306a36Sopenharmony_ci} 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci/** 94762306a36Sopenharmony_ci * smb3_encryption_negotiated() - checks if server and client agreed on enabling encryption 94862306a36Sopenharmony_ci * @conn: smb connection 94962306a36Sopenharmony_ci * 95062306a36Sopenharmony_ci * Return: true if connection should be encrypted, else false 95162306a36Sopenharmony_ci */ 95262306a36Sopenharmony_cibool smb3_encryption_negotiated(struct ksmbd_conn *conn) 95362306a36Sopenharmony_ci{ 95462306a36Sopenharmony_ci if (!conn->ops->generate_encryptionkey) 95562306a36Sopenharmony_ci return false; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci /* 95862306a36Sopenharmony_ci * SMB 3.0 and 3.0.2 dialects use the SMB2_GLOBAL_CAP_ENCRYPTION flag. 95962306a36Sopenharmony_ci * SMB 3.1.1 uses the cipher_type field. 96062306a36Sopenharmony_ci */ 96162306a36Sopenharmony_ci return (conn->vals->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION) || 96262306a36Sopenharmony_ci conn->cipher_type; 96362306a36Sopenharmony_ci} 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_cistatic void decode_compress_ctxt(struct ksmbd_conn *conn, 96662306a36Sopenharmony_ci struct smb2_compression_capabilities_context *pneg_ctxt) 96762306a36Sopenharmony_ci{ 96862306a36Sopenharmony_ci conn->compress_algorithm = SMB3_COMPRESS_NONE; 96962306a36Sopenharmony_ci} 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_cistatic void decode_sign_cap_ctxt(struct ksmbd_conn *conn, 97262306a36Sopenharmony_ci struct smb2_signing_capabilities *pneg_ctxt, 97362306a36Sopenharmony_ci int ctxt_len) 97462306a36Sopenharmony_ci{ 97562306a36Sopenharmony_ci int sign_algo_cnt; 97662306a36Sopenharmony_ci int i, sign_alos_size; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci if (sizeof(struct smb2_signing_capabilities) > ctxt_len) { 97962306a36Sopenharmony_ci pr_err("Invalid SMB2_SIGNING_CAPABILITIES context length\n"); 98062306a36Sopenharmony_ci return; 98162306a36Sopenharmony_ci } 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci conn->signing_negotiated = false; 98462306a36Sopenharmony_ci sign_algo_cnt = le16_to_cpu(pneg_ctxt->SigningAlgorithmCount); 98562306a36Sopenharmony_ci sign_alos_size = sign_algo_cnt * sizeof(__le16); 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci if (sizeof(struct smb2_signing_capabilities) + sign_alos_size > 98862306a36Sopenharmony_ci ctxt_len) { 98962306a36Sopenharmony_ci pr_err("Invalid signing algorithm count(%d)\n", sign_algo_cnt); 99062306a36Sopenharmony_ci return; 99162306a36Sopenharmony_ci } 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci for (i = 0; i < sign_algo_cnt; i++) { 99462306a36Sopenharmony_ci if (pneg_ctxt->SigningAlgorithms[i] == SIGNING_ALG_HMAC_SHA256_LE || 99562306a36Sopenharmony_ci pneg_ctxt->SigningAlgorithms[i] == SIGNING_ALG_AES_CMAC_LE) { 99662306a36Sopenharmony_ci ksmbd_debug(SMB, "Signing Algorithm ID = 0x%x\n", 99762306a36Sopenharmony_ci pneg_ctxt->SigningAlgorithms[i]); 99862306a36Sopenharmony_ci conn->signing_negotiated = true; 99962306a36Sopenharmony_ci conn->signing_algorithm = 100062306a36Sopenharmony_ci pneg_ctxt->SigningAlgorithms[i]; 100162306a36Sopenharmony_ci break; 100262306a36Sopenharmony_ci } 100362306a36Sopenharmony_ci } 100462306a36Sopenharmony_ci} 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_cistatic __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, 100762306a36Sopenharmony_ci struct smb2_negotiate_req *req, 100862306a36Sopenharmony_ci unsigned int len_of_smb) 100962306a36Sopenharmony_ci{ 101062306a36Sopenharmony_ci /* +4 is to account for the RFC1001 len field */ 101162306a36Sopenharmony_ci struct smb2_neg_context *pctx = (struct smb2_neg_context *)req; 101262306a36Sopenharmony_ci int i = 0, len_of_ctxts; 101362306a36Sopenharmony_ci unsigned int offset = le32_to_cpu(req->NegotiateContextOffset); 101462306a36Sopenharmony_ci unsigned int neg_ctxt_cnt = le16_to_cpu(req->NegotiateContextCount); 101562306a36Sopenharmony_ci __le32 status = STATUS_INVALID_PARAMETER; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci ksmbd_debug(SMB, "decoding %d negotiate contexts\n", neg_ctxt_cnt); 101862306a36Sopenharmony_ci if (len_of_smb <= offset) { 101962306a36Sopenharmony_ci ksmbd_debug(SMB, "Invalid response: negotiate context offset\n"); 102062306a36Sopenharmony_ci return status; 102162306a36Sopenharmony_ci } 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci len_of_ctxts = len_of_smb - offset; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci while (i++ < neg_ctxt_cnt) { 102662306a36Sopenharmony_ci int clen, ctxt_len; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci if (len_of_ctxts < (int)sizeof(struct smb2_neg_context)) 102962306a36Sopenharmony_ci break; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci pctx = (struct smb2_neg_context *)((char *)pctx + offset); 103262306a36Sopenharmony_ci clen = le16_to_cpu(pctx->DataLength); 103362306a36Sopenharmony_ci ctxt_len = clen + sizeof(struct smb2_neg_context); 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci if (ctxt_len > len_of_ctxts) 103662306a36Sopenharmony_ci break; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci if (pctx->ContextType == SMB2_PREAUTH_INTEGRITY_CAPABILITIES) { 103962306a36Sopenharmony_ci ksmbd_debug(SMB, 104062306a36Sopenharmony_ci "deassemble SMB2_PREAUTH_INTEGRITY_CAPABILITIES context\n"); 104162306a36Sopenharmony_ci if (conn->preauth_info->Preauth_HashId) 104262306a36Sopenharmony_ci break; 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci status = decode_preauth_ctxt(conn, 104562306a36Sopenharmony_ci (struct smb2_preauth_neg_context *)pctx, 104662306a36Sopenharmony_ci ctxt_len); 104762306a36Sopenharmony_ci if (status != STATUS_SUCCESS) 104862306a36Sopenharmony_ci break; 104962306a36Sopenharmony_ci } else if (pctx->ContextType == SMB2_ENCRYPTION_CAPABILITIES) { 105062306a36Sopenharmony_ci ksmbd_debug(SMB, 105162306a36Sopenharmony_ci "deassemble SMB2_ENCRYPTION_CAPABILITIES context\n"); 105262306a36Sopenharmony_ci if (conn->cipher_type) 105362306a36Sopenharmony_ci break; 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci decode_encrypt_ctxt(conn, 105662306a36Sopenharmony_ci (struct smb2_encryption_neg_context *)pctx, 105762306a36Sopenharmony_ci ctxt_len); 105862306a36Sopenharmony_ci } else if (pctx->ContextType == SMB2_COMPRESSION_CAPABILITIES) { 105962306a36Sopenharmony_ci ksmbd_debug(SMB, 106062306a36Sopenharmony_ci "deassemble SMB2_COMPRESSION_CAPABILITIES context\n"); 106162306a36Sopenharmony_ci if (conn->compress_algorithm) 106262306a36Sopenharmony_ci break; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci decode_compress_ctxt(conn, 106562306a36Sopenharmony_ci (struct smb2_compression_capabilities_context *)pctx); 106662306a36Sopenharmony_ci } else if (pctx->ContextType == SMB2_NETNAME_NEGOTIATE_CONTEXT_ID) { 106762306a36Sopenharmony_ci ksmbd_debug(SMB, 106862306a36Sopenharmony_ci "deassemble SMB2_NETNAME_NEGOTIATE_CONTEXT_ID context\n"); 106962306a36Sopenharmony_ci } else if (pctx->ContextType == SMB2_POSIX_EXTENSIONS_AVAILABLE) { 107062306a36Sopenharmony_ci ksmbd_debug(SMB, 107162306a36Sopenharmony_ci "deassemble SMB2_POSIX_EXTENSIONS_AVAILABLE context\n"); 107262306a36Sopenharmony_ci conn->posix_ext_supported = true; 107362306a36Sopenharmony_ci } else if (pctx->ContextType == SMB2_SIGNING_CAPABILITIES) { 107462306a36Sopenharmony_ci ksmbd_debug(SMB, 107562306a36Sopenharmony_ci "deassemble SMB2_SIGNING_CAPABILITIES context\n"); 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci decode_sign_cap_ctxt(conn, 107862306a36Sopenharmony_ci (struct smb2_signing_capabilities *)pctx, 107962306a36Sopenharmony_ci ctxt_len); 108062306a36Sopenharmony_ci } 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci /* offsets must be 8 byte aligned */ 108362306a36Sopenharmony_ci offset = (ctxt_len + 7) & ~0x7; 108462306a36Sopenharmony_ci len_of_ctxts -= offset; 108562306a36Sopenharmony_ci } 108662306a36Sopenharmony_ci return status; 108762306a36Sopenharmony_ci} 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci/** 109062306a36Sopenharmony_ci * smb2_handle_negotiate() - handler for smb2 negotiate command 109162306a36Sopenharmony_ci * @work: smb work containing smb request buffer 109262306a36Sopenharmony_ci * 109362306a36Sopenharmony_ci * Return: 0 109462306a36Sopenharmony_ci */ 109562306a36Sopenharmony_ciint smb2_handle_negotiate(struct ksmbd_work *work) 109662306a36Sopenharmony_ci{ 109762306a36Sopenharmony_ci struct ksmbd_conn *conn = work->conn; 109862306a36Sopenharmony_ci struct smb2_negotiate_req *req = smb2_get_msg(work->request_buf); 109962306a36Sopenharmony_ci struct smb2_negotiate_rsp *rsp = smb2_get_msg(work->response_buf); 110062306a36Sopenharmony_ci int rc = 0; 110162306a36Sopenharmony_ci unsigned int smb2_buf_len, smb2_neg_size, neg_ctxt_len = 0; 110262306a36Sopenharmony_ci __le32 status; 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci ksmbd_debug(SMB, "Received negotiate request\n"); 110562306a36Sopenharmony_ci conn->need_neg = false; 110662306a36Sopenharmony_ci if (ksmbd_conn_good(conn)) { 110762306a36Sopenharmony_ci pr_err("conn->tcp_status is already in CifsGood State\n"); 110862306a36Sopenharmony_ci work->send_no_response = 1; 110962306a36Sopenharmony_ci return rc; 111062306a36Sopenharmony_ci } 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci smb2_buf_len = get_rfc1002_len(work->request_buf); 111362306a36Sopenharmony_ci smb2_neg_size = offsetof(struct smb2_negotiate_req, Dialects); 111462306a36Sopenharmony_ci if (smb2_neg_size > smb2_buf_len) { 111562306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_PARAMETER; 111662306a36Sopenharmony_ci rc = -EINVAL; 111762306a36Sopenharmony_ci goto err_out; 111862306a36Sopenharmony_ci } 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci if (req->DialectCount == 0) { 112162306a36Sopenharmony_ci pr_err("malformed packet\n"); 112262306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_PARAMETER; 112362306a36Sopenharmony_ci rc = -EINVAL; 112462306a36Sopenharmony_ci goto err_out; 112562306a36Sopenharmony_ci } 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci if (conn->dialect == SMB311_PROT_ID) { 112862306a36Sopenharmony_ci unsigned int nego_ctxt_off = le32_to_cpu(req->NegotiateContextOffset); 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci if (smb2_buf_len < nego_ctxt_off) { 113162306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_PARAMETER; 113262306a36Sopenharmony_ci rc = -EINVAL; 113362306a36Sopenharmony_ci goto err_out; 113462306a36Sopenharmony_ci } 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci if (smb2_neg_size > nego_ctxt_off) { 113762306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_PARAMETER; 113862306a36Sopenharmony_ci rc = -EINVAL; 113962306a36Sopenharmony_ci goto err_out; 114062306a36Sopenharmony_ci } 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci if (smb2_neg_size + le16_to_cpu(req->DialectCount) * sizeof(__le16) > 114362306a36Sopenharmony_ci nego_ctxt_off) { 114462306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_PARAMETER; 114562306a36Sopenharmony_ci rc = -EINVAL; 114662306a36Sopenharmony_ci goto err_out; 114762306a36Sopenharmony_ci } 114862306a36Sopenharmony_ci } else { 114962306a36Sopenharmony_ci if (smb2_neg_size + le16_to_cpu(req->DialectCount) * sizeof(__le16) > 115062306a36Sopenharmony_ci smb2_buf_len) { 115162306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_PARAMETER; 115262306a36Sopenharmony_ci rc = -EINVAL; 115362306a36Sopenharmony_ci goto err_out; 115462306a36Sopenharmony_ci } 115562306a36Sopenharmony_ci } 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci conn->cli_cap = le32_to_cpu(req->Capabilities); 115862306a36Sopenharmony_ci switch (conn->dialect) { 115962306a36Sopenharmony_ci case SMB311_PROT_ID: 116062306a36Sopenharmony_ci conn->preauth_info = 116162306a36Sopenharmony_ci kzalloc(sizeof(struct preauth_integrity_info), 116262306a36Sopenharmony_ci GFP_KERNEL); 116362306a36Sopenharmony_ci if (!conn->preauth_info) { 116462306a36Sopenharmony_ci rc = -ENOMEM; 116562306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_PARAMETER; 116662306a36Sopenharmony_ci goto err_out; 116762306a36Sopenharmony_ci } 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci status = deassemble_neg_contexts(conn, req, 117062306a36Sopenharmony_ci get_rfc1002_len(work->request_buf)); 117162306a36Sopenharmony_ci if (status != STATUS_SUCCESS) { 117262306a36Sopenharmony_ci pr_err("deassemble_neg_contexts error(0x%x)\n", 117362306a36Sopenharmony_ci status); 117462306a36Sopenharmony_ci rsp->hdr.Status = status; 117562306a36Sopenharmony_ci rc = -EINVAL; 117662306a36Sopenharmony_ci kfree(conn->preauth_info); 117762306a36Sopenharmony_ci conn->preauth_info = NULL; 117862306a36Sopenharmony_ci goto err_out; 117962306a36Sopenharmony_ci } 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci rc = init_smb3_11_server(conn); 118262306a36Sopenharmony_ci if (rc < 0) { 118362306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_PARAMETER; 118462306a36Sopenharmony_ci kfree(conn->preauth_info); 118562306a36Sopenharmony_ci conn->preauth_info = NULL; 118662306a36Sopenharmony_ci goto err_out; 118762306a36Sopenharmony_ci } 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci ksmbd_gen_preauth_integrity_hash(conn, 119062306a36Sopenharmony_ci work->request_buf, 119162306a36Sopenharmony_ci conn->preauth_info->Preauth_HashValue); 119262306a36Sopenharmony_ci rsp->NegotiateContextOffset = 119362306a36Sopenharmony_ci cpu_to_le32(OFFSET_OF_NEG_CONTEXT); 119462306a36Sopenharmony_ci neg_ctxt_len = assemble_neg_contexts(conn, rsp); 119562306a36Sopenharmony_ci break; 119662306a36Sopenharmony_ci case SMB302_PROT_ID: 119762306a36Sopenharmony_ci init_smb3_02_server(conn); 119862306a36Sopenharmony_ci break; 119962306a36Sopenharmony_ci case SMB30_PROT_ID: 120062306a36Sopenharmony_ci init_smb3_0_server(conn); 120162306a36Sopenharmony_ci break; 120262306a36Sopenharmony_ci case SMB21_PROT_ID: 120362306a36Sopenharmony_ci init_smb2_1_server(conn); 120462306a36Sopenharmony_ci break; 120562306a36Sopenharmony_ci case SMB2X_PROT_ID: 120662306a36Sopenharmony_ci case BAD_PROT_ID: 120762306a36Sopenharmony_ci default: 120862306a36Sopenharmony_ci ksmbd_debug(SMB, "Server dialect :0x%x not supported\n", 120962306a36Sopenharmony_ci conn->dialect); 121062306a36Sopenharmony_ci rsp->hdr.Status = STATUS_NOT_SUPPORTED; 121162306a36Sopenharmony_ci rc = -EINVAL; 121262306a36Sopenharmony_ci goto err_out; 121362306a36Sopenharmony_ci } 121462306a36Sopenharmony_ci rsp->Capabilities = cpu_to_le32(conn->vals->capabilities); 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci /* For stats */ 121762306a36Sopenharmony_ci conn->connection_type = conn->dialect; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci rsp->MaxTransactSize = cpu_to_le32(conn->vals->max_trans_size); 122062306a36Sopenharmony_ci rsp->MaxReadSize = cpu_to_le32(conn->vals->max_read_size); 122162306a36Sopenharmony_ci rsp->MaxWriteSize = cpu_to_le32(conn->vals->max_write_size); 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci memcpy(conn->ClientGUID, req->ClientGUID, 122462306a36Sopenharmony_ci SMB2_CLIENT_GUID_SIZE); 122562306a36Sopenharmony_ci conn->cli_sec_mode = le16_to_cpu(req->SecurityMode); 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci rsp->StructureSize = cpu_to_le16(65); 122862306a36Sopenharmony_ci rsp->DialectRevision = cpu_to_le16(conn->dialect); 122962306a36Sopenharmony_ci /* Not setting conn guid rsp->ServerGUID, as it 123062306a36Sopenharmony_ci * not used by client for identifying server 123162306a36Sopenharmony_ci */ 123262306a36Sopenharmony_ci memset(rsp->ServerGUID, 0, SMB2_CLIENT_GUID_SIZE); 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci rsp->SystemTime = cpu_to_le64(ksmbd_systime()); 123562306a36Sopenharmony_ci rsp->ServerStartTime = 0; 123662306a36Sopenharmony_ci ksmbd_debug(SMB, "negotiate context offset %d, count %d\n", 123762306a36Sopenharmony_ci le32_to_cpu(rsp->NegotiateContextOffset), 123862306a36Sopenharmony_ci le16_to_cpu(rsp->NegotiateContextCount)); 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci rsp->SecurityBufferOffset = cpu_to_le16(128); 124162306a36Sopenharmony_ci rsp->SecurityBufferLength = cpu_to_le16(AUTH_GSS_LENGTH); 124262306a36Sopenharmony_ci ksmbd_copy_gss_neg_header((char *)(&rsp->hdr) + 124362306a36Sopenharmony_ci le16_to_cpu(rsp->SecurityBufferOffset)); 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci rsp->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED_LE; 124662306a36Sopenharmony_ci conn->use_spnego = true; 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci if ((server_conf.signing == KSMBD_CONFIG_OPT_AUTO || 124962306a36Sopenharmony_ci server_conf.signing == KSMBD_CONFIG_OPT_DISABLED) && 125062306a36Sopenharmony_ci req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED_LE) 125162306a36Sopenharmony_ci conn->sign = true; 125262306a36Sopenharmony_ci else if (server_conf.signing == KSMBD_CONFIG_OPT_MANDATORY) { 125362306a36Sopenharmony_ci server_conf.enforced_signing = true; 125462306a36Sopenharmony_ci rsp->SecurityMode |= SMB2_NEGOTIATE_SIGNING_REQUIRED_LE; 125562306a36Sopenharmony_ci conn->sign = true; 125662306a36Sopenharmony_ci } 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci conn->srv_sec_mode = le16_to_cpu(rsp->SecurityMode); 125962306a36Sopenharmony_ci ksmbd_conn_set_need_negotiate(conn); 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_cierr_out: 126262306a36Sopenharmony_ci if (rc) 126362306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci if (!rc) 126662306a36Sopenharmony_ci rc = ksmbd_iov_pin_rsp(work, rsp, 126762306a36Sopenharmony_ci sizeof(struct smb2_negotiate_rsp) + 126862306a36Sopenharmony_ci AUTH_GSS_LENGTH + neg_ctxt_len); 126962306a36Sopenharmony_ci if (rc < 0) 127062306a36Sopenharmony_ci smb2_set_err_rsp(work); 127162306a36Sopenharmony_ci return rc; 127262306a36Sopenharmony_ci} 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_cistatic int alloc_preauth_hash(struct ksmbd_session *sess, 127562306a36Sopenharmony_ci struct ksmbd_conn *conn) 127662306a36Sopenharmony_ci{ 127762306a36Sopenharmony_ci if (sess->Preauth_HashValue) 127862306a36Sopenharmony_ci return 0; 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci sess->Preauth_HashValue = kmemdup(conn->preauth_info->Preauth_HashValue, 128162306a36Sopenharmony_ci PREAUTH_HASHVALUE_SIZE, GFP_KERNEL); 128262306a36Sopenharmony_ci if (!sess->Preauth_HashValue) 128362306a36Sopenharmony_ci return -ENOMEM; 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci return 0; 128662306a36Sopenharmony_ci} 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_cistatic int generate_preauth_hash(struct ksmbd_work *work) 128962306a36Sopenharmony_ci{ 129062306a36Sopenharmony_ci struct ksmbd_conn *conn = work->conn; 129162306a36Sopenharmony_ci struct ksmbd_session *sess = work->sess; 129262306a36Sopenharmony_ci u8 *preauth_hash; 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci if (conn->dialect != SMB311_PROT_ID) 129562306a36Sopenharmony_ci return 0; 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci if (conn->binding) { 129862306a36Sopenharmony_ci struct preauth_session *preauth_sess; 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci preauth_sess = ksmbd_preauth_session_lookup(conn, sess->id); 130162306a36Sopenharmony_ci if (!preauth_sess) { 130262306a36Sopenharmony_ci preauth_sess = ksmbd_preauth_session_alloc(conn, sess->id); 130362306a36Sopenharmony_ci if (!preauth_sess) 130462306a36Sopenharmony_ci return -ENOMEM; 130562306a36Sopenharmony_ci } 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci preauth_hash = preauth_sess->Preauth_HashValue; 130862306a36Sopenharmony_ci } else { 130962306a36Sopenharmony_ci if (!sess->Preauth_HashValue) 131062306a36Sopenharmony_ci if (alloc_preauth_hash(sess, conn)) 131162306a36Sopenharmony_ci return -ENOMEM; 131262306a36Sopenharmony_ci preauth_hash = sess->Preauth_HashValue; 131362306a36Sopenharmony_ci } 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci ksmbd_gen_preauth_integrity_hash(conn, work->request_buf, preauth_hash); 131662306a36Sopenharmony_ci return 0; 131762306a36Sopenharmony_ci} 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_cistatic int decode_negotiation_token(struct ksmbd_conn *conn, 132062306a36Sopenharmony_ci struct negotiate_message *negblob, 132162306a36Sopenharmony_ci size_t sz) 132262306a36Sopenharmony_ci{ 132362306a36Sopenharmony_ci if (!conn->use_spnego) 132462306a36Sopenharmony_ci return -EINVAL; 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci if (ksmbd_decode_negTokenInit((char *)negblob, sz, conn)) { 132762306a36Sopenharmony_ci if (ksmbd_decode_negTokenTarg((char *)negblob, sz, conn)) { 132862306a36Sopenharmony_ci conn->auth_mechs |= KSMBD_AUTH_NTLMSSP; 132962306a36Sopenharmony_ci conn->preferred_auth_mech = KSMBD_AUTH_NTLMSSP; 133062306a36Sopenharmony_ci conn->use_spnego = false; 133162306a36Sopenharmony_ci } 133262306a36Sopenharmony_ci } 133362306a36Sopenharmony_ci return 0; 133462306a36Sopenharmony_ci} 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_cistatic int ntlm_negotiate(struct ksmbd_work *work, 133762306a36Sopenharmony_ci struct negotiate_message *negblob, 133862306a36Sopenharmony_ci size_t negblob_len, struct smb2_sess_setup_rsp *rsp) 133962306a36Sopenharmony_ci{ 134062306a36Sopenharmony_ci struct challenge_message *chgblob; 134162306a36Sopenharmony_ci unsigned char *spnego_blob = NULL; 134262306a36Sopenharmony_ci u16 spnego_blob_len; 134362306a36Sopenharmony_ci char *neg_blob; 134462306a36Sopenharmony_ci int sz, rc; 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci ksmbd_debug(SMB, "negotiate phase\n"); 134762306a36Sopenharmony_ci rc = ksmbd_decode_ntlmssp_neg_blob(negblob, negblob_len, work->conn); 134862306a36Sopenharmony_ci if (rc) 134962306a36Sopenharmony_ci return rc; 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci sz = le16_to_cpu(rsp->SecurityBufferOffset); 135262306a36Sopenharmony_ci chgblob = 135362306a36Sopenharmony_ci (struct challenge_message *)((char *)&rsp->hdr.ProtocolId + sz); 135462306a36Sopenharmony_ci memset(chgblob, 0, sizeof(struct challenge_message)); 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci if (!work->conn->use_spnego) { 135762306a36Sopenharmony_ci sz = ksmbd_build_ntlmssp_challenge_blob(chgblob, work->conn); 135862306a36Sopenharmony_ci if (sz < 0) 135962306a36Sopenharmony_ci return -ENOMEM; 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci rsp->SecurityBufferLength = cpu_to_le16(sz); 136262306a36Sopenharmony_ci return 0; 136362306a36Sopenharmony_ci } 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci sz = sizeof(struct challenge_message); 136662306a36Sopenharmony_ci sz += (strlen(ksmbd_netbios_name()) * 2 + 1 + 4) * 6; 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci neg_blob = kzalloc(sz, GFP_KERNEL); 136962306a36Sopenharmony_ci if (!neg_blob) 137062306a36Sopenharmony_ci return -ENOMEM; 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci chgblob = (struct challenge_message *)neg_blob; 137362306a36Sopenharmony_ci sz = ksmbd_build_ntlmssp_challenge_blob(chgblob, work->conn); 137462306a36Sopenharmony_ci if (sz < 0) { 137562306a36Sopenharmony_ci rc = -ENOMEM; 137662306a36Sopenharmony_ci goto out; 137762306a36Sopenharmony_ci } 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci rc = build_spnego_ntlmssp_neg_blob(&spnego_blob, &spnego_blob_len, 138062306a36Sopenharmony_ci neg_blob, sz); 138162306a36Sopenharmony_ci if (rc) { 138262306a36Sopenharmony_ci rc = -ENOMEM; 138362306a36Sopenharmony_ci goto out; 138462306a36Sopenharmony_ci } 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci sz = le16_to_cpu(rsp->SecurityBufferOffset); 138762306a36Sopenharmony_ci memcpy((char *)&rsp->hdr.ProtocolId + sz, spnego_blob, spnego_blob_len); 138862306a36Sopenharmony_ci rsp->SecurityBufferLength = cpu_to_le16(spnego_blob_len); 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ciout: 139162306a36Sopenharmony_ci kfree(spnego_blob); 139262306a36Sopenharmony_ci kfree(neg_blob); 139362306a36Sopenharmony_ci return rc; 139462306a36Sopenharmony_ci} 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_cistatic struct authenticate_message *user_authblob(struct ksmbd_conn *conn, 139762306a36Sopenharmony_ci struct smb2_sess_setup_req *req) 139862306a36Sopenharmony_ci{ 139962306a36Sopenharmony_ci int sz; 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci if (conn->use_spnego && conn->mechToken) 140262306a36Sopenharmony_ci return (struct authenticate_message *)conn->mechToken; 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci sz = le16_to_cpu(req->SecurityBufferOffset); 140562306a36Sopenharmony_ci return (struct authenticate_message *)((char *)&req->hdr.ProtocolId 140662306a36Sopenharmony_ci + sz); 140762306a36Sopenharmony_ci} 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_cistatic struct ksmbd_user *session_user(struct ksmbd_conn *conn, 141062306a36Sopenharmony_ci struct smb2_sess_setup_req *req) 141162306a36Sopenharmony_ci{ 141262306a36Sopenharmony_ci struct authenticate_message *authblob; 141362306a36Sopenharmony_ci struct ksmbd_user *user; 141462306a36Sopenharmony_ci char *name; 141562306a36Sopenharmony_ci unsigned int name_off, name_len, secbuf_len; 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci if (conn->use_spnego && conn->mechToken) 141862306a36Sopenharmony_ci secbuf_len = conn->mechTokenLen; 141962306a36Sopenharmony_ci else 142062306a36Sopenharmony_ci secbuf_len = le16_to_cpu(req->SecurityBufferLength); 142162306a36Sopenharmony_ci if (secbuf_len < sizeof(struct authenticate_message)) { 142262306a36Sopenharmony_ci ksmbd_debug(SMB, "blob len %d too small\n", secbuf_len); 142362306a36Sopenharmony_ci return NULL; 142462306a36Sopenharmony_ci } 142562306a36Sopenharmony_ci authblob = user_authblob(conn, req); 142662306a36Sopenharmony_ci name_off = le32_to_cpu(authblob->UserName.BufferOffset); 142762306a36Sopenharmony_ci name_len = le16_to_cpu(authblob->UserName.Length); 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci if (secbuf_len < (u64)name_off + name_len) 143062306a36Sopenharmony_ci return NULL; 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci name = smb_strndup_from_utf16((const char *)authblob + name_off, 143362306a36Sopenharmony_ci name_len, 143462306a36Sopenharmony_ci true, 143562306a36Sopenharmony_ci conn->local_nls); 143662306a36Sopenharmony_ci if (IS_ERR(name)) { 143762306a36Sopenharmony_ci pr_err("cannot allocate memory\n"); 143862306a36Sopenharmony_ci return NULL; 143962306a36Sopenharmony_ci } 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci ksmbd_debug(SMB, "session setup request for user %s\n", name); 144262306a36Sopenharmony_ci user = ksmbd_login_user(name); 144362306a36Sopenharmony_ci kfree(name); 144462306a36Sopenharmony_ci return user; 144562306a36Sopenharmony_ci} 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_cistatic int ntlm_authenticate(struct ksmbd_work *work, 144862306a36Sopenharmony_ci struct smb2_sess_setup_req *req, 144962306a36Sopenharmony_ci struct smb2_sess_setup_rsp *rsp) 145062306a36Sopenharmony_ci{ 145162306a36Sopenharmony_ci struct ksmbd_conn *conn = work->conn; 145262306a36Sopenharmony_ci struct ksmbd_session *sess = work->sess; 145362306a36Sopenharmony_ci struct channel *chann = NULL; 145462306a36Sopenharmony_ci struct ksmbd_user *user; 145562306a36Sopenharmony_ci u64 prev_id; 145662306a36Sopenharmony_ci int sz, rc; 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci ksmbd_debug(SMB, "authenticate phase\n"); 145962306a36Sopenharmony_ci if (conn->use_spnego) { 146062306a36Sopenharmony_ci unsigned char *spnego_blob; 146162306a36Sopenharmony_ci u16 spnego_blob_len; 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci rc = build_spnego_ntlmssp_auth_blob(&spnego_blob, 146462306a36Sopenharmony_ci &spnego_blob_len, 146562306a36Sopenharmony_ci 0); 146662306a36Sopenharmony_ci if (rc) 146762306a36Sopenharmony_ci return -ENOMEM; 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci sz = le16_to_cpu(rsp->SecurityBufferOffset); 147062306a36Sopenharmony_ci memcpy((char *)&rsp->hdr.ProtocolId + sz, spnego_blob, spnego_blob_len); 147162306a36Sopenharmony_ci rsp->SecurityBufferLength = cpu_to_le16(spnego_blob_len); 147262306a36Sopenharmony_ci kfree(spnego_blob); 147362306a36Sopenharmony_ci } 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci user = session_user(conn, req); 147662306a36Sopenharmony_ci if (!user) { 147762306a36Sopenharmony_ci ksmbd_debug(SMB, "Unknown user name or an error\n"); 147862306a36Sopenharmony_ci return -EPERM; 147962306a36Sopenharmony_ci } 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci /* Check for previous session */ 148262306a36Sopenharmony_ci prev_id = le64_to_cpu(req->PreviousSessionId); 148362306a36Sopenharmony_ci if (prev_id && prev_id != sess->id) 148462306a36Sopenharmony_ci destroy_previous_session(conn, user, prev_id); 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci if (sess->state == SMB2_SESSION_VALID) { 148762306a36Sopenharmony_ci /* 148862306a36Sopenharmony_ci * Reuse session if anonymous try to connect 148962306a36Sopenharmony_ci * on reauthetication. 149062306a36Sopenharmony_ci */ 149162306a36Sopenharmony_ci if (conn->binding == false && ksmbd_anonymous_user(user)) { 149262306a36Sopenharmony_ci ksmbd_free_user(user); 149362306a36Sopenharmony_ci return 0; 149462306a36Sopenharmony_ci } 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci if (!ksmbd_compare_user(sess->user, user)) { 149762306a36Sopenharmony_ci ksmbd_free_user(user); 149862306a36Sopenharmony_ci return -EPERM; 149962306a36Sopenharmony_ci } 150062306a36Sopenharmony_ci ksmbd_free_user(user); 150162306a36Sopenharmony_ci } else { 150262306a36Sopenharmony_ci sess->user = user; 150362306a36Sopenharmony_ci } 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci if (conn->binding == false && user_guest(sess->user)) { 150662306a36Sopenharmony_ci rsp->SessionFlags = SMB2_SESSION_FLAG_IS_GUEST_LE; 150762306a36Sopenharmony_ci } else { 150862306a36Sopenharmony_ci struct authenticate_message *authblob; 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ci authblob = user_authblob(conn, req); 151162306a36Sopenharmony_ci if (conn->use_spnego && conn->mechToken) 151262306a36Sopenharmony_ci sz = conn->mechTokenLen; 151362306a36Sopenharmony_ci else 151462306a36Sopenharmony_ci sz = le16_to_cpu(req->SecurityBufferLength); 151562306a36Sopenharmony_ci rc = ksmbd_decode_ntlmssp_auth_blob(authblob, sz, conn, sess); 151662306a36Sopenharmony_ci if (rc) { 151762306a36Sopenharmony_ci set_user_flag(sess->user, KSMBD_USER_FLAG_BAD_PASSWORD); 151862306a36Sopenharmony_ci ksmbd_debug(SMB, "authentication failed\n"); 151962306a36Sopenharmony_ci return -EPERM; 152062306a36Sopenharmony_ci } 152162306a36Sopenharmony_ci } 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci /* 152462306a36Sopenharmony_ci * If session state is SMB2_SESSION_VALID, We can assume 152562306a36Sopenharmony_ci * that it is reauthentication. And the user/password 152662306a36Sopenharmony_ci * has been verified, so return it here. 152762306a36Sopenharmony_ci */ 152862306a36Sopenharmony_ci if (sess->state == SMB2_SESSION_VALID) { 152962306a36Sopenharmony_ci if (conn->binding) 153062306a36Sopenharmony_ci goto binding_session; 153162306a36Sopenharmony_ci return 0; 153262306a36Sopenharmony_ci } 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci if ((rsp->SessionFlags != SMB2_SESSION_FLAG_IS_GUEST_LE && 153562306a36Sopenharmony_ci (conn->sign || server_conf.enforced_signing)) || 153662306a36Sopenharmony_ci (req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED)) 153762306a36Sopenharmony_ci sess->sign = true; 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci if (smb3_encryption_negotiated(conn) && 154062306a36Sopenharmony_ci !(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) { 154162306a36Sopenharmony_ci rc = conn->ops->generate_encryptionkey(conn, sess); 154262306a36Sopenharmony_ci if (rc) { 154362306a36Sopenharmony_ci ksmbd_debug(SMB, 154462306a36Sopenharmony_ci "SMB3 encryption key generation failed\n"); 154562306a36Sopenharmony_ci return -EINVAL; 154662306a36Sopenharmony_ci } 154762306a36Sopenharmony_ci sess->enc = true; 154862306a36Sopenharmony_ci if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION) 154962306a36Sopenharmony_ci rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE; 155062306a36Sopenharmony_ci /* 155162306a36Sopenharmony_ci * signing is disable if encryption is enable 155262306a36Sopenharmony_ci * on this session 155362306a36Sopenharmony_ci */ 155462306a36Sopenharmony_ci sess->sign = false; 155562306a36Sopenharmony_ci } 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_cibinding_session: 155862306a36Sopenharmony_ci if (conn->dialect >= SMB30_PROT_ID) { 155962306a36Sopenharmony_ci chann = lookup_chann_list(sess, conn); 156062306a36Sopenharmony_ci if (!chann) { 156162306a36Sopenharmony_ci chann = kmalloc(sizeof(struct channel), GFP_KERNEL); 156262306a36Sopenharmony_ci if (!chann) 156362306a36Sopenharmony_ci return -ENOMEM; 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci chann->conn = conn; 156662306a36Sopenharmony_ci xa_store(&sess->ksmbd_chann_list, (long)conn, chann, GFP_KERNEL); 156762306a36Sopenharmony_ci } 156862306a36Sopenharmony_ci } 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci if (conn->ops->generate_signingkey) { 157162306a36Sopenharmony_ci rc = conn->ops->generate_signingkey(sess, conn); 157262306a36Sopenharmony_ci if (rc) { 157362306a36Sopenharmony_ci ksmbd_debug(SMB, "SMB3 signing key generation failed\n"); 157462306a36Sopenharmony_ci return -EINVAL; 157562306a36Sopenharmony_ci } 157662306a36Sopenharmony_ci } 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_ci if (!ksmbd_conn_lookup_dialect(conn)) { 157962306a36Sopenharmony_ci pr_err("fail to verify the dialect\n"); 158062306a36Sopenharmony_ci return -ENOENT; 158162306a36Sopenharmony_ci } 158262306a36Sopenharmony_ci return 0; 158362306a36Sopenharmony_ci} 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_ci#ifdef CONFIG_SMB_SERVER_KERBEROS5 158662306a36Sopenharmony_cistatic int krb5_authenticate(struct ksmbd_work *work, 158762306a36Sopenharmony_ci struct smb2_sess_setup_req *req, 158862306a36Sopenharmony_ci struct smb2_sess_setup_rsp *rsp) 158962306a36Sopenharmony_ci{ 159062306a36Sopenharmony_ci struct ksmbd_conn *conn = work->conn; 159162306a36Sopenharmony_ci struct ksmbd_session *sess = work->sess; 159262306a36Sopenharmony_ci char *in_blob, *out_blob; 159362306a36Sopenharmony_ci struct channel *chann = NULL; 159462306a36Sopenharmony_ci u64 prev_sess_id; 159562306a36Sopenharmony_ci int in_len, out_len; 159662306a36Sopenharmony_ci int retval; 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_ci in_blob = (char *)&req->hdr.ProtocolId + 159962306a36Sopenharmony_ci le16_to_cpu(req->SecurityBufferOffset); 160062306a36Sopenharmony_ci in_len = le16_to_cpu(req->SecurityBufferLength); 160162306a36Sopenharmony_ci out_blob = (char *)&rsp->hdr.ProtocolId + 160262306a36Sopenharmony_ci le16_to_cpu(rsp->SecurityBufferOffset); 160362306a36Sopenharmony_ci out_len = work->response_sz - 160462306a36Sopenharmony_ci (le16_to_cpu(rsp->SecurityBufferOffset) + 4); 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ci /* Check previous session */ 160762306a36Sopenharmony_ci prev_sess_id = le64_to_cpu(req->PreviousSessionId); 160862306a36Sopenharmony_ci if (prev_sess_id && prev_sess_id != sess->id) 160962306a36Sopenharmony_ci destroy_previous_session(conn, sess->user, prev_sess_id); 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_ci if (sess->state == SMB2_SESSION_VALID) 161262306a36Sopenharmony_ci ksmbd_free_user(sess->user); 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_ci retval = ksmbd_krb5_authenticate(sess, in_blob, in_len, 161562306a36Sopenharmony_ci out_blob, &out_len); 161662306a36Sopenharmony_ci if (retval) { 161762306a36Sopenharmony_ci ksmbd_debug(SMB, "krb5 authentication failed\n"); 161862306a36Sopenharmony_ci return -EINVAL; 161962306a36Sopenharmony_ci } 162062306a36Sopenharmony_ci rsp->SecurityBufferLength = cpu_to_le16(out_len); 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_ci if ((conn->sign || server_conf.enforced_signing) || 162362306a36Sopenharmony_ci (req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED)) 162462306a36Sopenharmony_ci sess->sign = true; 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci if (smb3_encryption_negotiated(conn)) { 162762306a36Sopenharmony_ci retval = conn->ops->generate_encryptionkey(conn, sess); 162862306a36Sopenharmony_ci if (retval) { 162962306a36Sopenharmony_ci ksmbd_debug(SMB, 163062306a36Sopenharmony_ci "SMB3 encryption key generation failed\n"); 163162306a36Sopenharmony_ci return -EINVAL; 163262306a36Sopenharmony_ci } 163362306a36Sopenharmony_ci sess->enc = true; 163462306a36Sopenharmony_ci if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION) 163562306a36Sopenharmony_ci rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE; 163662306a36Sopenharmony_ci sess->sign = false; 163762306a36Sopenharmony_ci } 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci if (conn->dialect >= SMB30_PROT_ID) { 164062306a36Sopenharmony_ci chann = lookup_chann_list(sess, conn); 164162306a36Sopenharmony_ci if (!chann) { 164262306a36Sopenharmony_ci chann = kmalloc(sizeof(struct channel), GFP_KERNEL); 164362306a36Sopenharmony_ci if (!chann) 164462306a36Sopenharmony_ci return -ENOMEM; 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_ci chann->conn = conn; 164762306a36Sopenharmony_ci xa_store(&sess->ksmbd_chann_list, (long)conn, chann, GFP_KERNEL); 164862306a36Sopenharmony_ci } 164962306a36Sopenharmony_ci } 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci if (conn->ops->generate_signingkey) { 165262306a36Sopenharmony_ci retval = conn->ops->generate_signingkey(sess, conn); 165362306a36Sopenharmony_ci if (retval) { 165462306a36Sopenharmony_ci ksmbd_debug(SMB, "SMB3 signing key generation failed\n"); 165562306a36Sopenharmony_ci return -EINVAL; 165662306a36Sopenharmony_ci } 165762306a36Sopenharmony_ci } 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_ci if (!ksmbd_conn_lookup_dialect(conn)) { 166062306a36Sopenharmony_ci pr_err("fail to verify the dialect\n"); 166162306a36Sopenharmony_ci return -ENOENT; 166262306a36Sopenharmony_ci } 166362306a36Sopenharmony_ci return 0; 166462306a36Sopenharmony_ci} 166562306a36Sopenharmony_ci#else 166662306a36Sopenharmony_cistatic int krb5_authenticate(struct ksmbd_work *work, 166762306a36Sopenharmony_ci struct smb2_sess_setup_req *req, 166862306a36Sopenharmony_ci struct smb2_sess_setup_rsp *rsp) 166962306a36Sopenharmony_ci{ 167062306a36Sopenharmony_ci return -EOPNOTSUPP; 167162306a36Sopenharmony_ci} 167262306a36Sopenharmony_ci#endif 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ciint smb2_sess_setup(struct ksmbd_work *work) 167562306a36Sopenharmony_ci{ 167662306a36Sopenharmony_ci struct ksmbd_conn *conn = work->conn; 167762306a36Sopenharmony_ci struct smb2_sess_setup_req *req; 167862306a36Sopenharmony_ci struct smb2_sess_setup_rsp *rsp; 167962306a36Sopenharmony_ci struct ksmbd_session *sess; 168062306a36Sopenharmony_ci struct negotiate_message *negblob; 168162306a36Sopenharmony_ci unsigned int negblob_len, negblob_off; 168262306a36Sopenharmony_ci int rc = 0; 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_ci ksmbd_debug(SMB, "Received request for session setup\n"); 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_ci WORK_BUFFERS(work, req, rsp); 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_ci rsp->StructureSize = cpu_to_le16(9); 168962306a36Sopenharmony_ci rsp->SessionFlags = 0; 169062306a36Sopenharmony_ci rsp->SecurityBufferOffset = cpu_to_le16(72); 169162306a36Sopenharmony_ci rsp->SecurityBufferLength = 0; 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci ksmbd_conn_lock(conn); 169462306a36Sopenharmony_ci if (!req->hdr.SessionId) { 169562306a36Sopenharmony_ci sess = ksmbd_smb2_session_create(); 169662306a36Sopenharmony_ci if (!sess) { 169762306a36Sopenharmony_ci rc = -ENOMEM; 169862306a36Sopenharmony_ci goto out_err; 169962306a36Sopenharmony_ci } 170062306a36Sopenharmony_ci rsp->hdr.SessionId = cpu_to_le64(sess->id); 170162306a36Sopenharmony_ci rc = ksmbd_session_register(conn, sess); 170262306a36Sopenharmony_ci if (rc) 170362306a36Sopenharmony_ci goto out_err; 170462306a36Sopenharmony_ci } else if (conn->dialect >= SMB30_PROT_ID && 170562306a36Sopenharmony_ci (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) && 170662306a36Sopenharmony_ci req->Flags & SMB2_SESSION_REQ_FLAG_BINDING) { 170762306a36Sopenharmony_ci u64 sess_id = le64_to_cpu(req->hdr.SessionId); 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_ci sess = ksmbd_session_lookup_slowpath(sess_id); 171062306a36Sopenharmony_ci if (!sess) { 171162306a36Sopenharmony_ci rc = -ENOENT; 171262306a36Sopenharmony_ci goto out_err; 171362306a36Sopenharmony_ci } 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci if (conn->dialect != sess->dialect) { 171662306a36Sopenharmony_ci rc = -EINVAL; 171762306a36Sopenharmony_ci goto out_err; 171862306a36Sopenharmony_ci } 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_ci if (!(req->hdr.Flags & SMB2_FLAGS_SIGNED)) { 172162306a36Sopenharmony_ci rc = -EINVAL; 172262306a36Sopenharmony_ci goto out_err; 172362306a36Sopenharmony_ci } 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_ci if (strncmp(conn->ClientGUID, sess->ClientGUID, 172662306a36Sopenharmony_ci SMB2_CLIENT_GUID_SIZE)) { 172762306a36Sopenharmony_ci rc = -ENOENT; 172862306a36Sopenharmony_ci goto out_err; 172962306a36Sopenharmony_ci } 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_ci if (sess->state == SMB2_SESSION_IN_PROGRESS) { 173262306a36Sopenharmony_ci rc = -EACCES; 173362306a36Sopenharmony_ci goto out_err; 173462306a36Sopenharmony_ci } 173562306a36Sopenharmony_ci 173662306a36Sopenharmony_ci if (sess->state == SMB2_SESSION_EXPIRED) { 173762306a36Sopenharmony_ci rc = -EFAULT; 173862306a36Sopenharmony_ci goto out_err; 173962306a36Sopenharmony_ci } 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci if (ksmbd_conn_need_reconnect(conn)) { 174262306a36Sopenharmony_ci rc = -EFAULT; 174362306a36Sopenharmony_ci sess = NULL; 174462306a36Sopenharmony_ci goto out_err; 174562306a36Sopenharmony_ci } 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci if (ksmbd_session_lookup(conn, sess_id)) { 174862306a36Sopenharmony_ci rc = -EACCES; 174962306a36Sopenharmony_ci goto out_err; 175062306a36Sopenharmony_ci } 175162306a36Sopenharmony_ci 175262306a36Sopenharmony_ci if (user_guest(sess->user)) { 175362306a36Sopenharmony_ci rc = -EOPNOTSUPP; 175462306a36Sopenharmony_ci goto out_err; 175562306a36Sopenharmony_ci } 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci conn->binding = true; 175862306a36Sopenharmony_ci } else if ((conn->dialect < SMB30_PROT_ID || 175962306a36Sopenharmony_ci server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) && 176062306a36Sopenharmony_ci (req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) { 176162306a36Sopenharmony_ci sess = NULL; 176262306a36Sopenharmony_ci rc = -EACCES; 176362306a36Sopenharmony_ci goto out_err; 176462306a36Sopenharmony_ci } else { 176562306a36Sopenharmony_ci sess = ksmbd_session_lookup(conn, 176662306a36Sopenharmony_ci le64_to_cpu(req->hdr.SessionId)); 176762306a36Sopenharmony_ci if (!sess) { 176862306a36Sopenharmony_ci rc = -ENOENT; 176962306a36Sopenharmony_ci goto out_err; 177062306a36Sopenharmony_ci } 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_ci if (sess->state == SMB2_SESSION_EXPIRED) { 177362306a36Sopenharmony_ci rc = -EFAULT; 177462306a36Sopenharmony_ci goto out_err; 177562306a36Sopenharmony_ci } 177662306a36Sopenharmony_ci 177762306a36Sopenharmony_ci if (ksmbd_conn_need_reconnect(conn)) { 177862306a36Sopenharmony_ci rc = -EFAULT; 177962306a36Sopenharmony_ci sess = NULL; 178062306a36Sopenharmony_ci goto out_err; 178162306a36Sopenharmony_ci } 178262306a36Sopenharmony_ci } 178362306a36Sopenharmony_ci work->sess = sess; 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_ci negblob_off = le16_to_cpu(req->SecurityBufferOffset); 178662306a36Sopenharmony_ci negblob_len = le16_to_cpu(req->SecurityBufferLength); 178762306a36Sopenharmony_ci if (negblob_off < offsetof(struct smb2_sess_setup_req, Buffer)) { 178862306a36Sopenharmony_ci rc = -EINVAL; 178962306a36Sopenharmony_ci goto out_err; 179062306a36Sopenharmony_ci } 179162306a36Sopenharmony_ci 179262306a36Sopenharmony_ci negblob = (struct negotiate_message *)((char *)&req->hdr.ProtocolId + 179362306a36Sopenharmony_ci negblob_off); 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci if (decode_negotiation_token(conn, negblob, negblob_len) == 0) { 179662306a36Sopenharmony_ci if (conn->mechToken) { 179762306a36Sopenharmony_ci negblob = (struct negotiate_message *)conn->mechToken; 179862306a36Sopenharmony_ci negblob_len = conn->mechTokenLen; 179962306a36Sopenharmony_ci } 180062306a36Sopenharmony_ci } 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_ci if (negblob_len < offsetof(struct negotiate_message, NegotiateFlags)) { 180362306a36Sopenharmony_ci rc = -EINVAL; 180462306a36Sopenharmony_ci goto out_err; 180562306a36Sopenharmony_ci } 180662306a36Sopenharmony_ci 180762306a36Sopenharmony_ci if (server_conf.auth_mechs & conn->auth_mechs) { 180862306a36Sopenharmony_ci rc = generate_preauth_hash(work); 180962306a36Sopenharmony_ci if (rc) 181062306a36Sopenharmony_ci goto out_err; 181162306a36Sopenharmony_ci 181262306a36Sopenharmony_ci if (conn->preferred_auth_mech & 181362306a36Sopenharmony_ci (KSMBD_AUTH_KRB5 | KSMBD_AUTH_MSKRB5)) { 181462306a36Sopenharmony_ci rc = krb5_authenticate(work, req, rsp); 181562306a36Sopenharmony_ci if (rc) { 181662306a36Sopenharmony_ci rc = -EINVAL; 181762306a36Sopenharmony_ci goto out_err; 181862306a36Sopenharmony_ci } 181962306a36Sopenharmony_ci 182062306a36Sopenharmony_ci if (!ksmbd_conn_need_reconnect(conn)) { 182162306a36Sopenharmony_ci ksmbd_conn_set_good(conn); 182262306a36Sopenharmony_ci sess->state = SMB2_SESSION_VALID; 182362306a36Sopenharmony_ci } 182462306a36Sopenharmony_ci kfree(sess->Preauth_HashValue); 182562306a36Sopenharmony_ci sess->Preauth_HashValue = NULL; 182662306a36Sopenharmony_ci } else if (conn->preferred_auth_mech == KSMBD_AUTH_NTLMSSP) { 182762306a36Sopenharmony_ci if (negblob->MessageType == NtLmNegotiate) { 182862306a36Sopenharmony_ci rc = ntlm_negotiate(work, negblob, negblob_len, rsp); 182962306a36Sopenharmony_ci if (rc) 183062306a36Sopenharmony_ci goto out_err; 183162306a36Sopenharmony_ci rsp->hdr.Status = 183262306a36Sopenharmony_ci STATUS_MORE_PROCESSING_REQUIRED; 183362306a36Sopenharmony_ci } else if (negblob->MessageType == NtLmAuthenticate) { 183462306a36Sopenharmony_ci rc = ntlm_authenticate(work, req, rsp); 183562306a36Sopenharmony_ci if (rc) 183662306a36Sopenharmony_ci goto out_err; 183762306a36Sopenharmony_ci 183862306a36Sopenharmony_ci if (!ksmbd_conn_need_reconnect(conn)) { 183962306a36Sopenharmony_ci ksmbd_conn_set_good(conn); 184062306a36Sopenharmony_ci sess->state = SMB2_SESSION_VALID; 184162306a36Sopenharmony_ci } 184262306a36Sopenharmony_ci if (conn->binding) { 184362306a36Sopenharmony_ci struct preauth_session *preauth_sess; 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_ci preauth_sess = 184662306a36Sopenharmony_ci ksmbd_preauth_session_lookup(conn, sess->id); 184762306a36Sopenharmony_ci if (preauth_sess) { 184862306a36Sopenharmony_ci list_del(&preauth_sess->preauth_entry); 184962306a36Sopenharmony_ci kfree(preauth_sess); 185062306a36Sopenharmony_ci } 185162306a36Sopenharmony_ci } 185262306a36Sopenharmony_ci kfree(sess->Preauth_HashValue); 185362306a36Sopenharmony_ci sess->Preauth_HashValue = NULL; 185462306a36Sopenharmony_ci } else { 185562306a36Sopenharmony_ci pr_info_ratelimited("Unknown NTLMSSP message type : 0x%x\n", 185662306a36Sopenharmony_ci le32_to_cpu(negblob->MessageType)); 185762306a36Sopenharmony_ci rc = -EINVAL; 185862306a36Sopenharmony_ci } 185962306a36Sopenharmony_ci } else { 186062306a36Sopenharmony_ci /* TODO: need one more negotiation */ 186162306a36Sopenharmony_ci pr_err("Not support the preferred authentication\n"); 186262306a36Sopenharmony_ci rc = -EINVAL; 186362306a36Sopenharmony_ci } 186462306a36Sopenharmony_ci } else { 186562306a36Sopenharmony_ci pr_err("Not support authentication\n"); 186662306a36Sopenharmony_ci rc = -EINVAL; 186762306a36Sopenharmony_ci } 186862306a36Sopenharmony_ci 186962306a36Sopenharmony_ciout_err: 187062306a36Sopenharmony_ci if (rc == -EINVAL) 187162306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_PARAMETER; 187262306a36Sopenharmony_ci else if (rc == -ENOENT) 187362306a36Sopenharmony_ci rsp->hdr.Status = STATUS_USER_SESSION_DELETED; 187462306a36Sopenharmony_ci else if (rc == -EACCES) 187562306a36Sopenharmony_ci rsp->hdr.Status = STATUS_REQUEST_NOT_ACCEPTED; 187662306a36Sopenharmony_ci else if (rc == -EFAULT) 187762306a36Sopenharmony_ci rsp->hdr.Status = STATUS_NETWORK_SESSION_EXPIRED; 187862306a36Sopenharmony_ci else if (rc == -ENOMEM) 187962306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; 188062306a36Sopenharmony_ci else if (rc == -EOPNOTSUPP) 188162306a36Sopenharmony_ci rsp->hdr.Status = STATUS_NOT_SUPPORTED; 188262306a36Sopenharmony_ci else if (rc) 188362306a36Sopenharmony_ci rsp->hdr.Status = STATUS_LOGON_FAILURE; 188462306a36Sopenharmony_ci 188562306a36Sopenharmony_ci if (conn->use_spnego && conn->mechToken) { 188662306a36Sopenharmony_ci kfree(conn->mechToken); 188762306a36Sopenharmony_ci conn->mechToken = NULL; 188862306a36Sopenharmony_ci } 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_ci if (rc < 0) { 189162306a36Sopenharmony_ci /* 189262306a36Sopenharmony_ci * SecurityBufferOffset should be set to zero 189362306a36Sopenharmony_ci * in session setup error response. 189462306a36Sopenharmony_ci */ 189562306a36Sopenharmony_ci rsp->SecurityBufferOffset = 0; 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_ci if (sess) { 189862306a36Sopenharmony_ci bool try_delay = false; 189962306a36Sopenharmony_ci 190062306a36Sopenharmony_ci /* 190162306a36Sopenharmony_ci * To avoid dictionary attacks (repeated session setups rapidly sent) to 190262306a36Sopenharmony_ci * connect to server, ksmbd make a delay of a 5 seconds on session setup 190362306a36Sopenharmony_ci * failure to make it harder to send enough random connection requests 190462306a36Sopenharmony_ci * to break into a server. 190562306a36Sopenharmony_ci */ 190662306a36Sopenharmony_ci if (sess->user && sess->user->flags & KSMBD_USER_FLAG_DELAY_SESSION) 190762306a36Sopenharmony_ci try_delay = true; 190862306a36Sopenharmony_ci 190962306a36Sopenharmony_ci sess->last_active = jiffies; 191062306a36Sopenharmony_ci sess->state = SMB2_SESSION_EXPIRED; 191162306a36Sopenharmony_ci if (try_delay) { 191262306a36Sopenharmony_ci ksmbd_conn_set_need_reconnect(conn); 191362306a36Sopenharmony_ci ssleep(5); 191462306a36Sopenharmony_ci ksmbd_conn_set_need_negotiate(conn); 191562306a36Sopenharmony_ci } 191662306a36Sopenharmony_ci } 191762306a36Sopenharmony_ci smb2_set_err_rsp(work); 191862306a36Sopenharmony_ci } else { 191962306a36Sopenharmony_ci unsigned int iov_len; 192062306a36Sopenharmony_ci 192162306a36Sopenharmony_ci if (rsp->SecurityBufferLength) 192262306a36Sopenharmony_ci iov_len = offsetof(struct smb2_sess_setup_rsp, Buffer) + 192362306a36Sopenharmony_ci le16_to_cpu(rsp->SecurityBufferLength); 192462306a36Sopenharmony_ci else 192562306a36Sopenharmony_ci iov_len = sizeof(struct smb2_sess_setup_rsp); 192662306a36Sopenharmony_ci rc = ksmbd_iov_pin_rsp(work, rsp, iov_len); 192762306a36Sopenharmony_ci if (rc) 192862306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; 192962306a36Sopenharmony_ci } 193062306a36Sopenharmony_ci 193162306a36Sopenharmony_ci ksmbd_conn_unlock(conn); 193262306a36Sopenharmony_ci return rc; 193362306a36Sopenharmony_ci} 193462306a36Sopenharmony_ci 193562306a36Sopenharmony_ci/** 193662306a36Sopenharmony_ci * smb2_tree_connect() - handler for smb2 tree connect command 193762306a36Sopenharmony_ci * @work: smb work containing smb request buffer 193862306a36Sopenharmony_ci * 193962306a36Sopenharmony_ci * Return: 0 on success, otherwise error 194062306a36Sopenharmony_ci */ 194162306a36Sopenharmony_ciint smb2_tree_connect(struct ksmbd_work *work) 194262306a36Sopenharmony_ci{ 194362306a36Sopenharmony_ci struct ksmbd_conn *conn = work->conn; 194462306a36Sopenharmony_ci struct smb2_tree_connect_req *req; 194562306a36Sopenharmony_ci struct smb2_tree_connect_rsp *rsp; 194662306a36Sopenharmony_ci struct ksmbd_session *sess = work->sess; 194762306a36Sopenharmony_ci char *treename = NULL, *name = NULL; 194862306a36Sopenharmony_ci struct ksmbd_tree_conn_status status; 194962306a36Sopenharmony_ci struct ksmbd_share_config *share; 195062306a36Sopenharmony_ci int rc = -EINVAL; 195162306a36Sopenharmony_ci 195262306a36Sopenharmony_ci WORK_BUFFERS(work, req, rsp); 195362306a36Sopenharmony_ci 195462306a36Sopenharmony_ci treename = smb_strndup_from_utf16(req->Buffer, 195562306a36Sopenharmony_ci le16_to_cpu(req->PathLength), true, 195662306a36Sopenharmony_ci conn->local_nls); 195762306a36Sopenharmony_ci if (IS_ERR(treename)) { 195862306a36Sopenharmony_ci pr_err("treename is NULL\n"); 195962306a36Sopenharmony_ci status.ret = KSMBD_TREE_CONN_STATUS_ERROR; 196062306a36Sopenharmony_ci goto out_err1; 196162306a36Sopenharmony_ci } 196262306a36Sopenharmony_ci 196362306a36Sopenharmony_ci name = ksmbd_extract_sharename(conn->um, treename); 196462306a36Sopenharmony_ci if (IS_ERR(name)) { 196562306a36Sopenharmony_ci status.ret = KSMBD_TREE_CONN_STATUS_ERROR; 196662306a36Sopenharmony_ci goto out_err1; 196762306a36Sopenharmony_ci } 196862306a36Sopenharmony_ci 196962306a36Sopenharmony_ci ksmbd_debug(SMB, "tree connect request for tree %s treename %s\n", 197062306a36Sopenharmony_ci name, treename); 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_ci status = ksmbd_tree_conn_connect(conn, sess, name); 197362306a36Sopenharmony_ci if (status.ret == KSMBD_TREE_CONN_STATUS_OK) 197462306a36Sopenharmony_ci rsp->hdr.Id.SyncId.TreeId = cpu_to_le32(status.tree_conn->id); 197562306a36Sopenharmony_ci else 197662306a36Sopenharmony_ci goto out_err1; 197762306a36Sopenharmony_ci 197862306a36Sopenharmony_ci share = status.tree_conn->share_conf; 197962306a36Sopenharmony_ci if (test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) { 198062306a36Sopenharmony_ci ksmbd_debug(SMB, "IPC share path request\n"); 198162306a36Sopenharmony_ci rsp->ShareType = SMB2_SHARE_TYPE_PIPE; 198262306a36Sopenharmony_ci rsp->MaximalAccess = FILE_READ_DATA_LE | FILE_READ_EA_LE | 198362306a36Sopenharmony_ci FILE_EXECUTE_LE | FILE_READ_ATTRIBUTES_LE | 198462306a36Sopenharmony_ci FILE_DELETE_LE | FILE_READ_CONTROL_LE | 198562306a36Sopenharmony_ci FILE_WRITE_DAC_LE | FILE_WRITE_OWNER_LE | 198662306a36Sopenharmony_ci FILE_SYNCHRONIZE_LE; 198762306a36Sopenharmony_ci } else { 198862306a36Sopenharmony_ci rsp->ShareType = SMB2_SHARE_TYPE_DISK; 198962306a36Sopenharmony_ci rsp->MaximalAccess = FILE_READ_DATA_LE | FILE_READ_EA_LE | 199062306a36Sopenharmony_ci FILE_EXECUTE_LE | FILE_READ_ATTRIBUTES_LE; 199162306a36Sopenharmony_ci if (test_tree_conn_flag(status.tree_conn, 199262306a36Sopenharmony_ci KSMBD_TREE_CONN_FLAG_WRITABLE)) { 199362306a36Sopenharmony_ci rsp->MaximalAccess |= FILE_WRITE_DATA_LE | 199462306a36Sopenharmony_ci FILE_APPEND_DATA_LE | FILE_WRITE_EA_LE | 199562306a36Sopenharmony_ci FILE_DELETE_LE | FILE_WRITE_ATTRIBUTES_LE | 199662306a36Sopenharmony_ci FILE_DELETE_CHILD_LE | FILE_READ_CONTROL_LE | 199762306a36Sopenharmony_ci FILE_WRITE_DAC_LE | FILE_WRITE_OWNER_LE | 199862306a36Sopenharmony_ci FILE_SYNCHRONIZE_LE; 199962306a36Sopenharmony_ci } 200062306a36Sopenharmony_ci } 200162306a36Sopenharmony_ci 200262306a36Sopenharmony_ci status.tree_conn->maximal_access = le32_to_cpu(rsp->MaximalAccess); 200362306a36Sopenharmony_ci if (conn->posix_ext_supported) 200462306a36Sopenharmony_ci status.tree_conn->posix_extensions = true; 200562306a36Sopenharmony_ci 200662306a36Sopenharmony_ci write_lock(&sess->tree_conns_lock); 200762306a36Sopenharmony_ci status.tree_conn->t_state = TREE_CONNECTED; 200862306a36Sopenharmony_ci write_unlock(&sess->tree_conns_lock); 200962306a36Sopenharmony_ci rsp->StructureSize = cpu_to_le16(16); 201062306a36Sopenharmony_ciout_err1: 201162306a36Sopenharmony_ci rsp->Capabilities = 0; 201262306a36Sopenharmony_ci rsp->Reserved = 0; 201362306a36Sopenharmony_ci /* default manual caching */ 201462306a36Sopenharmony_ci rsp->ShareFlags = SMB2_SHAREFLAG_MANUAL_CACHING; 201562306a36Sopenharmony_ci 201662306a36Sopenharmony_ci rc = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_tree_connect_rsp)); 201762306a36Sopenharmony_ci if (rc) 201862306a36Sopenharmony_ci status.ret = KSMBD_TREE_CONN_STATUS_NOMEM; 201962306a36Sopenharmony_ci 202062306a36Sopenharmony_ci if (!IS_ERR(treename)) 202162306a36Sopenharmony_ci kfree(treename); 202262306a36Sopenharmony_ci if (!IS_ERR(name)) 202362306a36Sopenharmony_ci kfree(name); 202462306a36Sopenharmony_ci 202562306a36Sopenharmony_ci switch (status.ret) { 202662306a36Sopenharmony_ci case KSMBD_TREE_CONN_STATUS_OK: 202762306a36Sopenharmony_ci rsp->hdr.Status = STATUS_SUCCESS; 202862306a36Sopenharmony_ci rc = 0; 202962306a36Sopenharmony_ci break; 203062306a36Sopenharmony_ci case -ESTALE: 203162306a36Sopenharmony_ci case -ENOENT: 203262306a36Sopenharmony_ci case KSMBD_TREE_CONN_STATUS_NO_SHARE: 203362306a36Sopenharmony_ci rsp->hdr.Status = STATUS_BAD_NETWORK_NAME; 203462306a36Sopenharmony_ci break; 203562306a36Sopenharmony_ci case -ENOMEM: 203662306a36Sopenharmony_ci case KSMBD_TREE_CONN_STATUS_NOMEM: 203762306a36Sopenharmony_ci rsp->hdr.Status = STATUS_NO_MEMORY; 203862306a36Sopenharmony_ci break; 203962306a36Sopenharmony_ci case KSMBD_TREE_CONN_STATUS_ERROR: 204062306a36Sopenharmony_ci case KSMBD_TREE_CONN_STATUS_TOO_MANY_CONNS: 204162306a36Sopenharmony_ci case KSMBD_TREE_CONN_STATUS_TOO_MANY_SESSIONS: 204262306a36Sopenharmony_ci rsp->hdr.Status = STATUS_ACCESS_DENIED; 204362306a36Sopenharmony_ci break; 204462306a36Sopenharmony_ci case -EINVAL: 204562306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_PARAMETER; 204662306a36Sopenharmony_ci break; 204762306a36Sopenharmony_ci default: 204862306a36Sopenharmony_ci rsp->hdr.Status = STATUS_ACCESS_DENIED; 204962306a36Sopenharmony_ci } 205062306a36Sopenharmony_ci 205162306a36Sopenharmony_ci if (status.ret != KSMBD_TREE_CONN_STATUS_OK) 205262306a36Sopenharmony_ci smb2_set_err_rsp(work); 205362306a36Sopenharmony_ci 205462306a36Sopenharmony_ci return rc; 205562306a36Sopenharmony_ci} 205662306a36Sopenharmony_ci 205762306a36Sopenharmony_ci/** 205862306a36Sopenharmony_ci * smb2_create_open_flags() - convert smb open flags to unix open flags 205962306a36Sopenharmony_ci * @file_present: is file already present 206062306a36Sopenharmony_ci * @access: file access flags 206162306a36Sopenharmony_ci * @disposition: file disposition flags 206262306a36Sopenharmony_ci * @may_flags: set with MAY_ flags 206362306a36Sopenharmony_ci * 206462306a36Sopenharmony_ci * Return: file open flags 206562306a36Sopenharmony_ci */ 206662306a36Sopenharmony_cistatic int smb2_create_open_flags(bool file_present, __le32 access, 206762306a36Sopenharmony_ci __le32 disposition, 206862306a36Sopenharmony_ci int *may_flags) 206962306a36Sopenharmony_ci{ 207062306a36Sopenharmony_ci int oflags = O_NONBLOCK | O_LARGEFILE; 207162306a36Sopenharmony_ci 207262306a36Sopenharmony_ci if (access & FILE_READ_DESIRED_ACCESS_LE && 207362306a36Sopenharmony_ci access & FILE_WRITE_DESIRE_ACCESS_LE) { 207462306a36Sopenharmony_ci oflags |= O_RDWR; 207562306a36Sopenharmony_ci *may_flags = MAY_OPEN | MAY_READ | MAY_WRITE; 207662306a36Sopenharmony_ci } else if (access & FILE_WRITE_DESIRE_ACCESS_LE) { 207762306a36Sopenharmony_ci oflags |= O_WRONLY; 207862306a36Sopenharmony_ci *may_flags = MAY_OPEN | MAY_WRITE; 207962306a36Sopenharmony_ci } else { 208062306a36Sopenharmony_ci oflags |= O_RDONLY; 208162306a36Sopenharmony_ci *may_flags = MAY_OPEN | MAY_READ; 208262306a36Sopenharmony_ci } 208362306a36Sopenharmony_ci 208462306a36Sopenharmony_ci if (access == FILE_READ_ATTRIBUTES_LE) 208562306a36Sopenharmony_ci oflags |= O_PATH; 208662306a36Sopenharmony_ci 208762306a36Sopenharmony_ci if (file_present) { 208862306a36Sopenharmony_ci switch (disposition & FILE_CREATE_MASK_LE) { 208962306a36Sopenharmony_ci case FILE_OPEN_LE: 209062306a36Sopenharmony_ci case FILE_CREATE_LE: 209162306a36Sopenharmony_ci break; 209262306a36Sopenharmony_ci case FILE_SUPERSEDE_LE: 209362306a36Sopenharmony_ci case FILE_OVERWRITE_LE: 209462306a36Sopenharmony_ci case FILE_OVERWRITE_IF_LE: 209562306a36Sopenharmony_ci oflags |= O_TRUNC; 209662306a36Sopenharmony_ci break; 209762306a36Sopenharmony_ci default: 209862306a36Sopenharmony_ci break; 209962306a36Sopenharmony_ci } 210062306a36Sopenharmony_ci } else { 210162306a36Sopenharmony_ci switch (disposition & FILE_CREATE_MASK_LE) { 210262306a36Sopenharmony_ci case FILE_SUPERSEDE_LE: 210362306a36Sopenharmony_ci case FILE_CREATE_LE: 210462306a36Sopenharmony_ci case FILE_OPEN_IF_LE: 210562306a36Sopenharmony_ci case FILE_OVERWRITE_IF_LE: 210662306a36Sopenharmony_ci oflags |= O_CREAT; 210762306a36Sopenharmony_ci break; 210862306a36Sopenharmony_ci case FILE_OPEN_LE: 210962306a36Sopenharmony_ci case FILE_OVERWRITE_LE: 211062306a36Sopenharmony_ci oflags &= ~O_CREAT; 211162306a36Sopenharmony_ci break; 211262306a36Sopenharmony_ci default: 211362306a36Sopenharmony_ci break; 211462306a36Sopenharmony_ci } 211562306a36Sopenharmony_ci } 211662306a36Sopenharmony_ci 211762306a36Sopenharmony_ci return oflags; 211862306a36Sopenharmony_ci} 211962306a36Sopenharmony_ci 212062306a36Sopenharmony_ci/** 212162306a36Sopenharmony_ci * smb2_tree_disconnect() - handler for smb tree connect request 212262306a36Sopenharmony_ci * @work: smb work containing request buffer 212362306a36Sopenharmony_ci * 212462306a36Sopenharmony_ci * Return: 0 212562306a36Sopenharmony_ci */ 212662306a36Sopenharmony_ciint smb2_tree_disconnect(struct ksmbd_work *work) 212762306a36Sopenharmony_ci{ 212862306a36Sopenharmony_ci struct smb2_tree_disconnect_rsp *rsp; 212962306a36Sopenharmony_ci struct smb2_tree_disconnect_req *req; 213062306a36Sopenharmony_ci struct ksmbd_session *sess = work->sess; 213162306a36Sopenharmony_ci struct ksmbd_tree_connect *tcon = work->tcon; 213262306a36Sopenharmony_ci int err; 213362306a36Sopenharmony_ci 213462306a36Sopenharmony_ci WORK_BUFFERS(work, req, rsp); 213562306a36Sopenharmony_ci 213662306a36Sopenharmony_ci ksmbd_debug(SMB, "request\n"); 213762306a36Sopenharmony_ci 213862306a36Sopenharmony_ci if (!tcon) { 213962306a36Sopenharmony_ci ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); 214062306a36Sopenharmony_ci 214162306a36Sopenharmony_ci rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; 214262306a36Sopenharmony_ci err = -ENOENT; 214362306a36Sopenharmony_ci goto err_out; 214462306a36Sopenharmony_ci } 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_ci ksmbd_close_tree_conn_fds(work); 214762306a36Sopenharmony_ci 214862306a36Sopenharmony_ci write_lock(&sess->tree_conns_lock); 214962306a36Sopenharmony_ci if (tcon->t_state == TREE_DISCONNECTED) { 215062306a36Sopenharmony_ci write_unlock(&sess->tree_conns_lock); 215162306a36Sopenharmony_ci rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; 215262306a36Sopenharmony_ci err = -ENOENT; 215362306a36Sopenharmony_ci goto err_out; 215462306a36Sopenharmony_ci } 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_ci WARN_ON_ONCE(atomic_dec_and_test(&tcon->refcount)); 215762306a36Sopenharmony_ci tcon->t_state = TREE_DISCONNECTED; 215862306a36Sopenharmony_ci write_unlock(&sess->tree_conns_lock); 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_ci err = ksmbd_tree_conn_disconnect(sess, tcon); 216162306a36Sopenharmony_ci if (err) { 216262306a36Sopenharmony_ci rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; 216362306a36Sopenharmony_ci goto err_out; 216462306a36Sopenharmony_ci } 216562306a36Sopenharmony_ci 216662306a36Sopenharmony_ci work->tcon = NULL; 216762306a36Sopenharmony_ci 216862306a36Sopenharmony_ci rsp->StructureSize = cpu_to_le16(4); 216962306a36Sopenharmony_ci err = ksmbd_iov_pin_rsp(work, rsp, 217062306a36Sopenharmony_ci sizeof(struct smb2_tree_disconnect_rsp)); 217162306a36Sopenharmony_ci if (err) { 217262306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; 217362306a36Sopenharmony_ci goto err_out; 217462306a36Sopenharmony_ci } 217562306a36Sopenharmony_ci 217662306a36Sopenharmony_ci return 0; 217762306a36Sopenharmony_ci 217862306a36Sopenharmony_cierr_out: 217962306a36Sopenharmony_ci smb2_set_err_rsp(work); 218062306a36Sopenharmony_ci return err; 218162306a36Sopenharmony_ci 218262306a36Sopenharmony_ci} 218362306a36Sopenharmony_ci 218462306a36Sopenharmony_ci/** 218562306a36Sopenharmony_ci * smb2_session_logoff() - handler for session log off request 218662306a36Sopenharmony_ci * @work: smb work containing request buffer 218762306a36Sopenharmony_ci * 218862306a36Sopenharmony_ci * Return: 0 218962306a36Sopenharmony_ci */ 219062306a36Sopenharmony_ciint smb2_session_logoff(struct ksmbd_work *work) 219162306a36Sopenharmony_ci{ 219262306a36Sopenharmony_ci struct ksmbd_conn *conn = work->conn; 219362306a36Sopenharmony_ci struct smb2_logoff_req *req; 219462306a36Sopenharmony_ci struct smb2_logoff_rsp *rsp; 219562306a36Sopenharmony_ci struct ksmbd_session *sess; 219662306a36Sopenharmony_ci u64 sess_id; 219762306a36Sopenharmony_ci int err; 219862306a36Sopenharmony_ci 219962306a36Sopenharmony_ci WORK_BUFFERS(work, req, rsp); 220062306a36Sopenharmony_ci 220162306a36Sopenharmony_ci ksmbd_debug(SMB, "request\n"); 220262306a36Sopenharmony_ci 220362306a36Sopenharmony_ci ksmbd_conn_lock(conn); 220462306a36Sopenharmony_ci if (!ksmbd_conn_good(conn)) { 220562306a36Sopenharmony_ci ksmbd_conn_unlock(conn); 220662306a36Sopenharmony_ci rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; 220762306a36Sopenharmony_ci smb2_set_err_rsp(work); 220862306a36Sopenharmony_ci return -ENOENT; 220962306a36Sopenharmony_ci } 221062306a36Sopenharmony_ci sess_id = le64_to_cpu(req->hdr.SessionId); 221162306a36Sopenharmony_ci ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_RECONNECT); 221262306a36Sopenharmony_ci ksmbd_conn_unlock(conn); 221362306a36Sopenharmony_ci 221462306a36Sopenharmony_ci ksmbd_close_session_fds(work); 221562306a36Sopenharmony_ci ksmbd_conn_wait_idle(conn, sess_id); 221662306a36Sopenharmony_ci 221762306a36Sopenharmony_ci /* 221862306a36Sopenharmony_ci * Re-lookup session to validate if session is deleted 221962306a36Sopenharmony_ci * while waiting request complete 222062306a36Sopenharmony_ci */ 222162306a36Sopenharmony_ci sess = ksmbd_session_lookup_all(conn, sess_id); 222262306a36Sopenharmony_ci if (ksmbd_tree_conn_session_logoff(sess)) { 222362306a36Sopenharmony_ci ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); 222462306a36Sopenharmony_ci rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; 222562306a36Sopenharmony_ci smb2_set_err_rsp(work); 222662306a36Sopenharmony_ci return -ENOENT; 222762306a36Sopenharmony_ci } 222862306a36Sopenharmony_ci 222962306a36Sopenharmony_ci ksmbd_destroy_file_table(&sess->file_table); 223062306a36Sopenharmony_ci sess->state = SMB2_SESSION_EXPIRED; 223162306a36Sopenharmony_ci 223262306a36Sopenharmony_ci ksmbd_free_user(sess->user); 223362306a36Sopenharmony_ci sess->user = NULL; 223462306a36Sopenharmony_ci ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_NEGOTIATE); 223562306a36Sopenharmony_ci 223662306a36Sopenharmony_ci rsp->StructureSize = cpu_to_le16(4); 223762306a36Sopenharmony_ci err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_logoff_rsp)); 223862306a36Sopenharmony_ci if (err) { 223962306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; 224062306a36Sopenharmony_ci smb2_set_err_rsp(work); 224162306a36Sopenharmony_ci return err; 224262306a36Sopenharmony_ci } 224362306a36Sopenharmony_ci return 0; 224462306a36Sopenharmony_ci} 224562306a36Sopenharmony_ci 224662306a36Sopenharmony_ci/** 224762306a36Sopenharmony_ci * create_smb2_pipe() - create IPC pipe 224862306a36Sopenharmony_ci * @work: smb work containing request buffer 224962306a36Sopenharmony_ci * 225062306a36Sopenharmony_ci * Return: 0 on success, otherwise error 225162306a36Sopenharmony_ci */ 225262306a36Sopenharmony_cistatic noinline int create_smb2_pipe(struct ksmbd_work *work) 225362306a36Sopenharmony_ci{ 225462306a36Sopenharmony_ci struct smb2_create_rsp *rsp; 225562306a36Sopenharmony_ci struct smb2_create_req *req; 225662306a36Sopenharmony_ci int id; 225762306a36Sopenharmony_ci int err; 225862306a36Sopenharmony_ci char *name; 225962306a36Sopenharmony_ci 226062306a36Sopenharmony_ci WORK_BUFFERS(work, req, rsp); 226162306a36Sopenharmony_ci 226262306a36Sopenharmony_ci name = smb_strndup_from_utf16(req->Buffer, le16_to_cpu(req->NameLength), 226362306a36Sopenharmony_ci 1, work->conn->local_nls); 226462306a36Sopenharmony_ci if (IS_ERR(name)) { 226562306a36Sopenharmony_ci rsp->hdr.Status = STATUS_NO_MEMORY; 226662306a36Sopenharmony_ci err = PTR_ERR(name); 226762306a36Sopenharmony_ci goto out; 226862306a36Sopenharmony_ci } 226962306a36Sopenharmony_ci 227062306a36Sopenharmony_ci id = ksmbd_session_rpc_open(work->sess, name); 227162306a36Sopenharmony_ci if (id < 0) { 227262306a36Sopenharmony_ci pr_err("Unable to open RPC pipe: %d\n", id); 227362306a36Sopenharmony_ci err = id; 227462306a36Sopenharmony_ci goto out; 227562306a36Sopenharmony_ci } 227662306a36Sopenharmony_ci 227762306a36Sopenharmony_ci rsp->hdr.Status = STATUS_SUCCESS; 227862306a36Sopenharmony_ci rsp->StructureSize = cpu_to_le16(89); 227962306a36Sopenharmony_ci rsp->OplockLevel = SMB2_OPLOCK_LEVEL_NONE; 228062306a36Sopenharmony_ci rsp->Flags = 0; 228162306a36Sopenharmony_ci rsp->CreateAction = cpu_to_le32(FILE_OPENED); 228262306a36Sopenharmony_ci 228362306a36Sopenharmony_ci rsp->CreationTime = cpu_to_le64(0); 228462306a36Sopenharmony_ci rsp->LastAccessTime = cpu_to_le64(0); 228562306a36Sopenharmony_ci rsp->ChangeTime = cpu_to_le64(0); 228662306a36Sopenharmony_ci rsp->AllocationSize = cpu_to_le64(0); 228762306a36Sopenharmony_ci rsp->EndofFile = cpu_to_le64(0); 228862306a36Sopenharmony_ci rsp->FileAttributes = FILE_ATTRIBUTE_NORMAL_LE; 228962306a36Sopenharmony_ci rsp->Reserved2 = 0; 229062306a36Sopenharmony_ci rsp->VolatileFileId = id; 229162306a36Sopenharmony_ci rsp->PersistentFileId = 0; 229262306a36Sopenharmony_ci rsp->CreateContextsOffset = 0; 229362306a36Sopenharmony_ci rsp->CreateContextsLength = 0; 229462306a36Sopenharmony_ci 229562306a36Sopenharmony_ci err = ksmbd_iov_pin_rsp(work, rsp, offsetof(struct smb2_create_rsp, Buffer)); 229662306a36Sopenharmony_ci if (err) 229762306a36Sopenharmony_ci goto out; 229862306a36Sopenharmony_ci 229962306a36Sopenharmony_ci kfree(name); 230062306a36Sopenharmony_ci return 0; 230162306a36Sopenharmony_ci 230262306a36Sopenharmony_ciout: 230362306a36Sopenharmony_ci switch (err) { 230462306a36Sopenharmony_ci case -EINVAL: 230562306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_PARAMETER; 230662306a36Sopenharmony_ci break; 230762306a36Sopenharmony_ci case -ENOSPC: 230862306a36Sopenharmony_ci case -ENOMEM: 230962306a36Sopenharmony_ci rsp->hdr.Status = STATUS_NO_MEMORY; 231062306a36Sopenharmony_ci break; 231162306a36Sopenharmony_ci } 231262306a36Sopenharmony_ci 231362306a36Sopenharmony_ci if (!IS_ERR(name)) 231462306a36Sopenharmony_ci kfree(name); 231562306a36Sopenharmony_ci 231662306a36Sopenharmony_ci smb2_set_err_rsp(work); 231762306a36Sopenharmony_ci return err; 231862306a36Sopenharmony_ci} 231962306a36Sopenharmony_ci 232062306a36Sopenharmony_ci/** 232162306a36Sopenharmony_ci * smb2_set_ea() - handler for setting extended attributes using set 232262306a36Sopenharmony_ci * info command 232362306a36Sopenharmony_ci * @eabuf: set info command buffer 232462306a36Sopenharmony_ci * @buf_len: set info command buffer length 232562306a36Sopenharmony_ci * @path: dentry path for get ea 232662306a36Sopenharmony_ci * @get_write: get write access to a mount 232762306a36Sopenharmony_ci * 232862306a36Sopenharmony_ci * Return: 0 on success, otherwise error 232962306a36Sopenharmony_ci */ 233062306a36Sopenharmony_cistatic int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len, 233162306a36Sopenharmony_ci const struct path *path, bool get_write) 233262306a36Sopenharmony_ci{ 233362306a36Sopenharmony_ci struct mnt_idmap *idmap = mnt_idmap(path->mnt); 233462306a36Sopenharmony_ci char *attr_name = NULL, *value; 233562306a36Sopenharmony_ci int rc = 0; 233662306a36Sopenharmony_ci unsigned int next = 0; 233762306a36Sopenharmony_ci 233862306a36Sopenharmony_ci if (buf_len < sizeof(struct smb2_ea_info) + eabuf->EaNameLength + 233962306a36Sopenharmony_ci le16_to_cpu(eabuf->EaValueLength)) 234062306a36Sopenharmony_ci return -EINVAL; 234162306a36Sopenharmony_ci 234262306a36Sopenharmony_ci attr_name = kmalloc(XATTR_NAME_MAX + 1, GFP_KERNEL); 234362306a36Sopenharmony_ci if (!attr_name) 234462306a36Sopenharmony_ci return -ENOMEM; 234562306a36Sopenharmony_ci 234662306a36Sopenharmony_ci do { 234762306a36Sopenharmony_ci if (!eabuf->EaNameLength) 234862306a36Sopenharmony_ci goto next; 234962306a36Sopenharmony_ci 235062306a36Sopenharmony_ci ksmbd_debug(SMB, 235162306a36Sopenharmony_ci "name : <%s>, name_len : %u, value_len : %u, next : %u\n", 235262306a36Sopenharmony_ci eabuf->name, eabuf->EaNameLength, 235362306a36Sopenharmony_ci le16_to_cpu(eabuf->EaValueLength), 235462306a36Sopenharmony_ci le32_to_cpu(eabuf->NextEntryOffset)); 235562306a36Sopenharmony_ci 235662306a36Sopenharmony_ci if (eabuf->EaNameLength > 235762306a36Sopenharmony_ci (XATTR_NAME_MAX - XATTR_USER_PREFIX_LEN)) { 235862306a36Sopenharmony_ci rc = -EINVAL; 235962306a36Sopenharmony_ci break; 236062306a36Sopenharmony_ci } 236162306a36Sopenharmony_ci 236262306a36Sopenharmony_ci memcpy(attr_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN); 236362306a36Sopenharmony_ci memcpy(&attr_name[XATTR_USER_PREFIX_LEN], eabuf->name, 236462306a36Sopenharmony_ci eabuf->EaNameLength); 236562306a36Sopenharmony_ci attr_name[XATTR_USER_PREFIX_LEN + eabuf->EaNameLength] = '\0'; 236662306a36Sopenharmony_ci value = (char *)&eabuf->name + eabuf->EaNameLength + 1; 236762306a36Sopenharmony_ci 236862306a36Sopenharmony_ci if (!eabuf->EaValueLength) { 236962306a36Sopenharmony_ci rc = ksmbd_vfs_casexattr_len(idmap, 237062306a36Sopenharmony_ci path->dentry, 237162306a36Sopenharmony_ci attr_name, 237262306a36Sopenharmony_ci XATTR_USER_PREFIX_LEN + 237362306a36Sopenharmony_ci eabuf->EaNameLength); 237462306a36Sopenharmony_ci 237562306a36Sopenharmony_ci /* delete the EA only when it exits */ 237662306a36Sopenharmony_ci if (rc > 0) { 237762306a36Sopenharmony_ci rc = ksmbd_vfs_remove_xattr(idmap, 237862306a36Sopenharmony_ci path, 237962306a36Sopenharmony_ci attr_name); 238062306a36Sopenharmony_ci 238162306a36Sopenharmony_ci if (rc < 0) { 238262306a36Sopenharmony_ci ksmbd_debug(SMB, 238362306a36Sopenharmony_ci "remove xattr failed(%d)\n", 238462306a36Sopenharmony_ci rc); 238562306a36Sopenharmony_ci break; 238662306a36Sopenharmony_ci } 238762306a36Sopenharmony_ci } 238862306a36Sopenharmony_ci 238962306a36Sopenharmony_ci /* if the EA doesn't exist, just do nothing. */ 239062306a36Sopenharmony_ci rc = 0; 239162306a36Sopenharmony_ci } else { 239262306a36Sopenharmony_ci rc = ksmbd_vfs_setxattr(idmap, path, attr_name, value, 239362306a36Sopenharmony_ci le16_to_cpu(eabuf->EaValueLength), 239462306a36Sopenharmony_ci 0, true); 239562306a36Sopenharmony_ci if (rc < 0) { 239662306a36Sopenharmony_ci ksmbd_debug(SMB, 239762306a36Sopenharmony_ci "ksmbd_vfs_setxattr is failed(%d)\n", 239862306a36Sopenharmony_ci rc); 239962306a36Sopenharmony_ci break; 240062306a36Sopenharmony_ci } 240162306a36Sopenharmony_ci } 240262306a36Sopenharmony_ci 240362306a36Sopenharmony_cinext: 240462306a36Sopenharmony_ci next = le32_to_cpu(eabuf->NextEntryOffset); 240562306a36Sopenharmony_ci if (next == 0 || buf_len < next) 240662306a36Sopenharmony_ci break; 240762306a36Sopenharmony_ci buf_len -= next; 240862306a36Sopenharmony_ci eabuf = (struct smb2_ea_info *)((char *)eabuf + next); 240962306a36Sopenharmony_ci if (buf_len < sizeof(struct smb2_ea_info)) { 241062306a36Sopenharmony_ci rc = -EINVAL; 241162306a36Sopenharmony_ci break; 241262306a36Sopenharmony_ci } 241362306a36Sopenharmony_ci 241462306a36Sopenharmony_ci if (buf_len < sizeof(struct smb2_ea_info) + eabuf->EaNameLength + 241562306a36Sopenharmony_ci le16_to_cpu(eabuf->EaValueLength)) { 241662306a36Sopenharmony_ci rc = -EINVAL; 241762306a36Sopenharmony_ci break; 241862306a36Sopenharmony_ci } 241962306a36Sopenharmony_ci } while (next != 0); 242062306a36Sopenharmony_ci 242162306a36Sopenharmony_ci kfree(attr_name); 242262306a36Sopenharmony_ci return rc; 242362306a36Sopenharmony_ci} 242462306a36Sopenharmony_ci 242562306a36Sopenharmony_cistatic noinline int smb2_set_stream_name_xattr(const struct path *path, 242662306a36Sopenharmony_ci struct ksmbd_file *fp, 242762306a36Sopenharmony_ci char *stream_name, int s_type) 242862306a36Sopenharmony_ci{ 242962306a36Sopenharmony_ci struct mnt_idmap *idmap = mnt_idmap(path->mnt); 243062306a36Sopenharmony_ci size_t xattr_stream_size; 243162306a36Sopenharmony_ci char *xattr_stream_name; 243262306a36Sopenharmony_ci int rc; 243362306a36Sopenharmony_ci 243462306a36Sopenharmony_ci rc = ksmbd_vfs_xattr_stream_name(stream_name, 243562306a36Sopenharmony_ci &xattr_stream_name, 243662306a36Sopenharmony_ci &xattr_stream_size, 243762306a36Sopenharmony_ci s_type); 243862306a36Sopenharmony_ci if (rc) 243962306a36Sopenharmony_ci return rc; 244062306a36Sopenharmony_ci 244162306a36Sopenharmony_ci fp->stream.name = xattr_stream_name; 244262306a36Sopenharmony_ci fp->stream.size = xattr_stream_size; 244362306a36Sopenharmony_ci 244462306a36Sopenharmony_ci /* Check if there is stream prefix in xattr space */ 244562306a36Sopenharmony_ci rc = ksmbd_vfs_casexattr_len(idmap, 244662306a36Sopenharmony_ci path->dentry, 244762306a36Sopenharmony_ci xattr_stream_name, 244862306a36Sopenharmony_ci xattr_stream_size); 244962306a36Sopenharmony_ci if (rc >= 0) 245062306a36Sopenharmony_ci return 0; 245162306a36Sopenharmony_ci 245262306a36Sopenharmony_ci if (fp->cdoption == FILE_OPEN_LE) { 245362306a36Sopenharmony_ci ksmbd_debug(SMB, "XATTR stream name lookup failed: %d\n", rc); 245462306a36Sopenharmony_ci return -EBADF; 245562306a36Sopenharmony_ci } 245662306a36Sopenharmony_ci 245762306a36Sopenharmony_ci rc = ksmbd_vfs_setxattr(idmap, path, xattr_stream_name, NULL, 0, 0, false); 245862306a36Sopenharmony_ci if (rc < 0) 245962306a36Sopenharmony_ci pr_err("Failed to store XATTR stream name :%d\n", rc); 246062306a36Sopenharmony_ci return 0; 246162306a36Sopenharmony_ci} 246262306a36Sopenharmony_ci 246362306a36Sopenharmony_cistatic int smb2_remove_smb_xattrs(const struct path *path) 246462306a36Sopenharmony_ci{ 246562306a36Sopenharmony_ci struct mnt_idmap *idmap = mnt_idmap(path->mnt); 246662306a36Sopenharmony_ci char *name, *xattr_list = NULL; 246762306a36Sopenharmony_ci ssize_t xattr_list_len; 246862306a36Sopenharmony_ci int err = 0; 246962306a36Sopenharmony_ci 247062306a36Sopenharmony_ci xattr_list_len = ksmbd_vfs_listxattr(path->dentry, &xattr_list); 247162306a36Sopenharmony_ci if (xattr_list_len < 0) { 247262306a36Sopenharmony_ci goto out; 247362306a36Sopenharmony_ci } else if (!xattr_list_len) { 247462306a36Sopenharmony_ci ksmbd_debug(SMB, "empty xattr in the file\n"); 247562306a36Sopenharmony_ci goto out; 247662306a36Sopenharmony_ci } 247762306a36Sopenharmony_ci 247862306a36Sopenharmony_ci for (name = xattr_list; name - xattr_list < xattr_list_len; 247962306a36Sopenharmony_ci name += strlen(name) + 1) { 248062306a36Sopenharmony_ci ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name)); 248162306a36Sopenharmony_ci 248262306a36Sopenharmony_ci if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) && 248362306a36Sopenharmony_ci !strncmp(&name[XATTR_USER_PREFIX_LEN], STREAM_PREFIX, 248462306a36Sopenharmony_ci STREAM_PREFIX_LEN)) { 248562306a36Sopenharmony_ci err = ksmbd_vfs_remove_xattr(idmap, path, 248662306a36Sopenharmony_ci name); 248762306a36Sopenharmony_ci if (err) 248862306a36Sopenharmony_ci ksmbd_debug(SMB, "remove xattr failed : %s\n", 248962306a36Sopenharmony_ci name); 249062306a36Sopenharmony_ci } 249162306a36Sopenharmony_ci } 249262306a36Sopenharmony_ciout: 249362306a36Sopenharmony_ci kvfree(xattr_list); 249462306a36Sopenharmony_ci return err; 249562306a36Sopenharmony_ci} 249662306a36Sopenharmony_ci 249762306a36Sopenharmony_cistatic int smb2_create_truncate(const struct path *path) 249862306a36Sopenharmony_ci{ 249962306a36Sopenharmony_ci int rc = vfs_truncate(path, 0); 250062306a36Sopenharmony_ci 250162306a36Sopenharmony_ci if (rc) { 250262306a36Sopenharmony_ci pr_err("vfs_truncate failed, rc %d\n", rc); 250362306a36Sopenharmony_ci return rc; 250462306a36Sopenharmony_ci } 250562306a36Sopenharmony_ci 250662306a36Sopenharmony_ci rc = smb2_remove_smb_xattrs(path); 250762306a36Sopenharmony_ci if (rc == -EOPNOTSUPP) 250862306a36Sopenharmony_ci rc = 0; 250962306a36Sopenharmony_ci if (rc) 251062306a36Sopenharmony_ci ksmbd_debug(SMB, 251162306a36Sopenharmony_ci "ksmbd_truncate_stream_name_xattr failed, rc %d\n", 251262306a36Sopenharmony_ci rc); 251362306a36Sopenharmony_ci return rc; 251462306a36Sopenharmony_ci} 251562306a36Sopenharmony_ci 251662306a36Sopenharmony_cistatic void smb2_new_xattrs(struct ksmbd_tree_connect *tcon, const struct path *path, 251762306a36Sopenharmony_ci struct ksmbd_file *fp) 251862306a36Sopenharmony_ci{ 251962306a36Sopenharmony_ci struct xattr_dos_attrib da = {0}; 252062306a36Sopenharmony_ci int rc; 252162306a36Sopenharmony_ci 252262306a36Sopenharmony_ci if (!test_share_config_flag(tcon->share_conf, 252362306a36Sopenharmony_ci KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) 252462306a36Sopenharmony_ci return; 252562306a36Sopenharmony_ci 252662306a36Sopenharmony_ci da.version = 4; 252762306a36Sopenharmony_ci da.attr = le32_to_cpu(fp->f_ci->m_fattr); 252862306a36Sopenharmony_ci da.itime = da.create_time = fp->create_time; 252962306a36Sopenharmony_ci da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME | 253062306a36Sopenharmony_ci XATTR_DOSINFO_ITIME; 253162306a36Sopenharmony_ci 253262306a36Sopenharmony_ci rc = ksmbd_vfs_set_dos_attrib_xattr(mnt_idmap(path->mnt), path, &da, true); 253362306a36Sopenharmony_ci if (rc) 253462306a36Sopenharmony_ci ksmbd_debug(SMB, "failed to store file attribute into xattr\n"); 253562306a36Sopenharmony_ci} 253662306a36Sopenharmony_ci 253762306a36Sopenharmony_cistatic void smb2_update_xattrs(struct ksmbd_tree_connect *tcon, 253862306a36Sopenharmony_ci const struct path *path, struct ksmbd_file *fp) 253962306a36Sopenharmony_ci{ 254062306a36Sopenharmony_ci struct xattr_dos_attrib da; 254162306a36Sopenharmony_ci int rc; 254262306a36Sopenharmony_ci 254362306a36Sopenharmony_ci fp->f_ci->m_fattr &= ~(FILE_ATTRIBUTE_HIDDEN_LE | FILE_ATTRIBUTE_SYSTEM_LE); 254462306a36Sopenharmony_ci 254562306a36Sopenharmony_ci /* get FileAttributes from XATTR_NAME_DOS_ATTRIBUTE */ 254662306a36Sopenharmony_ci if (!test_share_config_flag(tcon->share_conf, 254762306a36Sopenharmony_ci KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) 254862306a36Sopenharmony_ci return; 254962306a36Sopenharmony_ci 255062306a36Sopenharmony_ci rc = ksmbd_vfs_get_dos_attrib_xattr(mnt_idmap(path->mnt), 255162306a36Sopenharmony_ci path->dentry, &da); 255262306a36Sopenharmony_ci if (rc > 0) { 255362306a36Sopenharmony_ci fp->f_ci->m_fattr = cpu_to_le32(da.attr); 255462306a36Sopenharmony_ci fp->create_time = da.create_time; 255562306a36Sopenharmony_ci fp->itime = da.itime; 255662306a36Sopenharmony_ci } 255762306a36Sopenharmony_ci} 255862306a36Sopenharmony_ci 255962306a36Sopenharmony_cistatic int smb2_creat(struct ksmbd_work *work, struct path *parent_path, 256062306a36Sopenharmony_ci struct path *path, char *name, int open_flags, 256162306a36Sopenharmony_ci umode_t posix_mode, bool is_dir) 256262306a36Sopenharmony_ci{ 256362306a36Sopenharmony_ci struct ksmbd_tree_connect *tcon = work->tcon; 256462306a36Sopenharmony_ci struct ksmbd_share_config *share = tcon->share_conf; 256562306a36Sopenharmony_ci umode_t mode; 256662306a36Sopenharmony_ci int rc; 256762306a36Sopenharmony_ci 256862306a36Sopenharmony_ci if (!(open_flags & O_CREAT)) 256962306a36Sopenharmony_ci return -EBADF; 257062306a36Sopenharmony_ci 257162306a36Sopenharmony_ci ksmbd_debug(SMB, "file does not exist, so creating\n"); 257262306a36Sopenharmony_ci if (is_dir == true) { 257362306a36Sopenharmony_ci ksmbd_debug(SMB, "creating directory\n"); 257462306a36Sopenharmony_ci 257562306a36Sopenharmony_ci mode = share_config_directory_mode(share, posix_mode); 257662306a36Sopenharmony_ci rc = ksmbd_vfs_mkdir(work, name, mode); 257762306a36Sopenharmony_ci if (rc) 257862306a36Sopenharmony_ci return rc; 257962306a36Sopenharmony_ci } else { 258062306a36Sopenharmony_ci ksmbd_debug(SMB, "creating regular file\n"); 258162306a36Sopenharmony_ci 258262306a36Sopenharmony_ci mode = share_config_create_mode(share, posix_mode); 258362306a36Sopenharmony_ci rc = ksmbd_vfs_create(work, name, mode); 258462306a36Sopenharmony_ci if (rc) 258562306a36Sopenharmony_ci return rc; 258662306a36Sopenharmony_ci } 258762306a36Sopenharmony_ci 258862306a36Sopenharmony_ci rc = ksmbd_vfs_kern_path_locked(work, name, 0, parent_path, path, 0); 258962306a36Sopenharmony_ci if (rc) { 259062306a36Sopenharmony_ci pr_err("cannot get linux path (%s), err = %d\n", 259162306a36Sopenharmony_ci name, rc); 259262306a36Sopenharmony_ci return rc; 259362306a36Sopenharmony_ci } 259462306a36Sopenharmony_ci return 0; 259562306a36Sopenharmony_ci} 259662306a36Sopenharmony_ci 259762306a36Sopenharmony_cistatic int smb2_create_sd_buffer(struct ksmbd_work *work, 259862306a36Sopenharmony_ci struct smb2_create_req *req, 259962306a36Sopenharmony_ci const struct path *path) 260062306a36Sopenharmony_ci{ 260162306a36Sopenharmony_ci struct create_context *context; 260262306a36Sopenharmony_ci struct create_sd_buf_req *sd_buf; 260362306a36Sopenharmony_ci 260462306a36Sopenharmony_ci if (!req->CreateContextsOffset) 260562306a36Sopenharmony_ci return -ENOENT; 260662306a36Sopenharmony_ci 260762306a36Sopenharmony_ci /* Parse SD BUFFER create contexts */ 260862306a36Sopenharmony_ci context = smb2_find_context_vals(req, SMB2_CREATE_SD_BUFFER, 4); 260962306a36Sopenharmony_ci if (!context) 261062306a36Sopenharmony_ci return -ENOENT; 261162306a36Sopenharmony_ci else if (IS_ERR(context)) 261262306a36Sopenharmony_ci return PTR_ERR(context); 261362306a36Sopenharmony_ci 261462306a36Sopenharmony_ci ksmbd_debug(SMB, 261562306a36Sopenharmony_ci "Set ACLs using SMB2_CREATE_SD_BUFFER context\n"); 261662306a36Sopenharmony_ci sd_buf = (struct create_sd_buf_req *)context; 261762306a36Sopenharmony_ci if (le16_to_cpu(context->DataOffset) + 261862306a36Sopenharmony_ci le32_to_cpu(context->DataLength) < 261962306a36Sopenharmony_ci sizeof(struct create_sd_buf_req)) 262062306a36Sopenharmony_ci return -EINVAL; 262162306a36Sopenharmony_ci return set_info_sec(work->conn, work->tcon, path, &sd_buf->ntsd, 262262306a36Sopenharmony_ci le32_to_cpu(sd_buf->ccontext.DataLength), true, false); 262362306a36Sopenharmony_ci} 262462306a36Sopenharmony_ci 262562306a36Sopenharmony_cistatic void ksmbd_acls_fattr(struct smb_fattr *fattr, 262662306a36Sopenharmony_ci struct mnt_idmap *idmap, 262762306a36Sopenharmony_ci struct inode *inode) 262862306a36Sopenharmony_ci{ 262962306a36Sopenharmony_ci vfsuid_t vfsuid = i_uid_into_vfsuid(idmap, inode); 263062306a36Sopenharmony_ci vfsgid_t vfsgid = i_gid_into_vfsgid(idmap, inode); 263162306a36Sopenharmony_ci 263262306a36Sopenharmony_ci fattr->cf_uid = vfsuid_into_kuid(vfsuid); 263362306a36Sopenharmony_ci fattr->cf_gid = vfsgid_into_kgid(vfsgid); 263462306a36Sopenharmony_ci fattr->cf_mode = inode->i_mode; 263562306a36Sopenharmony_ci fattr->cf_acls = NULL; 263662306a36Sopenharmony_ci fattr->cf_dacls = NULL; 263762306a36Sopenharmony_ci 263862306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { 263962306a36Sopenharmony_ci fattr->cf_acls = get_inode_acl(inode, ACL_TYPE_ACCESS); 264062306a36Sopenharmony_ci if (S_ISDIR(inode->i_mode)) 264162306a36Sopenharmony_ci fattr->cf_dacls = get_inode_acl(inode, ACL_TYPE_DEFAULT); 264262306a36Sopenharmony_ci } 264362306a36Sopenharmony_ci} 264462306a36Sopenharmony_ci 264562306a36Sopenharmony_ci/** 264662306a36Sopenharmony_ci * smb2_open() - handler for smb file open request 264762306a36Sopenharmony_ci * @work: smb work containing request buffer 264862306a36Sopenharmony_ci * 264962306a36Sopenharmony_ci * Return: 0 on success, otherwise error 265062306a36Sopenharmony_ci */ 265162306a36Sopenharmony_ciint smb2_open(struct ksmbd_work *work) 265262306a36Sopenharmony_ci{ 265362306a36Sopenharmony_ci struct ksmbd_conn *conn = work->conn; 265462306a36Sopenharmony_ci struct ksmbd_session *sess = work->sess; 265562306a36Sopenharmony_ci struct ksmbd_tree_connect *tcon = work->tcon; 265662306a36Sopenharmony_ci struct smb2_create_req *req; 265762306a36Sopenharmony_ci struct smb2_create_rsp *rsp; 265862306a36Sopenharmony_ci struct path path, parent_path; 265962306a36Sopenharmony_ci struct ksmbd_share_config *share = tcon->share_conf; 266062306a36Sopenharmony_ci struct ksmbd_file *fp = NULL; 266162306a36Sopenharmony_ci struct file *filp = NULL; 266262306a36Sopenharmony_ci struct mnt_idmap *idmap = NULL; 266362306a36Sopenharmony_ci struct kstat stat; 266462306a36Sopenharmony_ci struct create_context *context; 266562306a36Sopenharmony_ci struct lease_ctx_info *lc = NULL; 266662306a36Sopenharmony_ci struct create_ea_buf_req *ea_buf = NULL; 266762306a36Sopenharmony_ci struct oplock_info *opinfo; 266862306a36Sopenharmony_ci __le32 *next_ptr = NULL; 266962306a36Sopenharmony_ci int req_op_level = 0, open_flags = 0, may_flags = 0, file_info = 0; 267062306a36Sopenharmony_ci int rc = 0; 267162306a36Sopenharmony_ci int contxt_cnt = 0, query_disk_id = 0; 267262306a36Sopenharmony_ci int maximal_access_ctxt = 0, posix_ctxt = 0; 267362306a36Sopenharmony_ci int s_type = 0; 267462306a36Sopenharmony_ci int next_off = 0; 267562306a36Sopenharmony_ci char *name = NULL; 267662306a36Sopenharmony_ci char *stream_name = NULL; 267762306a36Sopenharmony_ci bool file_present = false, created = false, already_permitted = false; 267862306a36Sopenharmony_ci int share_ret, need_truncate = 0; 267962306a36Sopenharmony_ci u64 time; 268062306a36Sopenharmony_ci umode_t posix_mode = 0; 268162306a36Sopenharmony_ci __le32 daccess, maximal_access = 0; 268262306a36Sopenharmony_ci int iov_len = 0; 268362306a36Sopenharmony_ci 268462306a36Sopenharmony_ci WORK_BUFFERS(work, req, rsp); 268562306a36Sopenharmony_ci 268662306a36Sopenharmony_ci if (req->hdr.NextCommand && !work->next_smb2_rcv_hdr_off && 268762306a36Sopenharmony_ci (req->hdr.Flags & SMB2_FLAGS_RELATED_OPERATIONS)) { 268862306a36Sopenharmony_ci ksmbd_debug(SMB, "invalid flag in chained command\n"); 268962306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_PARAMETER; 269062306a36Sopenharmony_ci smb2_set_err_rsp(work); 269162306a36Sopenharmony_ci return -EINVAL; 269262306a36Sopenharmony_ci } 269362306a36Sopenharmony_ci 269462306a36Sopenharmony_ci if (test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) { 269562306a36Sopenharmony_ci ksmbd_debug(SMB, "IPC pipe create request\n"); 269662306a36Sopenharmony_ci return create_smb2_pipe(work); 269762306a36Sopenharmony_ci } 269862306a36Sopenharmony_ci 269962306a36Sopenharmony_ci if (req->NameLength) { 270062306a36Sopenharmony_ci if ((req->CreateOptions & FILE_DIRECTORY_FILE_LE) && 270162306a36Sopenharmony_ci *(char *)req->Buffer == '\\') { 270262306a36Sopenharmony_ci pr_err("not allow directory name included leading slash\n"); 270362306a36Sopenharmony_ci rc = -EINVAL; 270462306a36Sopenharmony_ci goto err_out2; 270562306a36Sopenharmony_ci } 270662306a36Sopenharmony_ci 270762306a36Sopenharmony_ci name = smb2_get_name(req->Buffer, 270862306a36Sopenharmony_ci le16_to_cpu(req->NameLength), 270962306a36Sopenharmony_ci work->conn->local_nls); 271062306a36Sopenharmony_ci if (IS_ERR(name)) { 271162306a36Sopenharmony_ci rc = PTR_ERR(name); 271262306a36Sopenharmony_ci if (rc != -ENOMEM) 271362306a36Sopenharmony_ci rc = -ENOENT; 271462306a36Sopenharmony_ci name = NULL; 271562306a36Sopenharmony_ci goto err_out2; 271662306a36Sopenharmony_ci } 271762306a36Sopenharmony_ci 271862306a36Sopenharmony_ci ksmbd_debug(SMB, "converted name = %s\n", name); 271962306a36Sopenharmony_ci if (strchr(name, ':')) { 272062306a36Sopenharmony_ci if (!test_share_config_flag(work->tcon->share_conf, 272162306a36Sopenharmony_ci KSMBD_SHARE_FLAG_STREAMS)) { 272262306a36Sopenharmony_ci rc = -EBADF; 272362306a36Sopenharmony_ci goto err_out2; 272462306a36Sopenharmony_ci } 272562306a36Sopenharmony_ci rc = parse_stream_name(name, &stream_name, &s_type); 272662306a36Sopenharmony_ci if (rc < 0) 272762306a36Sopenharmony_ci goto err_out2; 272862306a36Sopenharmony_ci } 272962306a36Sopenharmony_ci 273062306a36Sopenharmony_ci rc = ksmbd_validate_filename(name); 273162306a36Sopenharmony_ci if (rc < 0) 273262306a36Sopenharmony_ci goto err_out2; 273362306a36Sopenharmony_ci 273462306a36Sopenharmony_ci if (ksmbd_share_veto_filename(share, name)) { 273562306a36Sopenharmony_ci rc = -ENOENT; 273662306a36Sopenharmony_ci ksmbd_debug(SMB, "Reject open(), vetoed file: %s\n", 273762306a36Sopenharmony_ci name); 273862306a36Sopenharmony_ci goto err_out2; 273962306a36Sopenharmony_ci } 274062306a36Sopenharmony_ci } else { 274162306a36Sopenharmony_ci name = kstrdup("", GFP_KERNEL); 274262306a36Sopenharmony_ci if (!name) { 274362306a36Sopenharmony_ci rc = -ENOMEM; 274462306a36Sopenharmony_ci goto err_out2; 274562306a36Sopenharmony_ci } 274662306a36Sopenharmony_ci } 274762306a36Sopenharmony_ci 274862306a36Sopenharmony_ci if (le32_to_cpu(req->ImpersonationLevel) > le32_to_cpu(IL_DELEGATE)) { 274962306a36Sopenharmony_ci pr_err("Invalid impersonationlevel : 0x%x\n", 275062306a36Sopenharmony_ci le32_to_cpu(req->ImpersonationLevel)); 275162306a36Sopenharmony_ci rc = -EIO; 275262306a36Sopenharmony_ci rsp->hdr.Status = STATUS_BAD_IMPERSONATION_LEVEL; 275362306a36Sopenharmony_ci goto err_out2; 275462306a36Sopenharmony_ci } 275562306a36Sopenharmony_ci 275662306a36Sopenharmony_ci if (req->CreateOptions && !(req->CreateOptions & CREATE_OPTIONS_MASK_LE)) { 275762306a36Sopenharmony_ci pr_err("Invalid create options : 0x%x\n", 275862306a36Sopenharmony_ci le32_to_cpu(req->CreateOptions)); 275962306a36Sopenharmony_ci rc = -EINVAL; 276062306a36Sopenharmony_ci goto err_out2; 276162306a36Sopenharmony_ci } else { 276262306a36Sopenharmony_ci if (req->CreateOptions & FILE_SEQUENTIAL_ONLY_LE && 276362306a36Sopenharmony_ci req->CreateOptions & FILE_RANDOM_ACCESS_LE) 276462306a36Sopenharmony_ci req->CreateOptions = ~(FILE_SEQUENTIAL_ONLY_LE); 276562306a36Sopenharmony_ci 276662306a36Sopenharmony_ci if (req->CreateOptions & 276762306a36Sopenharmony_ci (FILE_OPEN_BY_FILE_ID_LE | CREATE_TREE_CONNECTION | 276862306a36Sopenharmony_ci FILE_RESERVE_OPFILTER_LE)) { 276962306a36Sopenharmony_ci rc = -EOPNOTSUPP; 277062306a36Sopenharmony_ci goto err_out2; 277162306a36Sopenharmony_ci } 277262306a36Sopenharmony_ci 277362306a36Sopenharmony_ci if (req->CreateOptions & FILE_DIRECTORY_FILE_LE) { 277462306a36Sopenharmony_ci if (req->CreateOptions & FILE_NON_DIRECTORY_FILE_LE) { 277562306a36Sopenharmony_ci rc = -EINVAL; 277662306a36Sopenharmony_ci goto err_out2; 277762306a36Sopenharmony_ci } else if (req->CreateOptions & FILE_NO_COMPRESSION_LE) { 277862306a36Sopenharmony_ci req->CreateOptions = ~(FILE_NO_COMPRESSION_LE); 277962306a36Sopenharmony_ci } 278062306a36Sopenharmony_ci } 278162306a36Sopenharmony_ci } 278262306a36Sopenharmony_ci 278362306a36Sopenharmony_ci if (le32_to_cpu(req->CreateDisposition) > 278462306a36Sopenharmony_ci le32_to_cpu(FILE_OVERWRITE_IF_LE)) { 278562306a36Sopenharmony_ci pr_err("Invalid create disposition : 0x%x\n", 278662306a36Sopenharmony_ci le32_to_cpu(req->CreateDisposition)); 278762306a36Sopenharmony_ci rc = -EINVAL; 278862306a36Sopenharmony_ci goto err_out2; 278962306a36Sopenharmony_ci } 279062306a36Sopenharmony_ci 279162306a36Sopenharmony_ci if (!(req->DesiredAccess & DESIRED_ACCESS_MASK)) { 279262306a36Sopenharmony_ci pr_err("Invalid desired access : 0x%x\n", 279362306a36Sopenharmony_ci le32_to_cpu(req->DesiredAccess)); 279462306a36Sopenharmony_ci rc = -EACCES; 279562306a36Sopenharmony_ci goto err_out2; 279662306a36Sopenharmony_ci } 279762306a36Sopenharmony_ci 279862306a36Sopenharmony_ci if (req->FileAttributes && !(req->FileAttributes & FILE_ATTRIBUTE_MASK_LE)) { 279962306a36Sopenharmony_ci pr_err("Invalid file attribute : 0x%x\n", 280062306a36Sopenharmony_ci le32_to_cpu(req->FileAttributes)); 280162306a36Sopenharmony_ci rc = -EINVAL; 280262306a36Sopenharmony_ci goto err_out2; 280362306a36Sopenharmony_ci } 280462306a36Sopenharmony_ci 280562306a36Sopenharmony_ci if (req->CreateContextsOffset) { 280662306a36Sopenharmony_ci /* Parse non-durable handle create contexts */ 280762306a36Sopenharmony_ci context = smb2_find_context_vals(req, SMB2_CREATE_EA_BUFFER, 4); 280862306a36Sopenharmony_ci if (IS_ERR(context)) { 280962306a36Sopenharmony_ci rc = PTR_ERR(context); 281062306a36Sopenharmony_ci goto err_out2; 281162306a36Sopenharmony_ci } else if (context) { 281262306a36Sopenharmony_ci ea_buf = (struct create_ea_buf_req *)context; 281362306a36Sopenharmony_ci if (le16_to_cpu(context->DataOffset) + 281462306a36Sopenharmony_ci le32_to_cpu(context->DataLength) < 281562306a36Sopenharmony_ci sizeof(struct create_ea_buf_req)) { 281662306a36Sopenharmony_ci rc = -EINVAL; 281762306a36Sopenharmony_ci goto err_out2; 281862306a36Sopenharmony_ci } 281962306a36Sopenharmony_ci if (req->CreateOptions & FILE_NO_EA_KNOWLEDGE_LE) { 282062306a36Sopenharmony_ci rsp->hdr.Status = STATUS_ACCESS_DENIED; 282162306a36Sopenharmony_ci rc = -EACCES; 282262306a36Sopenharmony_ci goto err_out2; 282362306a36Sopenharmony_ci } 282462306a36Sopenharmony_ci } 282562306a36Sopenharmony_ci 282662306a36Sopenharmony_ci context = smb2_find_context_vals(req, 282762306a36Sopenharmony_ci SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST, 4); 282862306a36Sopenharmony_ci if (IS_ERR(context)) { 282962306a36Sopenharmony_ci rc = PTR_ERR(context); 283062306a36Sopenharmony_ci goto err_out2; 283162306a36Sopenharmony_ci } else if (context) { 283262306a36Sopenharmony_ci ksmbd_debug(SMB, 283362306a36Sopenharmony_ci "get query maximal access context\n"); 283462306a36Sopenharmony_ci maximal_access_ctxt = 1; 283562306a36Sopenharmony_ci } 283662306a36Sopenharmony_ci 283762306a36Sopenharmony_ci context = smb2_find_context_vals(req, 283862306a36Sopenharmony_ci SMB2_CREATE_TIMEWARP_REQUEST, 4); 283962306a36Sopenharmony_ci if (IS_ERR(context)) { 284062306a36Sopenharmony_ci rc = PTR_ERR(context); 284162306a36Sopenharmony_ci goto err_out2; 284262306a36Sopenharmony_ci } else if (context) { 284362306a36Sopenharmony_ci ksmbd_debug(SMB, "get timewarp context\n"); 284462306a36Sopenharmony_ci rc = -EBADF; 284562306a36Sopenharmony_ci goto err_out2; 284662306a36Sopenharmony_ci } 284762306a36Sopenharmony_ci 284862306a36Sopenharmony_ci if (tcon->posix_extensions) { 284962306a36Sopenharmony_ci context = smb2_find_context_vals(req, 285062306a36Sopenharmony_ci SMB2_CREATE_TAG_POSIX, 16); 285162306a36Sopenharmony_ci if (IS_ERR(context)) { 285262306a36Sopenharmony_ci rc = PTR_ERR(context); 285362306a36Sopenharmony_ci goto err_out2; 285462306a36Sopenharmony_ci } else if (context) { 285562306a36Sopenharmony_ci struct create_posix *posix = 285662306a36Sopenharmony_ci (struct create_posix *)context; 285762306a36Sopenharmony_ci if (le16_to_cpu(context->DataOffset) + 285862306a36Sopenharmony_ci le32_to_cpu(context->DataLength) < 285962306a36Sopenharmony_ci sizeof(struct create_posix) - 4) { 286062306a36Sopenharmony_ci rc = -EINVAL; 286162306a36Sopenharmony_ci goto err_out2; 286262306a36Sopenharmony_ci } 286362306a36Sopenharmony_ci ksmbd_debug(SMB, "get posix context\n"); 286462306a36Sopenharmony_ci 286562306a36Sopenharmony_ci posix_mode = le32_to_cpu(posix->Mode); 286662306a36Sopenharmony_ci posix_ctxt = 1; 286762306a36Sopenharmony_ci } 286862306a36Sopenharmony_ci } 286962306a36Sopenharmony_ci } 287062306a36Sopenharmony_ci 287162306a36Sopenharmony_ci if (ksmbd_override_fsids(work)) { 287262306a36Sopenharmony_ci rc = -ENOMEM; 287362306a36Sopenharmony_ci goto err_out2; 287462306a36Sopenharmony_ci } 287562306a36Sopenharmony_ci 287662306a36Sopenharmony_ci rc = ksmbd_vfs_kern_path_locked(work, name, LOOKUP_NO_SYMLINKS, 287762306a36Sopenharmony_ci &parent_path, &path, 1); 287862306a36Sopenharmony_ci if (!rc) { 287962306a36Sopenharmony_ci file_present = true; 288062306a36Sopenharmony_ci 288162306a36Sopenharmony_ci if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) { 288262306a36Sopenharmony_ci /* 288362306a36Sopenharmony_ci * If file exists with under flags, return access 288462306a36Sopenharmony_ci * denied error. 288562306a36Sopenharmony_ci */ 288662306a36Sopenharmony_ci if (req->CreateDisposition == FILE_OVERWRITE_IF_LE || 288762306a36Sopenharmony_ci req->CreateDisposition == FILE_OPEN_IF_LE) { 288862306a36Sopenharmony_ci rc = -EACCES; 288962306a36Sopenharmony_ci goto err_out; 289062306a36Sopenharmony_ci } 289162306a36Sopenharmony_ci 289262306a36Sopenharmony_ci if (!test_tree_conn_flag(tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { 289362306a36Sopenharmony_ci ksmbd_debug(SMB, 289462306a36Sopenharmony_ci "User does not have write permission\n"); 289562306a36Sopenharmony_ci rc = -EACCES; 289662306a36Sopenharmony_ci goto err_out; 289762306a36Sopenharmony_ci } 289862306a36Sopenharmony_ci } else if (d_is_symlink(path.dentry)) { 289962306a36Sopenharmony_ci rc = -EACCES; 290062306a36Sopenharmony_ci goto err_out; 290162306a36Sopenharmony_ci } 290262306a36Sopenharmony_ci 290362306a36Sopenharmony_ci file_present = true; 290462306a36Sopenharmony_ci idmap = mnt_idmap(path.mnt); 290562306a36Sopenharmony_ci } else { 290662306a36Sopenharmony_ci if (rc != -ENOENT) 290762306a36Sopenharmony_ci goto err_out; 290862306a36Sopenharmony_ci ksmbd_debug(SMB, "can not get linux path for %s, rc = %d\n", 290962306a36Sopenharmony_ci name, rc); 291062306a36Sopenharmony_ci rc = 0; 291162306a36Sopenharmony_ci } 291262306a36Sopenharmony_ci 291362306a36Sopenharmony_ci if (stream_name) { 291462306a36Sopenharmony_ci if (req->CreateOptions & FILE_DIRECTORY_FILE_LE) { 291562306a36Sopenharmony_ci if (s_type == DATA_STREAM) { 291662306a36Sopenharmony_ci rc = -EIO; 291762306a36Sopenharmony_ci rsp->hdr.Status = STATUS_NOT_A_DIRECTORY; 291862306a36Sopenharmony_ci } 291962306a36Sopenharmony_ci } else { 292062306a36Sopenharmony_ci if (file_present && S_ISDIR(d_inode(path.dentry)->i_mode) && 292162306a36Sopenharmony_ci s_type == DATA_STREAM) { 292262306a36Sopenharmony_ci rc = -EIO; 292362306a36Sopenharmony_ci rsp->hdr.Status = STATUS_FILE_IS_A_DIRECTORY; 292462306a36Sopenharmony_ci } 292562306a36Sopenharmony_ci } 292662306a36Sopenharmony_ci 292762306a36Sopenharmony_ci if (req->CreateOptions & FILE_DIRECTORY_FILE_LE && 292862306a36Sopenharmony_ci req->FileAttributes & FILE_ATTRIBUTE_NORMAL_LE) { 292962306a36Sopenharmony_ci rsp->hdr.Status = STATUS_NOT_A_DIRECTORY; 293062306a36Sopenharmony_ci rc = -EIO; 293162306a36Sopenharmony_ci } 293262306a36Sopenharmony_ci 293362306a36Sopenharmony_ci if (rc < 0) 293462306a36Sopenharmony_ci goto err_out; 293562306a36Sopenharmony_ci } 293662306a36Sopenharmony_ci 293762306a36Sopenharmony_ci if (file_present && req->CreateOptions & FILE_NON_DIRECTORY_FILE_LE && 293862306a36Sopenharmony_ci S_ISDIR(d_inode(path.dentry)->i_mode) && 293962306a36Sopenharmony_ci !(req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { 294062306a36Sopenharmony_ci ksmbd_debug(SMB, "open() argument is a directory: %s, %x\n", 294162306a36Sopenharmony_ci name, req->CreateOptions); 294262306a36Sopenharmony_ci rsp->hdr.Status = STATUS_FILE_IS_A_DIRECTORY; 294362306a36Sopenharmony_ci rc = -EIO; 294462306a36Sopenharmony_ci goto err_out; 294562306a36Sopenharmony_ci } 294662306a36Sopenharmony_ci 294762306a36Sopenharmony_ci if (file_present && (req->CreateOptions & FILE_DIRECTORY_FILE_LE) && 294862306a36Sopenharmony_ci !(req->CreateDisposition == FILE_CREATE_LE) && 294962306a36Sopenharmony_ci !S_ISDIR(d_inode(path.dentry)->i_mode)) { 295062306a36Sopenharmony_ci rsp->hdr.Status = STATUS_NOT_A_DIRECTORY; 295162306a36Sopenharmony_ci rc = -EIO; 295262306a36Sopenharmony_ci goto err_out; 295362306a36Sopenharmony_ci } 295462306a36Sopenharmony_ci 295562306a36Sopenharmony_ci if (!stream_name && file_present && 295662306a36Sopenharmony_ci req->CreateDisposition == FILE_CREATE_LE) { 295762306a36Sopenharmony_ci rc = -EEXIST; 295862306a36Sopenharmony_ci goto err_out; 295962306a36Sopenharmony_ci } 296062306a36Sopenharmony_ci 296162306a36Sopenharmony_ci daccess = smb_map_generic_desired_access(req->DesiredAccess); 296262306a36Sopenharmony_ci 296362306a36Sopenharmony_ci if (file_present && !(req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { 296462306a36Sopenharmony_ci rc = smb_check_perm_dacl(conn, &path, &daccess, 296562306a36Sopenharmony_ci sess->user->uid); 296662306a36Sopenharmony_ci if (rc) 296762306a36Sopenharmony_ci goto err_out; 296862306a36Sopenharmony_ci } 296962306a36Sopenharmony_ci 297062306a36Sopenharmony_ci if (daccess & FILE_MAXIMAL_ACCESS_LE) { 297162306a36Sopenharmony_ci if (!file_present) { 297262306a36Sopenharmony_ci daccess = cpu_to_le32(GENERIC_ALL_FLAGS); 297362306a36Sopenharmony_ci } else { 297462306a36Sopenharmony_ci ksmbd_vfs_query_maximal_access(idmap, 297562306a36Sopenharmony_ci path.dentry, 297662306a36Sopenharmony_ci &daccess); 297762306a36Sopenharmony_ci already_permitted = true; 297862306a36Sopenharmony_ci } 297962306a36Sopenharmony_ci maximal_access = daccess; 298062306a36Sopenharmony_ci } 298162306a36Sopenharmony_ci 298262306a36Sopenharmony_ci open_flags = smb2_create_open_flags(file_present, daccess, 298362306a36Sopenharmony_ci req->CreateDisposition, 298462306a36Sopenharmony_ci &may_flags); 298562306a36Sopenharmony_ci 298662306a36Sopenharmony_ci if (!test_tree_conn_flag(tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { 298762306a36Sopenharmony_ci if (open_flags & (O_CREAT | O_TRUNC)) { 298862306a36Sopenharmony_ci ksmbd_debug(SMB, 298962306a36Sopenharmony_ci "User does not have write permission\n"); 299062306a36Sopenharmony_ci rc = -EACCES; 299162306a36Sopenharmony_ci goto err_out; 299262306a36Sopenharmony_ci } 299362306a36Sopenharmony_ci } 299462306a36Sopenharmony_ci 299562306a36Sopenharmony_ci /*create file if not present */ 299662306a36Sopenharmony_ci if (!file_present) { 299762306a36Sopenharmony_ci rc = smb2_creat(work, &parent_path, &path, name, open_flags, 299862306a36Sopenharmony_ci posix_mode, 299962306a36Sopenharmony_ci req->CreateOptions & FILE_DIRECTORY_FILE_LE); 300062306a36Sopenharmony_ci if (rc) { 300162306a36Sopenharmony_ci if (rc == -ENOENT) { 300262306a36Sopenharmony_ci rc = -EIO; 300362306a36Sopenharmony_ci rsp->hdr.Status = STATUS_OBJECT_PATH_NOT_FOUND; 300462306a36Sopenharmony_ci } 300562306a36Sopenharmony_ci goto err_out; 300662306a36Sopenharmony_ci } 300762306a36Sopenharmony_ci 300862306a36Sopenharmony_ci created = true; 300962306a36Sopenharmony_ci idmap = mnt_idmap(path.mnt); 301062306a36Sopenharmony_ci if (ea_buf) { 301162306a36Sopenharmony_ci if (le32_to_cpu(ea_buf->ccontext.DataLength) < 301262306a36Sopenharmony_ci sizeof(struct smb2_ea_info)) { 301362306a36Sopenharmony_ci rc = -EINVAL; 301462306a36Sopenharmony_ci goto err_out; 301562306a36Sopenharmony_ci } 301662306a36Sopenharmony_ci 301762306a36Sopenharmony_ci rc = smb2_set_ea(&ea_buf->ea, 301862306a36Sopenharmony_ci le32_to_cpu(ea_buf->ccontext.DataLength), 301962306a36Sopenharmony_ci &path, false); 302062306a36Sopenharmony_ci if (rc == -EOPNOTSUPP) 302162306a36Sopenharmony_ci rc = 0; 302262306a36Sopenharmony_ci else if (rc) 302362306a36Sopenharmony_ci goto err_out; 302462306a36Sopenharmony_ci } 302562306a36Sopenharmony_ci } else if (!already_permitted) { 302662306a36Sopenharmony_ci /* FILE_READ_ATTRIBUTE is allowed without inode_permission, 302762306a36Sopenharmony_ci * because execute(search) permission on a parent directory, 302862306a36Sopenharmony_ci * is already granted. 302962306a36Sopenharmony_ci */ 303062306a36Sopenharmony_ci if (daccess & ~(FILE_READ_ATTRIBUTES_LE | FILE_READ_CONTROL_LE)) { 303162306a36Sopenharmony_ci rc = inode_permission(idmap, 303262306a36Sopenharmony_ci d_inode(path.dentry), 303362306a36Sopenharmony_ci may_flags); 303462306a36Sopenharmony_ci if (rc) 303562306a36Sopenharmony_ci goto err_out; 303662306a36Sopenharmony_ci 303762306a36Sopenharmony_ci if ((daccess & FILE_DELETE_LE) || 303862306a36Sopenharmony_ci (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { 303962306a36Sopenharmony_ci rc = inode_permission(idmap, 304062306a36Sopenharmony_ci d_inode(path.dentry->d_parent), 304162306a36Sopenharmony_ci MAY_EXEC | MAY_WRITE); 304262306a36Sopenharmony_ci if (rc) 304362306a36Sopenharmony_ci goto err_out; 304462306a36Sopenharmony_ci } 304562306a36Sopenharmony_ci } 304662306a36Sopenharmony_ci } 304762306a36Sopenharmony_ci 304862306a36Sopenharmony_ci rc = ksmbd_query_inode_status(path.dentry->d_parent); 304962306a36Sopenharmony_ci if (rc == KSMBD_INODE_STATUS_PENDING_DELETE) { 305062306a36Sopenharmony_ci rc = -EBUSY; 305162306a36Sopenharmony_ci goto err_out; 305262306a36Sopenharmony_ci } 305362306a36Sopenharmony_ci 305462306a36Sopenharmony_ci rc = 0; 305562306a36Sopenharmony_ci filp = dentry_open(&path, open_flags, current_cred()); 305662306a36Sopenharmony_ci if (IS_ERR(filp)) { 305762306a36Sopenharmony_ci rc = PTR_ERR(filp); 305862306a36Sopenharmony_ci pr_err("dentry open for dir failed, rc %d\n", rc); 305962306a36Sopenharmony_ci goto err_out; 306062306a36Sopenharmony_ci } 306162306a36Sopenharmony_ci 306262306a36Sopenharmony_ci if (file_present) { 306362306a36Sopenharmony_ci if (!(open_flags & O_TRUNC)) 306462306a36Sopenharmony_ci file_info = FILE_OPENED; 306562306a36Sopenharmony_ci else 306662306a36Sopenharmony_ci file_info = FILE_OVERWRITTEN; 306762306a36Sopenharmony_ci 306862306a36Sopenharmony_ci if ((req->CreateDisposition & FILE_CREATE_MASK_LE) == 306962306a36Sopenharmony_ci FILE_SUPERSEDE_LE) 307062306a36Sopenharmony_ci file_info = FILE_SUPERSEDED; 307162306a36Sopenharmony_ci } else if (open_flags & O_CREAT) { 307262306a36Sopenharmony_ci file_info = FILE_CREATED; 307362306a36Sopenharmony_ci } 307462306a36Sopenharmony_ci 307562306a36Sopenharmony_ci ksmbd_vfs_set_fadvise(filp, req->CreateOptions); 307662306a36Sopenharmony_ci 307762306a36Sopenharmony_ci /* Obtain Volatile-ID */ 307862306a36Sopenharmony_ci fp = ksmbd_open_fd(work, filp); 307962306a36Sopenharmony_ci if (IS_ERR(fp)) { 308062306a36Sopenharmony_ci fput(filp); 308162306a36Sopenharmony_ci rc = PTR_ERR(fp); 308262306a36Sopenharmony_ci fp = NULL; 308362306a36Sopenharmony_ci goto err_out; 308462306a36Sopenharmony_ci } 308562306a36Sopenharmony_ci 308662306a36Sopenharmony_ci /* Get Persistent-ID */ 308762306a36Sopenharmony_ci ksmbd_open_durable_fd(fp); 308862306a36Sopenharmony_ci if (!has_file_id(fp->persistent_id)) { 308962306a36Sopenharmony_ci rc = -ENOMEM; 309062306a36Sopenharmony_ci goto err_out; 309162306a36Sopenharmony_ci } 309262306a36Sopenharmony_ci 309362306a36Sopenharmony_ci fp->cdoption = req->CreateDisposition; 309462306a36Sopenharmony_ci fp->daccess = daccess; 309562306a36Sopenharmony_ci fp->saccess = req->ShareAccess; 309662306a36Sopenharmony_ci fp->coption = req->CreateOptions; 309762306a36Sopenharmony_ci 309862306a36Sopenharmony_ci /* Set default windows and posix acls if creating new file */ 309962306a36Sopenharmony_ci if (created) { 310062306a36Sopenharmony_ci int posix_acl_rc; 310162306a36Sopenharmony_ci struct inode *inode = d_inode(path.dentry); 310262306a36Sopenharmony_ci 310362306a36Sopenharmony_ci posix_acl_rc = ksmbd_vfs_inherit_posix_acl(idmap, 310462306a36Sopenharmony_ci &path, 310562306a36Sopenharmony_ci d_inode(path.dentry->d_parent)); 310662306a36Sopenharmony_ci if (posix_acl_rc) 310762306a36Sopenharmony_ci ksmbd_debug(SMB, "inherit posix acl failed : %d\n", posix_acl_rc); 310862306a36Sopenharmony_ci 310962306a36Sopenharmony_ci if (test_share_config_flag(work->tcon->share_conf, 311062306a36Sopenharmony_ci KSMBD_SHARE_FLAG_ACL_XATTR)) { 311162306a36Sopenharmony_ci rc = smb_inherit_dacl(conn, &path, sess->user->uid, 311262306a36Sopenharmony_ci sess->user->gid); 311362306a36Sopenharmony_ci } 311462306a36Sopenharmony_ci 311562306a36Sopenharmony_ci if (rc) { 311662306a36Sopenharmony_ci rc = smb2_create_sd_buffer(work, req, &path); 311762306a36Sopenharmony_ci if (rc) { 311862306a36Sopenharmony_ci if (posix_acl_rc) 311962306a36Sopenharmony_ci ksmbd_vfs_set_init_posix_acl(idmap, 312062306a36Sopenharmony_ci &path); 312162306a36Sopenharmony_ci 312262306a36Sopenharmony_ci if (test_share_config_flag(work->tcon->share_conf, 312362306a36Sopenharmony_ci KSMBD_SHARE_FLAG_ACL_XATTR)) { 312462306a36Sopenharmony_ci struct smb_fattr fattr; 312562306a36Sopenharmony_ci struct smb_ntsd *pntsd; 312662306a36Sopenharmony_ci int pntsd_size, ace_num = 0; 312762306a36Sopenharmony_ci 312862306a36Sopenharmony_ci ksmbd_acls_fattr(&fattr, idmap, inode); 312962306a36Sopenharmony_ci if (fattr.cf_acls) 313062306a36Sopenharmony_ci ace_num = fattr.cf_acls->a_count; 313162306a36Sopenharmony_ci if (fattr.cf_dacls) 313262306a36Sopenharmony_ci ace_num += fattr.cf_dacls->a_count; 313362306a36Sopenharmony_ci 313462306a36Sopenharmony_ci pntsd = kmalloc(sizeof(struct smb_ntsd) + 313562306a36Sopenharmony_ci sizeof(struct smb_sid) * 3 + 313662306a36Sopenharmony_ci sizeof(struct smb_acl) + 313762306a36Sopenharmony_ci sizeof(struct smb_ace) * ace_num * 2, 313862306a36Sopenharmony_ci GFP_KERNEL); 313962306a36Sopenharmony_ci if (!pntsd) { 314062306a36Sopenharmony_ci posix_acl_release(fattr.cf_acls); 314162306a36Sopenharmony_ci posix_acl_release(fattr.cf_dacls); 314262306a36Sopenharmony_ci goto err_out; 314362306a36Sopenharmony_ci } 314462306a36Sopenharmony_ci 314562306a36Sopenharmony_ci rc = build_sec_desc(idmap, 314662306a36Sopenharmony_ci pntsd, NULL, 0, 314762306a36Sopenharmony_ci OWNER_SECINFO | 314862306a36Sopenharmony_ci GROUP_SECINFO | 314962306a36Sopenharmony_ci DACL_SECINFO, 315062306a36Sopenharmony_ci &pntsd_size, &fattr); 315162306a36Sopenharmony_ci posix_acl_release(fattr.cf_acls); 315262306a36Sopenharmony_ci posix_acl_release(fattr.cf_dacls); 315362306a36Sopenharmony_ci if (rc) { 315462306a36Sopenharmony_ci kfree(pntsd); 315562306a36Sopenharmony_ci goto err_out; 315662306a36Sopenharmony_ci } 315762306a36Sopenharmony_ci 315862306a36Sopenharmony_ci rc = ksmbd_vfs_set_sd_xattr(conn, 315962306a36Sopenharmony_ci idmap, 316062306a36Sopenharmony_ci &path, 316162306a36Sopenharmony_ci pntsd, 316262306a36Sopenharmony_ci pntsd_size, 316362306a36Sopenharmony_ci false); 316462306a36Sopenharmony_ci kfree(pntsd); 316562306a36Sopenharmony_ci if (rc) 316662306a36Sopenharmony_ci pr_err("failed to store ntacl in xattr : %d\n", 316762306a36Sopenharmony_ci rc); 316862306a36Sopenharmony_ci } 316962306a36Sopenharmony_ci } 317062306a36Sopenharmony_ci } 317162306a36Sopenharmony_ci rc = 0; 317262306a36Sopenharmony_ci } 317362306a36Sopenharmony_ci 317462306a36Sopenharmony_ci if (stream_name) { 317562306a36Sopenharmony_ci rc = smb2_set_stream_name_xattr(&path, 317662306a36Sopenharmony_ci fp, 317762306a36Sopenharmony_ci stream_name, 317862306a36Sopenharmony_ci s_type); 317962306a36Sopenharmony_ci if (rc) 318062306a36Sopenharmony_ci goto err_out; 318162306a36Sopenharmony_ci file_info = FILE_CREATED; 318262306a36Sopenharmony_ci } 318362306a36Sopenharmony_ci 318462306a36Sopenharmony_ci fp->attrib_only = !(req->DesiredAccess & ~(FILE_READ_ATTRIBUTES_LE | 318562306a36Sopenharmony_ci FILE_WRITE_ATTRIBUTES_LE | FILE_SYNCHRONIZE_LE)); 318662306a36Sopenharmony_ci 318762306a36Sopenharmony_ci /* fp should be searchable through ksmbd_inode.m_fp_list 318862306a36Sopenharmony_ci * after daccess, saccess, attrib_only, and stream are 318962306a36Sopenharmony_ci * initialized. 319062306a36Sopenharmony_ci */ 319162306a36Sopenharmony_ci write_lock(&fp->f_ci->m_lock); 319262306a36Sopenharmony_ci list_add(&fp->node, &fp->f_ci->m_fp_list); 319362306a36Sopenharmony_ci write_unlock(&fp->f_ci->m_lock); 319462306a36Sopenharmony_ci 319562306a36Sopenharmony_ci /* Check delete pending among previous fp before oplock break */ 319662306a36Sopenharmony_ci if (ksmbd_inode_pending_delete(fp)) { 319762306a36Sopenharmony_ci rc = -EBUSY; 319862306a36Sopenharmony_ci goto err_out; 319962306a36Sopenharmony_ci } 320062306a36Sopenharmony_ci 320162306a36Sopenharmony_ci if (file_present || created) 320262306a36Sopenharmony_ci ksmbd_vfs_kern_path_unlock(&parent_path, &path); 320362306a36Sopenharmony_ci 320462306a36Sopenharmony_ci if (!S_ISDIR(file_inode(filp)->i_mode) && open_flags & O_TRUNC && 320562306a36Sopenharmony_ci !fp->attrib_only && !stream_name) { 320662306a36Sopenharmony_ci smb_break_all_oplock(work, fp); 320762306a36Sopenharmony_ci need_truncate = 1; 320862306a36Sopenharmony_ci } 320962306a36Sopenharmony_ci 321062306a36Sopenharmony_ci req_op_level = req->RequestedOplockLevel; 321162306a36Sopenharmony_ci if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) 321262306a36Sopenharmony_ci lc = parse_lease_state(req, S_ISDIR(file_inode(filp)->i_mode)); 321362306a36Sopenharmony_ci 321462306a36Sopenharmony_ci share_ret = ksmbd_smb_check_shared_mode(fp->filp, fp); 321562306a36Sopenharmony_ci if (!test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_OPLOCKS) || 321662306a36Sopenharmony_ci (req_op_level == SMB2_OPLOCK_LEVEL_LEASE && 321762306a36Sopenharmony_ci !(conn->vals->capabilities & SMB2_GLOBAL_CAP_LEASING))) { 321862306a36Sopenharmony_ci if (share_ret < 0 && !S_ISDIR(file_inode(fp->filp)->i_mode)) { 321962306a36Sopenharmony_ci rc = share_ret; 322062306a36Sopenharmony_ci goto err_out1; 322162306a36Sopenharmony_ci } 322262306a36Sopenharmony_ci } else { 322362306a36Sopenharmony_ci if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) { 322462306a36Sopenharmony_ci /* 322562306a36Sopenharmony_ci * Compare parent lease using parent key. If there is no 322662306a36Sopenharmony_ci * a lease that has same parent key, Send lease break 322762306a36Sopenharmony_ci * notification. 322862306a36Sopenharmony_ci */ 322962306a36Sopenharmony_ci smb_send_parent_lease_break_noti(fp, lc); 323062306a36Sopenharmony_ci 323162306a36Sopenharmony_ci req_op_level = smb2_map_lease_to_oplock(lc->req_state); 323262306a36Sopenharmony_ci ksmbd_debug(SMB, 323362306a36Sopenharmony_ci "lease req for(%s) req oplock state 0x%x, lease state 0x%x\n", 323462306a36Sopenharmony_ci name, req_op_level, lc->req_state); 323562306a36Sopenharmony_ci rc = find_same_lease_key(sess, fp->f_ci, lc); 323662306a36Sopenharmony_ci if (rc) 323762306a36Sopenharmony_ci goto err_out1; 323862306a36Sopenharmony_ci } else if (open_flags == O_RDONLY && 323962306a36Sopenharmony_ci (req_op_level == SMB2_OPLOCK_LEVEL_BATCH || 324062306a36Sopenharmony_ci req_op_level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) 324162306a36Sopenharmony_ci req_op_level = SMB2_OPLOCK_LEVEL_II; 324262306a36Sopenharmony_ci 324362306a36Sopenharmony_ci rc = smb_grant_oplock(work, req_op_level, 324462306a36Sopenharmony_ci fp->persistent_id, fp, 324562306a36Sopenharmony_ci le32_to_cpu(req->hdr.Id.SyncId.TreeId), 324662306a36Sopenharmony_ci lc, share_ret); 324762306a36Sopenharmony_ci if (rc < 0) 324862306a36Sopenharmony_ci goto err_out1; 324962306a36Sopenharmony_ci } 325062306a36Sopenharmony_ci 325162306a36Sopenharmony_ci if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) 325262306a36Sopenharmony_ci ksmbd_fd_set_delete_on_close(fp, file_info); 325362306a36Sopenharmony_ci 325462306a36Sopenharmony_ci if (need_truncate) { 325562306a36Sopenharmony_ci rc = smb2_create_truncate(&fp->filp->f_path); 325662306a36Sopenharmony_ci if (rc) 325762306a36Sopenharmony_ci goto err_out1; 325862306a36Sopenharmony_ci } 325962306a36Sopenharmony_ci 326062306a36Sopenharmony_ci if (req->CreateContextsOffset) { 326162306a36Sopenharmony_ci struct create_alloc_size_req *az_req; 326262306a36Sopenharmony_ci 326362306a36Sopenharmony_ci az_req = (struct create_alloc_size_req *)smb2_find_context_vals(req, 326462306a36Sopenharmony_ci SMB2_CREATE_ALLOCATION_SIZE, 4); 326562306a36Sopenharmony_ci if (IS_ERR(az_req)) { 326662306a36Sopenharmony_ci rc = PTR_ERR(az_req); 326762306a36Sopenharmony_ci goto err_out1; 326862306a36Sopenharmony_ci } else if (az_req) { 326962306a36Sopenharmony_ci loff_t alloc_size; 327062306a36Sopenharmony_ci int err; 327162306a36Sopenharmony_ci 327262306a36Sopenharmony_ci if (le16_to_cpu(az_req->ccontext.DataOffset) + 327362306a36Sopenharmony_ci le32_to_cpu(az_req->ccontext.DataLength) < 327462306a36Sopenharmony_ci sizeof(struct create_alloc_size_req)) { 327562306a36Sopenharmony_ci rc = -EINVAL; 327662306a36Sopenharmony_ci goto err_out1; 327762306a36Sopenharmony_ci } 327862306a36Sopenharmony_ci alloc_size = le64_to_cpu(az_req->AllocationSize); 327962306a36Sopenharmony_ci ksmbd_debug(SMB, 328062306a36Sopenharmony_ci "request smb2 create allocate size : %llu\n", 328162306a36Sopenharmony_ci alloc_size); 328262306a36Sopenharmony_ci smb_break_all_levII_oplock(work, fp, 1); 328362306a36Sopenharmony_ci err = vfs_fallocate(fp->filp, FALLOC_FL_KEEP_SIZE, 0, 328462306a36Sopenharmony_ci alloc_size); 328562306a36Sopenharmony_ci if (err < 0) 328662306a36Sopenharmony_ci ksmbd_debug(SMB, 328762306a36Sopenharmony_ci "vfs_fallocate is failed : %d\n", 328862306a36Sopenharmony_ci err); 328962306a36Sopenharmony_ci } 329062306a36Sopenharmony_ci 329162306a36Sopenharmony_ci context = smb2_find_context_vals(req, SMB2_CREATE_QUERY_ON_DISK_ID, 4); 329262306a36Sopenharmony_ci if (IS_ERR(context)) { 329362306a36Sopenharmony_ci rc = PTR_ERR(context); 329462306a36Sopenharmony_ci goto err_out1; 329562306a36Sopenharmony_ci } else if (context) { 329662306a36Sopenharmony_ci ksmbd_debug(SMB, "get query on disk id context\n"); 329762306a36Sopenharmony_ci query_disk_id = 1; 329862306a36Sopenharmony_ci } 329962306a36Sopenharmony_ci } 330062306a36Sopenharmony_ci 330162306a36Sopenharmony_ci rc = ksmbd_vfs_getattr(&path, &stat); 330262306a36Sopenharmony_ci if (rc) 330362306a36Sopenharmony_ci goto err_out1; 330462306a36Sopenharmony_ci 330562306a36Sopenharmony_ci if (stat.result_mask & STATX_BTIME) 330662306a36Sopenharmony_ci fp->create_time = ksmbd_UnixTimeToNT(stat.btime); 330762306a36Sopenharmony_ci else 330862306a36Sopenharmony_ci fp->create_time = ksmbd_UnixTimeToNT(stat.ctime); 330962306a36Sopenharmony_ci if (req->FileAttributes || fp->f_ci->m_fattr == 0) 331062306a36Sopenharmony_ci fp->f_ci->m_fattr = 331162306a36Sopenharmony_ci cpu_to_le32(smb2_get_dos_mode(&stat, le32_to_cpu(req->FileAttributes))); 331262306a36Sopenharmony_ci 331362306a36Sopenharmony_ci if (!created) 331462306a36Sopenharmony_ci smb2_update_xattrs(tcon, &path, fp); 331562306a36Sopenharmony_ci else 331662306a36Sopenharmony_ci smb2_new_xattrs(tcon, &path, fp); 331762306a36Sopenharmony_ci 331862306a36Sopenharmony_ci memcpy(fp->client_guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE); 331962306a36Sopenharmony_ci 332062306a36Sopenharmony_ci rsp->StructureSize = cpu_to_le16(89); 332162306a36Sopenharmony_ci rcu_read_lock(); 332262306a36Sopenharmony_ci opinfo = rcu_dereference(fp->f_opinfo); 332362306a36Sopenharmony_ci rsp->OplockLevel = opinfo != NULL ? opinfo->level : 0; 332462306a36Sopenharmony_ci rcu_read_unlock(); 332562306a36Sopenharmony_ci rsp->Flags = 0; 332662306a36Sopenharmony_ci rsp->CreateAction = cpu_to_le32(file_info); 332762306a36Sopenharmony_ci rsp->CreationTime = cpu_to_le64(fp->create_time); 332862306a36Sopenharmony_ci time = ksmbd_UnixTimeToNT(stat.atime); 332962306a36Sopenharmony_ci rsp->LastAccessTime = cpu_to_le64(time); 333062306a36Sopenharmony_ci time = ksmbd_UnixTimeToNT(stat.mtime); 333162306a36Sopenharmony_ci rsp->LastWriteTime = cpu_to_le64(time); 333262306a36Sopenharmony_ci time = ksmbd_UnixTimeToNT(stat.ctime); 333362306a36Sopenharmony_ci rsp->ChangeTime = cpu_to_le64(time); 333462306a36Sopenharmony_ci rsp->AllocationSize = S_ISDIR(stat.mode) ? 0 : 333562306a36Sopenharmony_ci cpu_to_le64(stat.blocks << 9); 333662306a36Sopenharmony_ci rsp->EndofFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); 333762306a36Sopenharmony_ci rsp->FileAttributes = fp->f_ci->m_fattr; 333862306a36Sopenharmony_ci 333962306a36Sopenharmony_ci rsp->Reserved2 = 0; 334062306a36Sopenharmony_ci 334162306a36Sopenharmony_ci rsp->PersistentFileId = fp->persistent_id; 334262306a36Sopenharmony_ci rsp->VolatileFileId = fp->volatile_id; 334362306a36Sopenharmony_ci 334462306a36Sopenharmony_ci rsp->CreateContextsOffset = 0; 334562306a36Sopenharmony_ci rsp->CreateContextsLength = 0; 334662306a36Sopenharmony_ci iov_len = offsetof(struct smb2_create_rsp, Buffer); 334762306a36Sopenharmony_ci 334862306a36Sopenharmony_ci /* If lease is request send lease context response */ 334962306a36Sopenharmony_ci if (opinfo && opinfo->is_lease) { 335062306a36Sopenharmony_ci struct create_context *lease_ccontext; 335162306a36Sopenharmony_ci 335262306a36Sopenharmony_ci ksmbd_debug(SMB, "lease granted on(%s) lease state 0x%x\n", 335362306a36Sopenharmony_ci name, opinfo->o_lease->state); 335462306a36Sopenharmony_ci rsp->OplockLevel = SMB2_OPLOCK_LEVEL_LEASE; 335562306a36Sopenharmony_ci 335662306a36Sopenharmony_ci lease_ccontext = (struct create_context *)rsp->Buffer; 335762306a36Sopenharmony_ci contxt_cnt++; 335862306a36Sopenharmony_ci create_lease_buf(rsp->Buffer, opinfo->o_lease); 335962306a36Sopenharmony_ci le32_add_cpu(&rsp->CreateContextsLength, 336062306a36Sopenharmony_ci conn->vals->create_lease_size); 336162306a36Sopenharmony_ci iov_len += conn->vals->create_lease_size; 336262306a36Sopenharmony_ci next_ptr = &lease_ccontext->Next; 336362306a36Sopenharmony_ci next_off = conn->vals->create_lease_size; 336462306a36Sopenharmony_ci } 336562306a36Sopenharmony_ci 336662306a36Sopenharmony_ci if (maximal_access_ctxt) { 336762306a36Sopenharmony_ci struct create_context *mxac_ccontext; 336862306a36Sopenharmony_ci 336962306a36Sopenharmony_ci if (maximal_access == 0) 337062306a36Sopenharmony_ci ksmbd_vfs_query_maximal_access(idmap, 337162306a36Sopenharmony_ci path.dentry, 337262306a36Sopenharmony_ci &maximal_access); 337362306a36Sopenharmony_ci mxac_ccontext = (struct create_context *)(rsp->Buffer + 337462306a36Sopenharmony_ci le32_to_cpu(rsp->CreateContextsLength)); 337562306a36Sopenharmony_ci contxt_cnt++; 337662306a36Sopenharmony_ci create_mxac_rsp_buf(rsp->Buffer + 337762306a36Sopenharmony_ci le32_to_cpu(rsp->CreateContextsLength), 337862306a36Sopenharmony_ci le32_to_cpu(maximal_access)); 337962306a36Sopenharmony_ci le32_add_cpu(&rsp->CreateContextsLength, 338062306a36Sopenharmony_ci conn->vals->create_mxac_size); 338162306a36Sopenharmony_ci iov_len += conn->vals->create_mxac_size; 338262306a36Sopenharmony_ci if (next_ptr) 338362306a36Sopenharmony_ci *next_ptr = cpu_to_le32(next_off); 338462306a36Sopenharmony_ci next_ptr = &mxac_ccontext->Next; 338562306a36Sopenharmony_ci next_off = conn->vals->create_mxac_size; 338662306a36Sopenharmony_ci } 338762306a36Sopenharmony_ci 338862306a36Sopenharmony_ci if (query_disk_id) { 338962306a36Sopenharmony_ci struct create_context *disk_id_ccontext; 339062306a36Sopenharmony_ci 339162306a36Sopenharmony_ci disk_id_ccontext = (struct create_context *)(rsp->Buffer + 339262306a36Sopenharmony_ci le32_to_cpu(rsp->CreateContextsLength)); 339362306a36Sopenharmony_ci contxt_cnt++; 339462306a36Sopenharmony_ci create_disk_id_rsp_buf(rsp->Buffer + 339562306a36Sopenharmony_ci le32_to_cpu(rsp->CreateContextsLength), 339662306a36Sopenharmony_ci stat.ino, tcon->id); 339762306a36Sopenharmony_ci le32_add_cpu(&rsp->CreateContextsLength, 339862306a36Sopenharmony_ci conn->vals->create_disk_id_size); 339962306a36Sopenharmony_ci iov_len += conn->vals->create_disk_id_size; 340062306a36Sopenharmony_ci if (next_ptr) 340162306a36Sopenharmony_ci *next_ptr = cpu_to_le32(next_off); 340262306a36Sopenharmony_ci next_ptr = &disk_id_ccontext->Next; 340362306a36Sopenharmony_ci next_off = conn->vals->create_disk_id_size; 340462306a36Sopenharmony_ci } 340562306a36Sopenharmony_ci 340662306a36Sopenharmony_ci if (posix_ctxt) { 340762306a36Sopenharmony_ci contxt_cnt++; 340862306a36Sopenharmony_ci create_posix_rsp_buf(rsp->Buffer + 340962306a36Sopenharmony_ci le32_to_cpu(rsp->CreateContextsLength), 341062306a36Sopenharmony_ci fp); 341162306a36Sopenharmony_ci le32_add_cpu(&rsp->CreateContextsLength, 341262306a36Sopenharmony_ci conn->vals->create_posix_size); 341362306a36Sopenharmony_ci iov_len += conn->vals->create_posix_size; 341462306a36Sopenharmony_ci if (next_ptr) 341562306a36Sopenharmony_ci *next_ptr = cpu_to_le32(next_off); 341662306a36Sopenharmony_ci } 341762306a36Sopenharmony_ci 341862306a36Sopenharmony_ci if (contxt_cnt > 0) { 341962306a36Sopenharmony_ci rsp->CreateContextsOffset = 342062306a36Sopenharmony_ci cpu_to_le32(offsetof(struct smb2_create_rsp, Buffer)); 342162306a36Sopenharmony_ci } 342262306a36Sopenharmony_ci 342362306a36Sopenharmony_cierr_out: 342462306a36Sopenharmony_ci if (rc && (file_present || created)) 342562306a36Sopenharmony_ci ksmbd_vfs_kern_path_unlock(&parent_path, &path); 342662306a36Sopenharmony_ci 342762306a36Sopenharmony_cierr_out1: 342862306a36Sopenharmony_ci ksmbd_revert_fsids(work); 342962306a36Sopenharmony_ci 343062306a36Sopenharmony_cierr_out2: 343162306a36Sopenharmony_ci if (!rc) { 343262306a36Sopenharmony_ci ksmbd_update_fstate(&work->sess->file_table, fp, FP_INITED); 343362306a36Sopenharmony_ci rc = ksmbd_iov_pin_rsp(work, (void *)rsp, iov_len); 343462306a36Sopenharmony_ci } 343562306a36Sopenharmony_ci if (rc) { 343662306a36Sopenharmony_ci if (rc == -EINVAL) 343762306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_PARAMETER; 343862306a36Sopenharmony_ci else if (rc == -EOPNOTSUPP) 343962306a36Sopenharmony_ci rsp->hdr.Status = STATUS_NOT_SUPPORTED; 344062306a36Sopenharmony_ci else if (rc == -EACCES || rc == -ESTALE || rc == -EXDEV) 344162306a36Sopenharmony_ci rsp->hdr.Status = STATUS_ACCESS_DENIED; 344262306a36Sopenharmony_ci else if (rc == -ENOENT) 344362306a36Sopenharmony_ci rsp->hdr.Status = STATUS_OBJECT_NAME_INVALID; 344462306a36Sopenharmony_ci else if (rc == -EPERM) 344562306a36Sopenharmony_ci rsp->hdr.Status = STATUS_SHARING_VIOLATION; 344662306a36Sopenharmony_ci else if (rc == -EBUSY) 344762306a36Sopenharmony_ci rsp->hdr.Status = STATUS_DELETE_PENDING; 344862306a36Sopenharmony_ci else if (rc == -EBADF) 344962306a36Sopenharmony_ci rsp->hdr.Status = STATUS_OBJECT_NAME_NOT_FOUND; 345062306a36Sopenharmony_ci else if (rc == -ENOEXEC) 345162306a36Sopenharmony_ci rsp->hdr.Status = STATUS_DUPLICATE_OBJECTID; 345262306a36Sopenharmony_ci else if (rc == -ENXIO) 345362306a36Sopenharmony_ci rsp->hdr.Status = STATUS_NO_SUCH_DEVICE; 345462306a36Sopenharmony_ci else if (rc == -EEXIST) 345562306a36Sopenharmony_ci rsp->hdr.Status = STATUS_OBJECT_NAME_COLLISION; 345662306a36Sopenharmony_ci else if (rc == -EMFILE) 345762306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; 345862306a36Sopenharmony_ci if (!rsp->hdr.Status) 345962306a36Sopenharmony_ci rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; 346062306a36Sopenharmony_ci 346162306a36Sopenharmony_ci if (fp) 346262306a36Sopenharmony_ci ksmbd_fd_put(work, fp); 346362306a36Sopenharmony_ci smb2_set_err_rsp(work); 346462306a36Sopenharmony_ci ksmbd_debug(SMB, "Error response: %x\n", rsp->hdr.Status); 346562306a36Sopenharmony_ci } 346662306a36Sopenharmony_ci 346762306a36Sopenharmony_ci kfree(name); 346862306a36Sopenharmony_ci kfree(lc); 346962306a36Sopenharmony_ci 347062306a36Sopenharmony_ci return 0; 347162306a36Sopenharmony_ci} 347262306a36Sopenharmony_ci 347362306a36Sopenharmony_cistatic int readdir_info_level_struct_sz(int info_level) 347462306a36Sopenharmony_ci{ 347562306a36Sopenharmony_ci switch (info_level) { 347662306a36Sopenharmony_ci case FILE_FULL_DIRECTORY_INFORMATION: 347762306a36Sopenharmony_ci return sizeof(struct file_full_directory_info); 347862306a36Sopenharmony_ci case FILE_BOTH_DIRECTORY_INFORMATION: 347962306a36Sopenharmony_ci return sizeof(struct file_both_directory_info); 348062306a36Sopenharmony_ci case FILE_DIRECTORY_INFORMATION: 348162306a36Sopenharmony_ci return sizeof(struct file_directory_info); 348262306a36Sopenharmony_ci case FILE_NAMES_INFORMATION: 348362306a36Sopenharmony_ci return sizeof(struct file_names_info); 348462306a36Sopenharmony_ci case FILEID_FULL_DIRECTORY_INFORMATION: 348562306a36Sopenharmony_ci return sizeof(struct file_id_full_dir_info); 348662306a36Sopenharmony_ci case FILEID_BOTH_DIRECTORY_INFORMATION: 348762306a36Sopenharmony_ci return sizeof(struct file_id_both_directory_info); 348862306a36Sopenharmony_ci case SMB_FIND_FILE_POSIX_INFO: 348962306a36Sopenharmony_ci return sizeof(struct smb2_posix_info); 349062306a36Sopenharmony_ci default: 349162306a36Sopenharmony_ci return -EOPNOTSUPP; 349262306a36Sopenharmony_ci } 349362306a36Sopenharmony_ci} 349462306a36Sopenharmony_ci 349562306a36Sopenharmony_cistatic int dentry_name(struct ksmbd_dir_info *d_info, int info_level) 349662306a36Sopenharmony_ci{ 349762306a36Sopenharmony_ci switch (info_level) { 349862306a36Sopenharmony_ci case FILE_FULL_DIRECTORY_INFORMATION: 349962306a36Sopenharmony_ci { 350062306a36Sopenharmony_ci struct file_full_directory_info *ffdinfo; 350162306a36Sopenharmony_ci 350262306a36Sopenharmony_ci ffdinfo = (struct file_full_directory_info *)d_info->rptr; 350362306a36Sopenharmony_ci d_info->rptr += le32_to_cpu(ffdinfo->NextEntryOffset); 350462306a36Sopenharmony_ci d_info->name = ffdinfo->FileName; 350562306a36Sopenharmony_ci d_info->name_len = le32_to_cpu(ffdinfo->FileNameLength); 350662306a36Sopenharmony_ci return 0; 350762306a36Sopenharmony_ci } 350862306a36Sopenharmony_ci case FILE_BOTH_DIRECTORY_INFORMATION: 350962306a36Sopenharmony_ci { 351062306a36Sopenharmony_ci struct file_both_directory_info *fbdinfo; 351162306a36Sopenharmony_ci 351262306a36Sopenharmony_ci fbdinfo = (struct file_both_directory_info *)d_info->rptr; 351362306a36Sopenharmony_ci d_info->rptr += le32_to_cpu(fbdinfo->NextEntryOffset); 351462306a36Sopenharmony_ci d_info->name = fbdinfo->FileName; 351562306a36Sopenharmony_ci d_info->name_len = le32_to_cpu(fbdinfo->FileNameLength); 351662306a36Sopenharmony_ci return 0; 351762306a36Sopenharmony_ci } 351862306a36Sopenharmony_ci case FILE_DIRECTORY_INFORMATION: 351962306a36Sopenharmony_ci { 352062306a36Sopenharmony_ci struct file_directory_info *fdinfo; 352162306a36Sopenharmony_ci 352262306a36Sopenharmony_ci fdinfo = (struct file_directory_info *)d_info->rptr; 352362306a36Sopenharmony_ci d_info->rptr += le32_to_cpu(fdinfo->NextEntryOffset); 352462306a36Sopenharmony_ci d_info->name = fdinfo->FileName; 352562306a36Sopenharmony_ci d_info->name_len = le32_to_cpu(fdinfo->FileNameLength); 352662306a36Sopenharmony_ci return 0; 352762306a36Sopenharmony_ci } 352862306a36Sopenharmony_ci case FILE_NAMES_INFORMATION: 352962306a36Sopenharmony_ci { 353062306a36Sopenharmony_ci struct file_names_info *fninfo; 353162306a36Sopenharmony_ci 353262306a36Sopenharmony_ci fninfo = (struct file_names_info *)d_info->rptr; 353362306a36Sopenharmony_ci d_info->rptr += le32_to_cpu(fninfo->NextEntryOffset); 353462306a36Sopenharmony_ci d_info->name = fninfo->FileName; 353562306a36Sopenharmony_ci d_info->name_len = le32_to_cpu(fninfo->FileNameLength); 353662306a36Sopenharmony_ci return 0; 353762306a36Sopenharmony_ci } 353862306a36Sopenharmony_ci case FILEID_FULL_DIRECTORY_INFORMATION: 353962306a36Sopenharmony_ci { 354062306a36Sopenharmony_ci struct file_id_full_dir_info *dinfo; 354162306a36Sopenharmony_ci 354262306a36Sopenharmony_ci dinfo = (struct file_id_full_dir_info *)d_info->rptr; 354362306a36Sopenharmony_ci d_info->rptr += le32_to_cpu(dinfo->NextEntryOffset); 354462306a36Sopenharmony_ci d_info->name = dinfo->FileName; 354562306a36Sopenharmony_ci d_info->name_len = le32_to_cpu(dinfo->FileNameLength); 354662306a36Sopenharmony_ci return 0; 354762306a36Sopenharmony_ci } 354862306a36Sopenharmony_ci case FILEID_BOTH_DIRECTORY_INFORMATION: 354962306a36Sopenharmony_ci { 355062306a36Sopenharmony_ci struct file_id_both_directory_info *fibdinfo; 355162306a36Sopenharmony_ci 355262306a36Sopenharmony_ci fibdinfo = (struct file_id_both_directory_info *)d_info->rptr; 355362306a36Sopenharmony_ci d_info->rptr += le32_to_cpu(fibdinfo->NextEntryOffset); 355462306a36Sopenharmony_ci d_info->name = fibdinfo->FileName; 355562306a36Sopenharmony_ci d_info->name_len = le32_to_cpu(fibdinfo->FileNameLength); 355662306a36Sopenharmony_ci return 0; 355762306a36Sopenharmony_ci } 355862306a36Sopenharmony_ci case SMB_FIND_FILE_POSIX_INFO: 355962306a36Sopenharmony_ci { 356062306a36Sopenharmony_ci struct smb2_posix_info *posix_info; 356162306a36Sopenharmony_ci 356262306a36Sopenharmony_ci posix_info = (struct smb2_posix_info *)d_info->rptr; 356362306a36Sopenharmony_ci d_info->rptr += le32_to_cpu(posix_info->NextEntryOffset); 356462306a36Sopenharmony_ci d_info->name = posix_info->name; 356562306a36Sopenharmony_ci d_info->name_len = le32_to_cpu(posix_info->name_len); 356662306a36Sopenharmony_ci return 0; 356762306a36Sopenharmony_ci } 356862306a36Sopenharmony_ci default: 356962306a36Sopenharmony_ci return -EINVAL; 357062306a36Sopenharmony_ci } 357162306a36Sopenharmony_ci} 357262306a36Sopenharmony_ci 357362306a36Sopenharmony_ci/** 357462306a36Sopenharmony_ci * smb2_populate_readdir_entry() - encode directory entry in smb2 response 357562306a36Sopenharmony_ci * buffer 357662306a36Sopenharmony_ci * @conn: connection instance 357762306a36Sopenharmony_ci * @info_level: smb information level 357862306a36Sopenharmony_ci * @d_info: structure included variables for query dir 357962306a36Sopenharmony_ci * @ksmbd_kstat: ksmbd wrapper of dirent stat information 358062306a36Sopenharmony_ci * 358162306a36Sopenharmony_ci * if directory has many entries, find first can't read it fully. 358262306a36Sopenharmony_ci * find next might be called multiple times to read remaining dir entries 358362306a36Sopenharmony_ci * 358462306a36Sopenharmony_ci * Return: 0 on success, otherwise error 358562306a36Sopenharmony_ci */ 358662306a36Sopenharmony_cistatic int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, 358762306a36Sopenharmony_ci struct ksmbd_dir_info *d_info, 358862306a36Sopenharmony_ci struct ksmbd_kstat *ksmbd_kstat) 358962306a36Sopenharmony_ci{ 359062306a36Sopenharmony_ci int next_entry_offset = 0; 359162306a36Sopenharmony_ci char *conv_name; 359262306a36Sopenharmony_ci int conv_len; 359362306a36Sopenharmony_ci void *kstat; 359462306a36Sopenharmony_ci int struct_sz, rc = 0; 359562306a36Sopenharmony_ci 359662306a36Sopenharmony_ci conv_name = ksmbd_convert_dir_info_name(d_info, 359762306a36Sopenharmony_ci conn->local_nls, 359862306a36Sopenharmony_ci &conv_len); 359962306a36Sopenharmony_ci if (!conv_name) 360062306a36Sopenharmony_ci return -ENOMEM; 360162306a36Sopenharmony_ci 360262306a36Sopenharmony_ci /* Somehow the name has only terminating NULL bytes */ 360362306a36Sopenharmony_ci if (conv_len < 0) { 360462306a36Sopenharmony_ci rc = -EINVAL; 360562306a36Sopenharmony_ci goto free_conv_name; 360662306a36Sopenharmony_ci } 360762306a36Sopenharmony_ci 360862306a36Sopenharmony_ci struct_sz = readdir_info_level_struct_sz(info_level) + conv_len; 360962306a36Sopenharmony_ci next_entry_offset = ALIGN(struct_sz, KSMBD_DIR_INFO_ALIGNMENT); 361062306a36Sopenharmony_ci d_info->last_entry_off_align = next_entry_offset - struct_sz; 361162306a36Sopenharmony_ci 361262306a36Sopenharmony_ci if (next_entry_offset > d_info->out_buf_len) { 361362306a36Sopenharmony_ci d_info->out_buf_len = 0; 361462306a36Sopenharmony_ci rc = -ENOSPC; 361562306a36Sopenharmony_ci goto free_conv_name; 361662306a36Sopenharmony_ci } 361762306a36Sopenharmony_ci 361862306a36Sopenharmony_ci kstat = d_info->wptr; 361962306a36Sopenharmony_ci if (info_level != FILE_NAMES_INFORMATION) 362062306a36Sopenharmony_ci kstat = ksmbd_vfs_init_kstat(&d_info->wptr, ksmbd_kstat); 362162306a36Sopenharmony_ci 362262306a36Sopenharmony_ci switch (info_level) { 362362306a36Sopenharmony_ci case FILE_FULL_DIRECTORY_INFORMATION: 362462306a36Sopenharmony_ci { 362562306a36Sopenharmony_ci struct file_full_directory_info *ffdinfo; 362662306a36Sopenharmony_ci 362762306a36Sopenharmony_ci ffdinfo = (struct file_full_directory_info *)kstat; 362862306a36Sopenharmony_ci ffdinfo->FileNameLength = cpu_to_le32(conv_len); 362962306a36Sopenharmony_ci ffdinfo->EaSize = 363062306a36Sopenharmony_ci smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); 363162306a36Sopenharmony_ci if (ffdinfo->EaSize) 363262306a36Sopenharmony_ci ffdinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE; 363362306a36Sopenharmony_ci if (d_info->hide_dot_file && d_info->name[0] == '.') 363462306a36Sopenharmony_ci ffdinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; 363562306a36Sopenharmony_ci memcpy(ffdinfo->FileName, conv_name, conv_len); 363662306a36Sopenharmony_ci ffdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); 363762306a36Sopenharmony_ci break; 363862306a36Sopenharmony_ci } 363962306a36Sopenharmony_ci case FILE_BOTH_DIRECTORY_INFORMATION: 364062306a36Sopenharmony_ci { 364162306a36Sopenharmony_ci struct file_both_directory_info *fbdinfo; 364262306a36Sopenharmony_ci 364362306a36Sopenharmony_ci fbdinfo = (struct file_both_directory_info *)kstat; 364462306a36Sopenharmony_ci fbdinfo->FileNameLength = cpu_to_le32(conv_len); 364562306a36Sopenharmony_ci fbdinfo->EaSize = 364662306a36Sopenharmony_ci smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); 364762306a36Sopenharmony_ci if (fbdinfo->EaSize) 364862306a36Sopenharmony_ci fbdinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE; 364962306a36Sopenharmony_ci fbdinfo->ShortNameLength = 0; 365062306a36Sopenharmony_ci fbdinfo->Reserved = 0; 365162306a36Sopenharmony_ci if (d_info->hide_dot_file && d_info->name[0] == '.') 365262306a36Sopenharmony_ci fbdinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; 365362306a36Sopenharmony_ci memcpy(fbdinfo->FileName, conv_name, conv_len); 365462306a36Sopenharmony_ci fbdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); 365562306a36Sopenharmony_ci break; 365662306a36Sopenharmony_ci } 365762306a36Sopenharmony_ci case FILE_DIRECTORY_INFORMATION: 365862306a36Sopenharmony_ci { 365962306a36Sopenharmony_ci struct file_directory_info *fdinfo; 366062306a36Sopenharmony_ci 366162306a36Sopenharmony_ci fdinfo = (struct file_directory_info *)kstat; 366262306a36Sopenharmony_ci fdinfo->FileNameLength = cpu_to_le32(conv_len); 366362306a36Sopenharmony_ci if (d_info->hide_dot_file && d_info->name[0] == '.') 366462306a36Sopenharmony_ci fdinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; 366562306a36Sopenharmony_ci memcpy(fdinfo->FileName, conv_name, conv_len); 366662306a36Sopenharmony_ci fdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); 366762306a36Sopenharmony_ci break; 366862306a36Sopenharmony_ci } 366962306a36Sopenharmony_ci case FILE_NAMES_INFORMATION: 367062306a36Sopenharmony_ci { 367162306a36Sopenharmony_ci struct file_names_info *fninfo; 367262306a36Sopenharmony_ci 367362306a36Sopenharmony_ci fninfo = (struct file_names_info *)kstat; 367462306a36Sopenharmony_ci fninfo->FileNameLength = cpu_to_le32(conv_len); 367562306a36Sopenharmony_ci memcpy(fninfo->FileName, conv_name, conv_len); 367662306a36Sopenharmony_ci fninfo->NextEntryOffset = cpu_to_le32(next_entry_offset); 367762306a36Sopenharmony_ci break; 367862306a36Sopenharmony_ci } 367962306a36Sopenharmony_ci case FILEID_FULL_DIRECTORY_INFORMATION: 368062306a36Sopenharmony_ci { 368162306a36Sopenharmony_ci struct file_id_full_dir_info *dinfo; 368262306a36Sopenharmony_ci 368362306a36Sopenharmony_ci dinfo = (struct file_id_full_dir_info *)kstat; 368462306a36Sopenharmony_ci dinfo->FileNameLength = cpu_to_le32(conv_len); 368562306a36Sopenharmony_ci dinfo->EaSize = 368662306a36Sopenharmony_ci smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); 368762306a36Sopenharmony_ci if (dinfo->EaSize) 368862306a36Sopenharmony_ci dinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE; 368962306a36Sopenharmony_ci dinfo->Reserved = 0; 369062306a36Sopenharmony_ci dinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino); 369162306a36Sopenharmony_ci if (d_info->hide_dot_file && d_info->name[0] == '.') 369262306a36Sopenharmony_ci dinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; 369362306a36Sopenharmony_ci memcpy(dinfo->FileName, conv_name, conv_len); 369462306a36Sopenharmony_ci dinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); 369562306a36Sopenharmony_ci break; 369662306a36Sopenharmony_ci } 369762306a36Sopenharmony_ci case FILEID_BOTH_DIRECTORY_INFORMATION: 369862306a36Sopenharmony_ci { 369962306a36Sopenharmony_ci struct file_id_both_directory_info *fibdinfo; 370062306a36Sopenharmony_ci 370162306a36Sopenharmony_ci fibdinfo = (struct file_id_both_directory_info *)kstat; 370262306a36Sopenharmony_ci fibdinfo->FileNameLength = cpu_to_le32(conv_len); 370362306a36Sopenharmony_ci fibdinfo->EaSize = 370462306a36Sopenharmony_ci smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); 370562306a36Sopenharmony_ci if (fibdinfo->EaSize) 370662306a36Sopenharmony_ci fibdinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE; 370762306a36Sopenharmony_ci fibdinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino); 370862306a36Sopenharmony_ci fibdinfo->ShortNameLength = 0; 370962306a36Sopenharmony_ci fibdinfo->Reserved = 0; 371062306a36Sopenharmony_ci fibdinfo->Reserved2 = cpu_to_le16(0); 371162306a36Sopenharmony_ci if (d_info->hide_dot_file && d_info->name[0] == '.') 371262306a36Sopenharmony_ci fibdinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; 371362306a36Sopenharmony_ci memcpy(fibdinfo->FileName, conv_name, conv_len); 371462306a36Sopenharmony_ci fibdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); 371562306a36Sopenharmony_ci break; 371662306a36Sopenharmony_ci } 371762306a36Sopenharmony_ci case SMB_FIND_FILE_POSIX_INFO: 371862306a36Sopenharmony_ci { 371962306a36Sopenharmony_ci struct smb2_posix_info *posix_info; 372062306a36Sopenharmony_ci u64 time; 372162306a36Sopenharmony_ci 372262306a36Sopenharmony_ci posix_info = (struct smb2_posix_info *)kstat; 372362306a36Sopenharmony_ci posix_info->Ignored = 0; 372462306a36Sopenharmony_ci posix_info->CreationTime = cpu_to_le64(ksmbd_kstat->create_time); 372562306a36Sopenharmony_ci time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->ctime); 372662306a36Sopenharmony_ci posix_info->ChangeTime = cpu_to_le64(time); 372762306a36Sopenharmony_ci time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->atime); 372862306a36Sopenharmony_ci posix_info->LastAccessTime = cpu_to_le64(time); 372962306a36Sopenharmony_ci time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->mtime); 373062306a36Sopenharmony_ci posix_info->LastWriteTime = cpu_to_le64(time); 373162306a36Sopenharmony_ci posix_info->EndOfFile = cpu_to_le64(ksmbd_kstat->kstat->size); 373262306a36Sopenharmony_ci posix_info->AllocationSize = cpu_to_le64(ksmbd_kstat->kstat->blocks << 9); 373362306a36Sopenharmony_ci posix_info->DeviceId = cpu_to_le32(ksmbd_kstat->kstat->rdev); 373462306a36Sopenharmony_ci posix_info->HardLinks = cpu_to_le32(ksmbd_kstat->kstat->nlink); 373562306a36Sopenharmony_ci posix_info->Mode = cpu_to_le32(ksmbd_kstat->kstat->mode & 0777); 373662306a36Sopenharmony_ci posix_info->Inode = cpu_to_le64(ksmbd_kstat->kstat->ino); 373762306a36Sopenharmony_ci posix_info->DosAttributes = 373862306a36Sopenharmony_ci S_ISDIR(ksmbd_kstat->kstat->mode) ? 373962306a36Sopenharmony_ci FILE_ATTRIBUTE_DIRECTORY_LE : FILE_ATTRIBUTE_ARCHIVE_LE; 374062306a36Sopenharmony_ci if (d_info->hide_dot_file && d_info->name[0] == '.') 374162306a36Sopenharmony_ci posix_info->DosAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; 374262306a36Sopenharmony_ci /* 374362306a36Sopenharmony_ci * SidBuffer(32) contain two sids(Domain sid(16), UNIX group sid(16)). 374462306a36Sopenharmony_ci * UNIX sid(16) = revision(1) + num_subauth(1) + authority(6) + 374562306a36Sopenharmony_ci * sub_auth(4 * 1(num_subauth)) + RID(4). 374662306a36Sopenharmony_ci */ 374762306a36Sopenharmony_ci id_to_sid(from_kuid_munged(&init_user_ns, ksmbd_kstat->kstat->uid), 374862306a36Sopenharmony_ci SIDUNIX_USER, (struct smb_sid *)&posix_info->SidBuffer[0]); 374962306a36Sopenharmony_ci id_to_sid(from_kgid_munged(&init_user_ns, ksmbd_kstat->kstat->gid), 375062306a36Sopenharmony_ci SIDUNIX_GROUP, (struct smb_sid *)&posix_info->SidBuffer[16]); 375162306a36Sopenharmony_ci memcpy(posix_info->name, conv_name, conv_len); 375262306a36Sopenharmony_ci posix_info->name_len = cpu_to_le32(conv_len); 375362306a36Sopenharmony_ci posix_info->NextEntryOffset = cpu_to_le32(next_entry_offset); 375462306a36Sopenharmony_ci break; 375562306a36Sopenharmony_ci } 375662306a36Sopenharmony_ci 375762306a36Sopenharmony_ci } /* switch (info_level) */ 375862306a36Sopenharmony_ci 375962306a36Sopenharmony_ci d_info->last_entry_offset = d_info->data_count; 376062306a36Sopenharmony_ci d_info->data_count += next_entry_offset; 376162306a36Sopenharmony_ci d_info->out_buf_len -= next_entry_offset; 376262306a36Sopenharmony_ci d_info->wptr += next_entry_offset; 376362306a36Sopenharmony_ci 376462306a36Sopenharmony_ci ksmbd_debug(SMB, 376562306a36Sopenharmony_ci "info_level : %d, buf_len :%d, next_offset : %d, data_count : %d\n", 376662306a36Sopenharmony_ci info_level, d_info->out_buf_len, 376762306a36Sopenharmony_ci next_entry_offset, d_info->data_count); 376862306a36Sopenharmony_ci 376962306a36Sopenharmony_cifree_conv_name: 377062306a36Sopenharmony_ci kfree(conv_name); 377162306a36Sopenharmony_ci return rc; 377262306a36Sopenharmony_ci} 377362306a36Sopenharmony_ci 377462306a36Sopenharmony_cistruct smb2_query_dir_private { 377562306a36Sopenharmony_ci struct ksmbd_work *work; 377662306a36Sopenharmony_ci char *search_pattern; 377762306a36Sopenharmony_ci struct ksmbd_file *dir_fp; 377862306a36Sopenharmony_ci 377962306a36Sopenharmony_ci struct ksmbd_dir_info *d_info; 378062306a36Sopenharmony_ci int info_level; 378162306a36Sopenharmony_ci}; 378262306a36Sopenharmony_ci 378362306a36Sopenharmony_cistatic void lock_dir(struct ksmbd_file *dir_fp) 378462306a36Sopenharmony_ci{ 378562306a36Sopenharmony_ci struct dentry *dir = dir_fp->filp->f_path.dentry; 378662306a36Sopenharmony_ci 378762306a36Sopenharmony_ci inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); 378862306a36Sopenharmony_ci} 378962306a36Sopenharmony_ci 379062306a36Sopenharmony_cistatic void unlock_dir(struct ksmbd_file *dir_fp) 379162306a36Sopenharmony_ci{ 379262306a36Sopenharmony_ci struct dentry *dir = dir_fp->filp->f_path.dentry; 379362306a36Sopenharmony_ci 379462306a36Sopenharmony_ci inode_unlock(d_inode(dir)); 379562306a36Sopenharmony_ci} 379662306a36Sopenharmony_ci 379762306a36Sopenharmony_cistatic int process_query_dir_entries(struct smb2_query_dir_private *priv) 379862306a36Sopenharmony_ci{ 379962306a36Sopenharmony_ci struct mnt_idmap *idmap = file_mnt_idmap(priv->dir_fp->filp); 380062306a36Sopenharmony_ci struct kstat kstat; 380162306a36Sopenharmony_ci struct ksmbd_kstat ksmbd_kstat; 380262306a36Sopenharmony_ci int rc; 380362306a36Sopenharmony_ci int i; 380462306a36Sopenharmony_ci 380562306a36Sopenharmony_ci for (i = 0; i < priv->d_info->num_entry; i++) { 380662306a36Sopenharmony_ci struct dentry *dent; 380762306a36Sopenharmony_ci 380862306a36Sopenharmony_ci if (dentry_name(priv->d_info, priv->info_level)) 380962306a36Sopenharmony_ci return -EINVAL; 381062306a36Sopenharmony_ci 381162306a36Sopenharmony_ci lock_dir(priv->dir_fp); 381262306a36Sopenharmony_ci dent = lookup_one(idmap, priv->d_info->name, 381362306a36Sopenharmony_ci priv->dir_fp->filp->f_path.dentry, 381462306a36Sopenharmony_ci priv->d_info->name_len); 381562306a36Sopenharmony_ci unlock_dir(priv->dir_fp); 381662306a36Sopenharmony_ci 381762306a36Sopenharmony_ci if (IS_ERR(dent)) { 381862306a36Sopenharmony_ci ksmbd_debug(SMB, "Cannot lookup `%s' [%ld]\n", 381962306a36Sopenharmony_ci priv->d_info->name, 382062306a36Sopenharmony_ci PTR_ERR(dent)); 382162306a36Sopenharmony_ci continue; 382262306a36Sopenharmony_ci } 382362306a36Sopenharmony_ci if (unlikely(d_is_negative(dent))) { 382462306a36Sopenharmony_ci dput(dent); 382562306a36Sopenharmony_ci ksmbd_debug(SMB, "Negative dentry `%s'\n", 382662306a36Sopenharmony_ci priv->d_info->name); 382762306a36Sopenharmony_ci continue; 382862306a36Sopenharmony_ci } 382962306a36Sopenharmony_ci 383062306a36Sopenharmony_ci ksmbd_kstat.kstat = &kstat; 383162306a36Sopenharmony_ci if (priv->info_level != FILE_NAMES_INFORMATION) 383262306a36Sopenharmony_ci ksmbd_vfs_fill_dentry_attrs(priv->work, 383362306a36Sopenharmony_ci idmap, 383462306a36Sopenharmony_ci dent, 383562306a36Sopenharmony_ci &ksmbd_kstat); 383662306a36Sopenharmony_ci 383762306a36Sopenharmony_ci rc = smb2_populate_readdir_entry(priv->work->conn, 383862306a36Sopenharmony_ci priv->info_level, 383962306a36Sopenharmony_ci priv->d_info, 384062306a36Sopenharmony_ci &ksmbd_kstat); 384162306a36Sopenharmony_ci dput(dent); 384262306a36Sopenharmony_ci if (rc) 384362306a36Sopenharmony_ci return rc; 384462306a36Sopenharmony_ci } 384562306a36Sopenharmony_ci return 0; 384662306a36Sopenharmony_ci} 384762306a36Sopenharmony_ci 384862306a36Sopenharmony_cistatic int reserve_populate_dentry(struct ksmbd_dir_info *d_info, 384962306a36Sopenharmony_ci int info_level) 385062306a36Sopenharmony_ci{ 385162306a36Sopenharmony_ci int struct_sz; 385262306a36Sopenharmony_ci int conv_len; 385362306a36Sopenharmony_ci int next_entry_offset; 385462306a36Sopenharmony_ci 385562306a36Sopenharmony_ci struct_sz = readdir_info_level_struct_sz(info_level); 385662306a36Sopenharmony_ci if (struct_sz == -EOPNOTSUPP) 385762306a36Sopenharmony_ci return -EOPNOTSUPP; 385862306a36Sopenharmony_ci 385962306a36Sopenharmony_ci conv_len = (d_info->name_len + 1) * 2; 386062306a36Sopenharmony_ci next_entry_offset = ALIGN(struct_sz + conv_len, 386162306a36Sopenharmony_ci KSMBD_DIR_INFO_ALIGNMENT); 386262306a36Sopenharmony_ci 386362306a36Sopenharmony_ci if (next_entry_offset > d_info->out_buf_len) { 386462306a36Sopenharmony_ci d_info->out_buf_len = 0; 386562306a36Sopenharmony_ci return -ENOSPC; 386662306a36Sopenharmony_ci } 386762306a36Sopenharmony_ci 386862306a36Sopenharmony_ci switch (info_level) { 386962306a36Sopenharmony_ci case FILE_FULL_DIRECTORY_INFORMATION: 387062306a36Sopenharmony_ci { 387162306a36Sopenharmony_ci struct file_full_directory_info *ffdinfo; 387262306a36Sopenharmony_ci 387362306a36Sopenharmony_ci ffdinfo = (struct file_full_directory_info *)d_info->wptr; 387462306a36Sopenharmony_ci memcpy(ffdinfo->FileName, d_info->name, d_info->name_len); 387562306a36Sopenharmony_ci ffdinfo->FileName[d_info->name_len] = 0x00; 387662306a36Sopenharmony_ci ffdinfo->FileNameLength = cpu_to_le32(d_info->name_len); 387762306a36Sopenharmony_ci ffdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); 387862306a36Sopenharmony_ci break; 387962306a36Sopenharmony_ci } 388062306a36Sopenharmony_ci case FILE_BOTH_DIRECTORY_INFORMATION: 388162306a36Sopenharmony_ci { 388262306a36Sopenharmony_ci struct file_both_directory_info *fbdinfo; 388362306a36Sopenharmony_ci 388462306a36Sopenharmony_ci fbdinfo = (struct file_both_directory_info *)d_info->wptr; 388562306a36Sopenharmony_ci memcpy(fbdinfo->FileName, d_info->name, d_info->name_len); 388662306a36Sopenharmony_ci fbdinfo->FileName[d_info->name_len] = 0x00; 388762306a36Sopenharmony_ci fbdinfo->FileNameLength = cpu_to_le32(d_info->name_len); 388862306a36Sopenharmony_ci fbdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); 388962306a36Sopenharmony_ci break; 389062306a36Sopenharmony_ci } 389162306a36Sopenharmony_ci case FILE_DIRECTORY_INFORMATION: 389262306a36Sopenharmony_ci { 389362306a36Sopenharmony_ci struct file_directory_info *fdinfo; 389462306a36Sopenharmony_ci 389562306a36Sopenharmony_ci fdinfo = (struct file_directory_info *)d_info->wptr; 389662306a36Sopenharmony_ci memcpy(fdinfo->FileName, d_info->name, d_info->name_len); 389762306a36Sopenharmony_ci fdinfo->FileName[d_info->name_len] = 0x00; 389862306a36Sopenharmony_ci fdinfo->FileNameLength = cpu_to_le32(d_info->name_len); 389962306a36Sopenharmony_ci fdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); 390062306a36Sopenharmony_ci break; 390162306a36Sopenharmony_ci } 390262306a36Sopenharmony_ci case FILE_NAMES_INFORMATION: 390362306a36Sopenharmony_ci { 390462306a36Sopenharmony_ci struct file_names_info *fninfo; 390562306a36Sopenharmony_ci 390662306a36Sopenharmony_ci fninfo = (struct file_names_info *)d_info->wptr; 390762306a36Sopenharmony_ci memcpy(fninfo->FileName, d_info->name, d_info->name_len); 390862306a36Sopenharmony_ci fninfo->FileName[d_info->name_len] = 0x00; 390962306a36Sopenharmony_ci fninfo->FileNameLength = cpu_to_le32(d_info->name_len); 391062306a36Sopenharmony_ci fninfo->NextEntryOffset = cpu_to_le32(next_entry_offset); 391162306a36Sopenharmony_ci break; 391262306a36Sopenharmony_ci } 391362306a36Sopenharmony_ci case FILEID_FULL_DIRECTORY_INFORMATION: 391462306a36Sopenharmony_ci { 391562306a36Sopenharmony_ci struct file_id_full_dir_info *dinfo; 391662306a36Sopenharmony_ci 391762306a36Sopenharmony_ci dinfo = (struct file_id_full_dir_info *)d_info->wptr; 391862306a36Sopenharmony_ci memcpy(dinfo->FileName, d_info->name, d_info->name_len); 391962306a36Sopenharmony_ci dinfo->FileName[d_info->name_len] = 0x00; 392062306a36Sopenharmony_ci dinfo->FileNameLength = cpu_to_le32(d_info->name_len); 392162306a36Sopenharmony_ci dinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); 392262306a36Sopenharmony_ci break; 392362306a36Sopenharmony_ci } 392462306a36Sopenharmony_ci case FILEID_BOTH_DIRECTORY_INFORMATION: 392562306a36Sopenharmony_ci { 392662306a36Sopenharmony_ci struct file_id_both_directory_info *fibdinfo; 392762306a36Sopenharmony_ci 392862306a36Sopenharmony_ci fibdinfo = (struct file_id_both_directory_info *)d_info->wptr; 392962306a36Sopenharmony_ci memcpy(fibdinfo->FileName, d_info->name, d_info->name_len); 393062306a36Sopenharmony_ci fibdinfo->FileName[d_info->name_len] = 0x00; 393162306a36Sopenharmony_ci fibdinfo->FileNameLength = cpu_to_le32(d_info->name_len); 393262306a36Sopenharmony_ci fibdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); 393362306a36Sopenharmony_ci break; 393462306a36Sopenharmony_ci } 393562306a36Sopenharmony_ci case SMB_FIND_FILE_POSIX_INFO: 393662306a36Sopenharmony_ci { 393762306a36Sopenharmony_ci struct smb2_posix_info *posix_info; 393862306a36Sopenharmony_ci 393962306a36Sopenharmony_ci posix_info = (struct smb2_posix_info *)d_info->wptr; 394062306a36Sopenharmony_ci memcpy(posix_info->name, d_info->name, d_info->name_len); 394162306a36Sopenharmony_ci posix_info->name[d_info->name_len] = 0x00; 394262306a36Sopenharmony_ci posix_info->name_len = cpu_to_le32(d_info->name_len); 394362306a36Sopenharmony_ci posix_info->NextEntryOffset = 394462306a36Sopenharmony_ci cpu_to_le32(next_entry_offset); 394562306a36Sopenharmony_ci break; 394662306a36Sopenharmony_ci } 394762306a36Sopenharmony_ci } /* switch (info_level) */ 394862306a36Sopenharmony_ci 394962306a36Sopenharmony_ci d_info->num_entry++; 395062306a36Sopenharmony_ci d_info->out_buf_len -= next_entry_offset; 395162306a36Sopenharmony_ci d_info->wptr += next_entry_offset; 395262306a36Sopenharmony_ci return 0; 395362306a36Sopenharmony_ci} 395462306a36Sopenharmony_ci 395562306a36Sopenharmony_cistatic bool __query_dir(struct dir_context *ctx, const char *name, int namlen, 395662306a36Sopenharmony_ci loff_t offset, u64 ino, unsigned int d_type) 395762306a36Sopenharmony_ci{ 395862306a36Sopenharmony_ci struct ksmbd_readdir_data *buf; 395962306a36Sopenharmony_ci struct smb2_query_dir_private *priv; 396062306a36Sopenharmony_ci struct ksmbd_dir_info *d_info; 396162306a36Sopenharmony_ci int rc; 396262306a36Sopenharmony_ci 396362306a36Sopenharmony_ci buf = container_of(ctx, struct ksmbd_readdir_data, ctx); 396462306a36Sopenharmony_ci priv = buf->private; 396562306a36Sopenharmony_ci d_info = priv->d_info; 396662306a36Sopenharmony_ci 396762306a36Sopenharmony_ci /* dot and dotdot entries are already reserved */ 396862306a36Sopenharmony_ci if (!strcmp(".", name) || !strcmp("..", name)) 396962306a36Sopenharmony_ci return true; 397062306a36Sopenharmony_ci if (ksmbd_share_veto_filename(priv->work->tcon->share_conf, name)) 397162306a36Sopenharmony_ci return true; 397262306a36Sopenharmony_ci if (!match_pattern(name, namlen, priv->search_pattern)) 397362306a36Sopenharmony_ci return true; 397462306a36Sopenharmony_ci 397562306a36Sopenharmony_ci d_info->name = name; 397662306a36Sopenharmony_ci d_info->name_len = namlen; 397762306a36Sopenharmony_ci rc = reserve_populate_dentry(d_info, priv->info_level); 397862306a36Sopenharmony_ci if (rc) 397962306a36Sopenharmony_ci return false; 398062306a36Sopenharmony_ci if (d_info->flags & SMB2_RETURN_SINGLE_ENTRY) 398162306a36Sopenharmony_ci d_info->out_buf_len = 0; 398262306a36Sopenharmony_ci return true; 398362306a36Sopenharmony_ci} 398462306a36Sopenharmony_ci 398562306a36Sopenharmony_cistatic int verify_info_level(int info_level) 398662306a36Sopenharmony_ci{ 398762306a36Sopenharmony_ci switch (info_level) { 398862306a36Sopenharmony_ci case FILE_FULL_DIRECTORY_INFORMATION: 398962306a36Sopenharmony_ci case FILE_BOTH_DIRECTORY_INFORMATION: 399062306a36Sopenharmony_ci case FILE_DIRECTORY_INFORMATION: 399162306a36Sopenharmony_ci case FILE_NAMES_INFORMATION: 399262306a36Sopenharmony_ci case FILEID_FULL_DIRECTORY_INFORMATION: 399362306a36Sopenharmony_ci case FILEID_BOTH_DIRECTORY_INFORMATION: 399462306a36Sopenharmony_ci case SMB_FIND_FILE_POSIX_INFO: 399562306a36Sopenharmony_ci break; 399662306a36Sopenharmony_ci default: 399762306a36Sopenharmony_ci return -EOPNOTSUPP; 399862306a36Sopenharmony_ci } 399962306a36Sopenharmony_ci 400062306a36Sopenharmony_ci return 0; 400162306a36Sopenharmony_ci} 400262306a36Sopenharmony_ci 400362306a36Sopenharmony_cistatic int smb2_resp_buf_len(struct ksmbd_work *work, unsigned short hdr2_len) 400462306a36Sopenharmony_ci{ 400562306a36Sopenharmony_ci int free_len; 400662306a36Sopenharmony_ci 400762306a36Sopenharmony_ci free_len = (int)(work->response_sz - 400862306a36Sopenharmony_ci (get_rfc1002_len(work->response_buf) + 4)) - hdr2_len; 400962306a36Sopenharmony_ci return free_len; 401062306a36Sopenharmony_ci} 401162306a36Sopenharmony_ci 401262306a36Sopenharmony_cistatic int smb2_calc_max_out_buf_len(struct ksmbd_work *work, 401362306a36Sopenharmony_ci unsigned short hdr2_len, 401462306a36Sopenharmony_ci unsigned int out_buf_len) 401562306a36Sopenharmony_ci{ 401662306a36Sopenharmony_ci int free_len; 401762306a36Sopenharmony_ci 401862306a36Sopenharmony_ci if (out_buf_len > work->conn->vals->max_trans_size) 401962306a36Sopenharmony_ci return -EINVAL; 402062306a36Sopenharmony_ci 402162306a36Sopenharmony_ci free_len = smb2_resp_buf_len(work, hdr2_len); 402262306a36Sopenharmony_ci if (free_len < 0) 402362306a36Sopenharmony_ci return -EINVAL; 402462306a36Sopenharmony_ci 402562306a36Sopenharmony_ci return min_t(int, out_buf_len, free_len); 402662306a36Sopenharmony_ci} 402762306a36Sopenharmony_ci 402862306a36Sopenharmony_ciint smb2_query_dir(struct ksmbd_work *work) 402962306a36Sopenharmony_ci{ 403062306a36Sopenharmony_ci struct ksmbd_conn *conn = work->conn; 403162306a36Sopenharmony_ci struct smb2_query_directory_req *req; 403262306a36Sopenharmony_ci struct smb2_query_directory_rsp *rsp; 403362306a36Sopenharmony_ci struct ksmbd_share_config *share = work->tcon->share_conf; 403462306a36Sopenharmony_ci struct ksmbd_file *dir_fp = NULL; 403562306a36Sopenharmony_ci struct ksmbd_dir_info d_info; 403662306a36Sopenharmony_ci int rc = 0; 403762306a36Sopenharmony_ci char *srch_ptr = NULL; 403862306a36Sopenharmony_ci unsigned char srch_flag; 403962306a36Sopenharmony_ci int buffer_sz; 404062306a36Sopenharmony_ci struct smb2_query_dir_private query_dir_private = {NULL, }; 404162306a36Sopenharmony_ci 404262306a36Sopenharmony_ci WORK_BUFFERS(work, req, rsp); 404362306a36Sopenharmony_ci 404462306a36Sopenharmony_ci if (ksmbd_override_fsids(work)) { 404562306a36Sopenharmony_ci rsp->hdr.Status = STATUS_NO_MEMORY; 404662306a36Sopenharmony_ci smb2_set_err_rsp(work); 404762306a36Sopenharmony_ci return -ENOMEM; 404862306a36Sopenharmony_ci } 404962306a36Sopenharmony_ci 405062306a36Sopenharmony_ci rc = verify_info_level(req->FileInformationClass); 405162306a36Sopenharmony_ci if (rc) { 405262306a36Sopenharmony_ci rc = -EFAULT; 405362306a36Sopenharmony_ci goto err_out2; 405462306a36Sopenharmony_ci } 405562306a36Sopenharmony_ci 405662306a36Sopenharmony_ci dir_fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); 405762306a36Sopenharmony_ci if (!dir_fp) { 405862306a36Sopenharmony_ci rc = -EBADF; 405962306a36Sopenharmony_ci goto err_out2; 406062306a36Sopenharmony_ci } 406162306a36Sopenharmony_ci 406262306a36Sopenharmony_ci if (!(dir_fp->daccess & FILE_LIST_DIRECTORY_LE) || 406362306a36Sopenharmony_ci inode_permission(file_mnt_idmap(dir_fp->filp), 406462306a36Sopenharmony_ci file_inode(dir_fp->filp), 406562306a36Sopenharmony_ci MAY_READ | MAY_EXEC)) { 406662306a36Sopenharmony_ci pr_err("no right to enumerate directory (%pD)\n", dir_fp->filp); 406762306a36Sopenharmony_ci rc = -EACCES; 406862306a36Sopenharmony_ci goto err_out2; 406962306a36Sopenharmony_ci } 407062306a36Sopenharmony_ci 407162306a36Sopenharmony_ci if (!S_ISDIR(file_inode(dir_fp->filp)->i_mode)) { 407262306a36Sopenharmony_ci pr_err("can't do query dir for a file\n"); 407362306a36Sopenharmony_ci rc = -EINVAL; 407462306a36Sopenharmony_ci goto err_out2; 407562306a36Sopenharmony_ci } 407662306a36Sopenharmony_ci 407762306a36Sopenharmony_ci srch_flag = req->Flags; 407862306a36Sopenharmony_ci srch_ptr = smb_strndup_from_utf16(req->Buffer, 407962306a36Sopenharmony_ci le16_to_cpu(req->FileNameLength), 1, 408062306a36Sopenharmony_ci conn->local_nls); 408162306a36Sopenharmony_ci if (IS_ERR(srch_ptr)) { 408262306a36Sopenharmony_ci ksmbd_debug(SMB, "Search Pattern not found\n"); 408362306a36Sopenharmony_ci rc = -EINVAL; 408462306a36Sopenharmony_ci goto err_out2; 408562306a36Sopenharmony_ci } else { 408662306a36Sopenharmony_ci ksmbd_debug(SMB, "Search pattern is %s\n", srch_ptr); 408762306a36Sopenharmony_ci } 408862306a36Sopenharmony_ci 408962306a36Sopenharmony_ci if (srch_flag & SMB2_REOPEN || srch_flag & SMB2_RESTART_SCANS) { 409062306a36Sopenharmony_ci ksmbd_debug(SMB, "Restart directory scan\n"); 409162306a36Sopenharmony_ci generic_file_llseek(dir_fp->filp, 0, SEEK_SET); 409262306a36Sopenharmony_ci } 409362306a36Sopenharmony_ci 409462306a36Sopenharmony_ci memset(&d_info, 0, sizeof(struct ksmbd_dir_info)); 409562306a36Sopenharmony_ci d_info.wptr = (char *)rsp->Buffer; 409662306a36Sopenharmony_ci d_info.rptr = (char *)rsp->Buffer; 409762306a36Sopenharmony_ci d_info.out_buf_len = 409862306a36Sopenharmony_ci smb2_calc_max_out_buf_len(work, 8, 409962306a36Sopenharmony_ci le32_to_cpu(req->OutputBufferLength)); 410062306a36Sopenharmony_ci if (d_info.out_buf_len < 0) { 410162306a36Sopenharmony_ci rc = -EINVAL; 410262306a36Sopenharmony_ci goto err_out; 410362306a36Sopenharmony_ci } 410462306a36Sopenharmony_ci d_info.flags = srch_flag; 410562306a36Sopenharmony_ci 410662306a36Sopenharmony_ci /* 410762306a36Sopenharmony_ci * reserve dot and dotdot entries in head of buffer 410862306a36Sopenharmony_ci * in first response 410962306a36Sopenharmony_ci */ 411062306a36Sopenharmony_ci rc = ksmbd_populate_dot_dotdot_entries(work, req->FileInformationClass, 411162306a36Sopenharmony_ci dir_fp, &d_info, srch_ptr, 411262306a36Sopenharmony_ci smb2_populate_readdir_entry); 411362306a36Sopenharmony_ci if (rc == -ENOSPC) 411462306a36Sopenharmony_ci rc = 0; 411562306a36Sopenharmony_ci else if (rc) 411662306a36Sopenharmony_ci goto err_out; 411762306a36Sopenharmony_ci 411862306a36Sopenharmony_ci if (test_share_config_flag(share, KSMBD_SHARE_FLAG_HIDE_DOT_FILES)) 411962306a36Sopenharmony_ci d_info.hide_dot_file = true; 412062306a36Sopenharmony_ci 412162306a36Sopenharmony_ci buffer_sz = d_info.out_buf_len; 412262306a36Sopenharmony_ci d_info.rptr = d_info.wptr; 412362306a36Sopenharmony_ci query_dir_private.work = work; 412462306a36Sopenharmony_ci query_dir_private.search_pattern = srch_ptr; 412562306a36Sopenharmony_ci query_dir_private.dir_fp = dir_fp; 412662306a36Sopenharmony_ci query_dir_private.d_info = &d_info; 412762306a36Sopenharmony_ci query_dir_private.info_level = req->FileInformationClass; 412862306a36Sopenharmony_ci dir_fp->readdir_data.private = &query_dir_private; 412962306a36Sopenharmony_ci set_ctx_actor(&dir_fp->readdir_data.ctx, __query_dir); 413062306a36Sopenharmony_ci 413162306a36Sopenharmony_ci rc = iterate_dir(dir_fp->filp, &dir_fp->readdir_data.ctx); 413262306a36Sopenharmony_ci /* 413362306a36Sopenharmony_ci * req->OutputBufferLength is too small to contain even one entry. 413462306a36Sopenharmony_ci * In this case, it immediately returns OutputBufferLength 0 to client. 413562306a36Sopenharmony_ci */ 413662306a36Sopenharmony_ci if (!d_info.out_buf_len && !d_info.num_entry) 413762306a36Sopenharmony_ci goto no_buf_len; 413862306a36Sopenharmony_ci if (rc > 0 || rc == -ENOSPC) 413962306a36Sopenharmony_ci rc = 0; 414062306a36Sopenharmony_ci else if (rc) 414162306a36Sopenharmony_ci goto err_out; 414262306a36Sopenharmony_ci 414362306a36Sopenharmony_ci d_info.wptr = d_info.rptr; 414462306a36Sopenharmony_ci d_info.out_buf_len = buffer_sz; 414562306a36Sopenharmony_ci rc = process_query_dir_entries(&query_dir_private); 414662306a36Sopenharmony_ci if (rc) 414762306a36Sopenharmony_ci goto err_out; 414862306a36Sopenharmony_ci 414962306a36Sopenharmony_ci if (!d_info.data_count && d_info.out_buf_len >= 0) { 415062306a36Sopenharmony_ci if (srch_flag & SMB2_RETURN_SINGLE_ENTRY && !is_asterisk(srch_ptr)) { 415162306a36Sopenharmony_ci rsp->hdr.Status = STATUS_NO_SUCH_FILE; 415262306a36Sopenharmony_ci } else { 415362306a36Sopenharmony_ci dir_fp->dot_dotdot[0] = dir_fp->dot_dotdot[1] = 0; 415462306a36Sopenharmony_ci rsp->hdr.Status = STATUS_NO_MORE_FILES; 415562306a36Sopenharmony_ci } 415662306a36Sopenharmony_ci rsp->StructureSize = cpu_to_le16(9); 415762306a36Sopenharmony_ci rsp->OutputBufferOffset = cpu_to_le16(0); 415862306a36Sopenharmony_ci rsp->OutputBufferLength = cpu_to_le32(0); 415962306a36Sopenharmony_ci rsp->Buffer[0] = 0; 416062306a36Sopenharmony_ci rc = ksmbd_iov_pin_rsp(work, (void *)rsp, 416162306a36Sopenharmony_ci sizeof(struct smb2_query_directory_rsp)); 416262306a36Sopenharmony_ci if (rc) 416362306a36Sopenharmony_ci goto err_out; 416462306a36Sopenharmony_ci } else { 416562306a36Sopenharmony_cino_buf_len: 416662306a36Sopenharmony_ci ((struct file_directory_info *) 416762306a36Sopenharmony_ci ((char *)rsp->Buffer + d_info.last_entry_offset)) 416862306a36Sopenharmony_ci ->NextEntryOffset = 0; 416962306a36Sopenharmony_ci if (d_info.data_count >= d_info.last_entry_off_align) 417062306a36Sopenharmony_ci d_info.data_count -= d_info.last_entry_off_align; 417162306a36Sopenharmony_ci 417262306a36Sopenharmony_ci rsp->StructureSize = cpu_to_le16(9); 417362306a36Sopenharmony_ci rsp->OutputBufferOffset = cpu_to_le16(72); 417462306a36Sopenharmony_ci rsp->OutputBufferLength = cpu_to_le32(d_info.data_count); 417562306a36Sopenharmony_ci rc = ksmbd_iov_pin_rsp(work, (void *)rsp, 417662306a36Sopenharmony_ci offsetof(struct smb2_query_directory_rsp, Buffer) + 417762306a36Sopenharmony_ci d_info.data_count); 417862306a36Sopenharmony_ci if (rc) 417962306a36Sopenharmony_ci goto err_out; 418062306a36Sopenharmony_ci } 418162306a36Sopenharmony_ci 418262306a36Sopenharmony_ci kfree(srch_ptr); 418362306a36Sopenharmony_ci ksmbd_fd_put(work, dir_fp); 418462306a36Sopenharmony_ci ksmbd_revert_fsids(work); 418562306a36Sopenharmony_ci return 0; 418662306a36Sopenharmony_ci 418762306a36Sopenharmony_cierr_out: 418862306a36Sopenharmony_ci pr_err("error while processing smb2 query dir rc = %d\n", rc); 418962306a36Sopenharmony_ci kfree(srch_ptr); 419062306a36Sopenharmony_ci 419162306a36Sopenharmony_cierr_out2: 419262306a36Sopenharmony_ci if (rc == -EINVAL) 419362306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_PARAMETER; 419462306a36Sopenharmony_ci else if (rc == -EACCES) 419562306a36Sopenharmony_ci rsp->hdr.Status = STATUS_ACCESS_DENIED; 419662306a36Sopenharmony_ci else if (rc == -ENOENT) 419762306a36Sopenharmony_ci rsp->hdr.Status = STATUS_NO_SUCH_FILE; 419862306a36Sopenharmony_ci else if (rc == -EBADF) 419962306a36Sopenharmony_ci rsp->hdr.Status = STATUS_FILE_CLOSED; 420062306a36Sopenharmony_ci else if (rc == -ENOMEM) 420162306a36Sopenharmony_ci rsp->hdr.Status = STATUS_NO_MEMORY; 420262306a36Sopenharmony_ci else if (rc == -EFAULT) 420362306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_INFO_CLASS; 420462306a36Sopenharmony_ci else if (rc == -EIO) 420562306a36Sopenharmony_ci rsp->hdr.Status = STATUS_FILE_CORRUPT_ERROR; 420662306a36Sopenharmony_ci if (!rsp->hdr.Status) 420762306a36Sopenharmony_ci rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; 420862306a36Sopenharmony_ci 420962306a36Sopenharmony_ci smb2_set_err_rsp(work); 421062306a36Sopenharmony_ci ksmbd_fd_put(work, dir_fp); 421162306a36Sopenharmony_ci ksmbd_revert_fsids(work); 421262306a36Sopenharmony_ci return 0; 421362306a36Sopenharmony_ci} 421462306a36Sopenharmony_ci 421562306a36Sopenharmony_ci/** 421662306a36Sopenharmony_ci * buffer_check_err() - helper function to check buffer errors 421762306a36Sopenharmony_ci * @reqOutputBufferLength: max buffer length expected in command response 421862306a36Sopenharmony_ci * @rsp: query info response buffer contains output buffer length 421962306a36Sopenharmony_ci * @rsp_org: base response buffer pointer in case of chained response 422062306a36Sopenharmony_ci * 422162306a36Sopenharmony_ci * Return: 0 on success, otherwise error 422262306a36Sopenharmony_ci */ 422362306a36Sopenharmony_cistatic int buffer_check_err(int reqOutputBufferLength, 422462306a36Sopenharmony_ci struct smb2_query_info_rsp *rsp, 422562306a36Sopenharmony_ci void *rsp_org) 422662306a36Sopenharmony_ci{ 422762306a36Sopenharmony_ci if (reqOutputBufferLength < le32_to_cpu(rsp->OutputBufferLength)) { 422862306a36Sopenharmony_ci pr_err("Invalid Buffer Size Requested\n"); 422962306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INFO_LENGTH_MISMATCH; 423062306a36Sopenharmony_ci *(__be32 *)rsp_org = cpu_to_be32(sizeof(struct smb2_hdr)); 423162306a36Sopenharmony_ci return -EINVAL; 423262306a36Sopenharmony_ci } 423362306a36Sopenharmony_ci return 0; 423462306a36Sopenharmony_ci} 423562306a36Sopenharmony_ci 423662306a36Sopenharmony_cistatic void get_standard_info_pipe(struct smb2_query_info_rsp *rsp, 423762306a36Sopenharmony_ci void *rsp_org) 423862306a36Sopenharmony_ci{ 423962306a36Sopenharmony_ci struct smb2_file_standard_info *sinfo; 424062306a36Sopenharmony_ci 424162306a36Sopenharmony_ci sinfo = (struct smb2_file_standard_info *)rsp->Buffer; 424262306a36Sopenharmony_ci 424362306a36Sopenharmony_ci sinfo->AllocationSize = cpu_to_le64(4096); 424462306a36Sopenharmony_ci sinfo->EndOfFile = cpu_to_le64(0); 424562306a36Sopenharmony_ci sinfo->NumberOfLinks = cpu_to_le32(1); 424662306a36Sopenharmony_ci sinfo->DeletePending = 1; 424762306a36Sopenharmony_ci sinfo->Directory = 0; 424862306a36Sopenharmony_ci rsp->OutputBufferLength = 424962306a36Sopenharmony_ci cpu_to_le32(sizeof(struct smb2_file_standard_info)); 425062306a36Sopenharmony_ci} 425162306a36Sopenharmony_ci 425262306a36Sopenharmony_cistatic void get_internal_info_pipe(struct smb2_query_info_rsp *rsp, u64 num, 425362306a36Sopenharmony_ci void *rsp_org) 425462306a36Sopenharmony_ci{ 425562306a36Sopenharmony_ci struct smb2_file_internal_info *file_info; 425662306a36Sopenharmony_ci 425762306a36Sopenharmony_ci file_info = (struct smb2_file_internal_info *)rsp->Buffer; 425862306a36Sopenharmony_ci 425962306a36Sopenharmony_ci /* any unique number */ 426062306a36Sopenharmony_ci file_info->IndexNumber = cpu_to_le64(num | (1ULL << 63)); 426162306a36Sopenharmony_ci rsp->OutputBufferLength = 426262306a36Sopenharmony_ci cpu_to_le32(sizeof(struct smb2_file_internal_info)); 426362306a36Sopenharmony_ci} 426462306a36Sopenharmony_ci 426562306a36Sopenharmony_cistatic int smb2_get_info_file_pipe(struct ksmbd_session *sess, 426662306a36Sopenharmony_ci struct smb2_query_info_req *req, 426762306a36Sopenharmony_ci struct smb2_query_info_rsp *rsp, 426862306a36Sopenharmony_ci void *rsp_org) 426962306a36Sopenharmony_ci{ 427062306a36Sopenharmony_ci u64 id; 427162306a36Sopenharmony_ci int rc; 427262306a36Sopenharmony_ci 427362306a36Sopenharmony_ci /* 427462306a36Sopenharmony_ci * Windows can sometime send query file info request on 427562306a36Sopenharmony_ci * pipe without opening it, checking error condition here 427662306a36Sopenharmony_ci */ 427762306a36Sopenharmony_ci id = req->VolatileFileId; 427862306a36Sopenharmony_ci if (!ksmbd_session_rpc_method(sess, id)) 427962306a36Sopenharmony_ci return -ENOENT; 428062306a36Sopenharmony_ci 428162306a36Sopenharmony_ci ksmbd_debug(SMB, "FileInfoClass %u, FileId 0x%llx\n", 428262306a36Sopenharmony_ci req->FileInfoClass, req->VolatileFileId); 428362306a36Sopenharmony_ci 428462306a36Sopenharmony_ci switch (req->FileInfoClass) { 428562306a36Sopenharmony_ci case FILE_STANDARD_INFORMATION: 428662306a36Sopenharmony_ci get_standard_info_pipe(rsp, rsp_org); 428762306a36Sopenharmony_ci rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), 428862306a36Sopenharmony_ci rsp, rsp_org); 428962306a36Sopenharmony_ci break; 429062306a36Sopenharmony_ci case FILE_INTERNAL_INFORMATION: 429162306a36Sopenharmony_ci get_internal_info_pipe(rsp, id, rsp_org); 429262306a36Sopenharmony_ci rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), 429362306a36Sopenharmony_ci rsp, rsp_org); 429462306a36Sopenharmony_ci break; 429562306a36Sopenharmony_ci default: 429662306a36Sopenharmony_ci ksmbd_debug(SMB, "smb2_info_file_pipe for %u not supported\n", 429762306a36Sopenharmony_ci req->FileInfoClass); 429862306a36Sopenharmony_ci rc = -EOPNOTSUPP; 429962306a36Sopenharmony_ci } 430062306a36Sopenharmony_ci return rc; 430162306a36Sopenharmony_ci} 430262306a36Sopenharmony_ci 430362306a36Sopenharmony_ci/** 430462306a36Sopenharmony_ci * smb2_get_ea() - handler for smb2 get extended attribute command 430562306a36Sopenharmony_ci * @work: smb work containing query info command buffer 430662306a36Sopenharmony_ci * @fp: ksmbd_file pointer 430762306a36Sopenharmony_ci * @req: get extended attribute request 430862306a36Sopenharmony_ci * @rsp: response buffer pointer 430962306a36Sopenharmony_ci * @rsp_org: base response buffer pointer in case of chained response 431062306a36Sopenharmony_ci * 431162306a36Sopenharmony_ci * Return: 0 on success, otherwise error 431262306a36Sopenharmony_ci */ 431362306a36Sopenharmony_cistatic int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp, 431462306a36Sopenharmony_ci struct smb2_query_info_req *req, 431562306a36Sopenharmony_ci struct smb2_query_info_rsp *rsp, void *rsp_org) 431662306a36Sopenharmony_ci{ 431762306a36Sopenharmony_ci struct smb2_ea_info *eainfo, *prev_eainfo; 431862306a36Sopenharmony_ci char *name, *ptr, *xattr_list = NULL, *buf; 431962306a36Sopenharmony_ci int rc, name_len, value_len, xattr_list_len, idx; 432062306a36Sopenharmony_ci ssize_t buf_free_len, alignment_bytes, next_offset, rsp_data_cnt = 0; 432162306a36Sopenharmony_ci struct smb2_ea_info_req *ea_req = NULL; 432262306a36Sopenharmony_ci const struct path *path; 432362306a36Sopenharmony_ci struct mnt_idmap *idmap = file_mnt_idmap(fp->filp); 432462306a36Sopenharmony_ci 432562306a36Sopenharmony_ci if (!(fp->daccess & FILE_READ_EA_LE)) { 432662306a36Sopenharmony_ci pr_err("Not permitted to read ext attr : 0x%x\n", 432762306a36Sopenharmony_ci fp->daccess); 432862306a36Sopenharmony_ci return -EACCES; 432962306a36Sopenharmony_ci } 433062306a36Sopenharmony_ci 433162306a36Sopenharmony_ci path = &fp->filp->f_path; 433262306a36Sopenharmony_ci /* single EA entry is requested with given user.* name */ 433362306a36Sopenharmony_ci if (req->InputBufferLength) { 433462306a36Sopenharmony_ci if (le32_to_cpu(req->InputBufferLength) < 433562306a36Sopenharmony_ci sizeof(struct smb2_ea_info_req)) 433662306a36Sopenharmony_ci return -EINVAL; 433762306a36Sopenharmony_ci 433862306a36Sopenharmony_ci ea_req = (struct smb2_ea_info_req *)req->Buffer; 433962306a36Sopenharmony_ci } else { 434062306a36Sopenharmony_ci /* need to send all EAs, if no specific EA is requested*/ 434162306a36Sopenharmony_ci if (le32_to_cpu(req->Flags) & SL_RETURN_SINGLE_ENTRY) 434262306a36Sopenharmony_ci ksmbd_debug(SMB, 434362306a36Sopenharmony_ci "All EAs are requested but need to send single EA entry in rsp flags 0x%x\n", 434462306a36Sopenharmony_ci le32_to_cpu(req->Flags)); 434562306a36Sopenharmony_ci } 434662306a36Sopenharmony_ci 434762306a36Sopenharmony_ci buf_free_len = 434862306a36Sopenharmony_ci smb2_calc_max_out_buf_len(work, 8, 434962306a36Sopenharmony_ci le32_to_cpu(req->OutputBufferLength)); 435062306a36Sopenharmony_ci if (buf_free_len < 0) 435162306a36Sopenharmony_ci return -EINVAL; 435262306a36Sopenharmony_ci 435362306a36Sopenharmony_ci rc = ksmbd_vfs_listxattr(path->dentry, &xattr_list); 435462306a36Sopenharmony_ci if (rc < 0) { 435562306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_HANDLE; 435662306a36Sopenharmony_ci goto out; 435762306a36Sopenharmony_ci } else if (!rc) { /* there is no EA in the file */ 435862306a36Sopenharmony_ci ksmbd_debug(SMB, "no ea data in the file\n"); 435962306a36Sopenharmony_ci goto done; 436062306a36Sopenharmony_ci } 436162306a36Sopenharmony_ci xattr_list_len = rc; 436262306a36Sopenharmony_ci 436362306a36Sopenharmony_ci ptr = (char *)rsp->Buffer; 436462306a36Sopenharmony_ci eainfo = (struct smb2_ea_info *)ptr; 436562306a36Sopenharmony_ci prev_eainfo = eainfo; 436662306a36Sopenharmony_ci idx = 0; 436762306a36Sopenharmony_ci 436862306a36Sopenharmony_ci while (idx < xattr_list_len) { 436962306a36Sopenharmony_ci name = xattr_list + idx; 437062306a36Sopenharmony_ci name_len = strlen(name); 437162306a36Sopenharmony_ci 437262306a36Sopenharmony_ci ksmbd_debug(SMB, "%s, len %d\n", name, name_len); 437362306a36Sopenharmony_ci idx += name_len + 1; 437462306a36Sopenharmony_ci 437562306a36Sopenharmony_ci /* 437662306a36Sopenharmony_ci * CIFS does not support EA other than user.* namespace, 437762306a36Sopenharmony_ci * still keep the framework generic, to list other attrs 437862306a36Sopenharmony_ci * in future. 437962306a36Sopenharmony_ci */ 438062306a36Sopenharmony_ci if (strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) 438162306a36Sopenharmony_ci continue; 438262306a36Sopenharmony_ci 438362306a36Sopenharmony_ci if (!strncmp(&name[XATTR_USER_PREFIX_LEN], STREAM_PREFIX, 438462306a36Sopenharmony_ci STREAM_PREFIX_LEN)) 438562306a36Sopenharmony_ci continue; 438662306a36Sopenharmony_ci 438762306a36Sopenharmony_ci if (req->InputBufferLength && 438862306a36Sopenharmony_ci strncmp(&name[XATTR_USER_PREFIX_LEN], ea_req->name, 438962306a36Sopenharmony_ci ea_req->EaNameLength)) 439062306a36Sopenharmony_ci continue; 439162306a36Sopenharmony_ci 439262306a36Sopenharmony_ci if (!strncmp(&name[XATTR_USER_PREFIX_LEN], 439362306a36Sopenharmony_ci DOS_ATTRIBUTE_PREFIX, DOS_ATTRIBUTE_PREFIX_LEN)) 439462306a36Sopenharmony_ci continue; 439562306a36Sopenharmony_ci 439662306a36Sopenharmony_ci if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) 439762306a36Sopenharmony_ci name_len -= XATTR_USER_PREFIX_LEN; 439862306a36Sopenharmony_ci 439962306a36Sopenharmony_ci ptr = eainfo->name + name_len + 1; 440062306a36Sopenharmony_ci buf_free_len -= (offsetof(struct smb2_ea_info, name) + 440162306a36Sopenharmony_ci name_len + 1); 440262306a36Sopenharmony_ci /* bailout if xattr can't fit in buf_free_len */ 440362306a36Sopenharmony_ci value_len = ksmbd_vfs_getxattr(idmap, path->dentry, 440462306a36Sopenharmony_ci name, &buf); 440562306a36Sopenharmony_ci if (value_len <= 0) { 440662306a36Sopenharmony_ci rc = -ENOENT; 440762306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_HANDLE; 440862306a36Sopenharmony_ci goto out; 440962306a36Sopenharmony_ci } 441062306a36Sopenharmony_ci 441162306a36Sopenharmony_ci buf_free_len -= value_len; 441262306a36Sopenharmony_ci if (buf_free_len < 0) { 441362306a36Sopenharmony_ci kfree(buf); 441462306a36Sopenharmony_ci break; 441562306a36Sopenharmony_ci } 441662306a36Sopenharmony_ci 441762306a36Sopenharmony_ci memcpy(ptr, buf, value_len); 441862306a36Sopenharmony_ci kfree(buf); 441962306a36Sopenharmony_ci 442062306a36Sopenharmony_ci ptr += value_len; 442162306a36Sopenharmony_ci eainfo->Flags = 0; 442262306a36Sopenharmony_ci eainfo->EaNameLength = name_len; 442362306a36Sopenharmony_ci 442462306a36Sopenharmony_ci if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) 442562306a36Sopenharmony_ci memcpy(eainfo->name, &name[XATTR_USER_PREFIX_LEN], 442662306a36Sopenharmony_ci name_len); 442762306a36Sopenharmony_ci else 442862306a36Sopenharmony_ci memcpy(eainfo->name, name, name_len); 442962306a36Sopenharmony_ci 443062306a36Sopenharmony_ci eainfo->name[name_len] = '\0'; 443162306a36Sopenharmony_ci eainfo->EaValueLength = cpu_to_le16(value_len); 443262306a36Sopenharmony_ci next_offset = offsetof(struct smb2_ea_info, name) + 443362306a36Sopenharmony_ci name_len + 1 + value_len; 443462306a36Sopenharmony_ci 443562306a36Sopenharmony_ci /* align next xattr entry at 4 byte bundary */ 443662306a36Sopenharmony_ci alignment_bytes = ((next_offset + 3) & ~3) - next_offset; 443762306a36Sopenharmony_ci if (alignment_bytes) { 443862306a36Sopenharmony_ci memset(ptr, '\0', alignment_bytes); 443962306a36Sopenharmony_ci ptr += alignment_bytes; 444062306a36Sopenharmony_ci next_offset += alignment_bytes; 444162306a36Sopenharmony_ci buf_free_len -= alignment_bytes; 444262306a36Sopenharmony_ci } 444362306a36Sopenharmony_ci eainfo->NextEntryOffset = cpu_to_le32(next_offset); 444462306a36Sopenharmony_ci prev_eainfo = eainfo; 444562306a36Sopenharmony_ci eainfo = (struct smb2_ea_info *)ptr; 444662306a36Sopenharmony_ci rsp_data_cnt += next_offset; 444762306a36Sopenharmony_ci 444862306a36Sopenharmony_ci if (req->InputBufferLength) { 444962306a36Sopenharmony_ci ksmbd_debug(SMB, "single entry requested\n"); 445062306a36Sopenharmony_ci break; 445162306a36Sopenharmony_ci } 445262306a36Sopenharmony_ci } 445362306a36Sopenharmony_ci 445462306a36Sopenharmony_ci /* no more ea entries */ 445562306a36Sopenharmony_ci prev_eainfo->NextEntryOffset = 0; 445662306a36Sopenharmony_cidone: 445762306a36Sopenharmony_ci rc = 0; 445862306a36Sopenharmony_ci if (rsp_data_cnt == 0) 445962306a36Sopenharmony_ci rsp->hdr.Status = STATUS_NO_EAS_ON_FILE; 446062306a36Sopenharmony_ci rsp->OutputBufferLength = cpu_to_le32(rsp_data_cnt); 446162306a36Sopenharmony_ciout: 446262306a36Sopenharmony_ci kvfree(xattr_list); 446362306a36Sopenharmony_ci return rc; 446462306a36Sopenharmony_ci} 446562306a36Sopenharmony_ci 446662306a36Sopenharmony_cistatic void get_file_access_info(struct smb2_query_info_rsp *rsp, 446762306a36Sopenharmony_ci struct ksmbd_file *fp, void *rsp_org) 446862306a36Sopenharmony_ci{ 446962306a36Sopenharmony_ci struct smb2_file_access_info *file_info; 447062306a36Sopenharmony_ci 447162306a36Sopenharmony_ci file_info = (struct smb2_file_access_info *)rsp->Buffer; 447262306a36Sopenharmony_ci file_info->AccessFlags = fp->daccess; 447362306a36Sopenharmony_ci rsp->OutputBufferLength = 447462306a36Sopenharmony_ci cpu_to_le32(sizeof(struct smb2_file_access_info)); 447562306a36Sopenharmony_ci} 447662306a36Sopenharmony_ci 447762306a36Sopenharmony_cistatic int get_file_basic_info(struct smb2_query_info_rsp *rsp, 447862306a36Sopenharmony_ci struct ksmbd_file *fp, void *rsp_org) 447962306a36Sopenharmony_ci{ 448062306a36Sopenharmony_ci struct smb2_file_basic_info *basic_info; 448162306a36Sopenharmony_ci struct kstat stat; 448262306a36Sopenharmony_ci u64 time; 448362306a36Sopenharmony_ci 448462306a36Sopenharmony_ci if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { 448562306a36Sopenharmony_ci pr_err("no right to read the attributes : 0x%x\n", 448662306a36Sopenharmony_ci fp->daccess); 448762306a36Sopenharmony_ci return -EACCES; 448862306a36Sopenharmony_ci } 448962306a36Sopenharmony_ci 449062306a36Sopenharmony_ci basic_info = (struct smb2_file_basic_info *)rsp->Buffer; 449162306a36Sopenharmony_ci generic_fillattr(file_mnt_idmap(fp->filp), STATX_BASIC_STATS, 449262306a36Sopenharmony_ci file_inode(fp->filp), &stat); 449362306a36Sopenharmony_ci basic_info->CreationTime = cpu_to_le64(fp->create_time); 449462306a36Sopenharmony_ci time = ksmbd_UnixTimeToNT(stat.atime); 449562306a36Sopenharmony_ci basic_info->LastAccessTime = cpu_to_le64(time); 449662306a36Sopenharmony_ci time = ksmbd_UnixTimeToNT(stat.mtime); 449762306a36Sopenharmony_ci basic_info->LastWriteTime = cpu_to_le64(time); 449862306a36Sopenharmony_ci time = ksmbd_UnixTimeToNT(stat.ctime); 449962306a36Sopenharmony_ci basic_info->ChangeTime = cpu_to_le64(time); 450062306a36Sopenharmony_ci basic_info->Attributes = fp->f_ci->m_fattr; 450162306a36Sopenharmony_ci basic_info->Pad1 = 0; 450262306a36Sopenharmony_ci rsp->OutputBufferLength = 450362306a36Sopenharmony_ci cpu_to_le32(sizeof(struct smb2_file_basic_info)); 450462306a36Sopenharmony_ci return 0; 450562306a36Sopenharmony_ci} 450662306a36Sopenharmony_ci 450762306a36Sopenharmony_cistatic void get_file_standard_info(struct smb2_query_info_rsp *rsp, 450862306a36Sopenharmony_ci struct ksmbd_file *fp, void *rsp_org) 450962306a36Sopenharmony_ci{ 451062306a36Sopenharmony_ci struct smb2_file_standard_info *sinfo; 451162306a36Sopenharmony_ci unsigned int delete_pending; 451262306a36Sopenharmony_ci struct inode *inode; 451362306a36Sopenharmony_ci struct kstat stat; 451462306a36Sopenharmony_ci 451562306a36Sopenharmony_ci inode = file_inode(fp->filp); 451662306a36Sopenharmony_ci generic_fillattr(file_mnt_idmap(fp->filp), STATX_BASIC_STATS, inode, &stat); 451762306a36Sopenharmony_ci 451862306a36Sopenharmony_ci sinfo = (struct smb2_file_standard_info *)rsp->Buffer; 451962306a36Sopenharmony_ci delete_pending = ksmbd_inode_pending_delete(fp); 452062306a36Sopenharmony_ci 452162306a36Sopenharmony_ci sinfo->AllocationSize = cpu_to_le64(inode->i_blocks << 9); 452262306a36Sopenharmony_ci sinfo->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); 452362306a36Sopenharmony_ci sinfo->NumberOfLinks = cpu_to_le32(get_nlink(&stat) - delete_pending); 452462306a36Sopenharmony_ci sinfo->DeletePending = delete_pending; 452562306a36Sopenharmony_ci sinfo->Directory = S_ISDIR(stat.mode) ? 1 : 0; 452662306a36Sopenharmony_ci rsp->OutputBufferLength = 452762306a36Sopenharmony_ci cpu_to_le32(sizeof(struct smb2_file_standard_info)); 452862306a36Sopenharmony_ci} 452962306a36Sopenharmony_ci 453062306a36Sopenharmony_cistatic void get_file_alignment_info(struct smb2_query_info_rsp *rsp, 453162306a36Sopenharmony_ci void *rsp_org) 453262306a36Sopenharmony_ci{ 453362306a36Sopenharmony_ci struct smb2_file_alignment_info *file_info; 453462306a36Sopenharmony_ci 453562306a36Sopenharmony_ci file_info = (struct smb2_file_alignment_info *)rsp->Buffer; 453662306a36Sopenharmony_ci file_info->AlignmentRequirement = 0; 453762306a36Sopenharmony_ci rsp->OutputBufferLength = 453862306a36Sopenharmony_ci cpu_to_le32(sizeof(struct smb2_file_alignment_info)); 453962306a36Sopenharmony_ci} 454062306a36Sopenharmony_ci 454162306a36Sopenharmony_cistatic int get_file_all_info(struct ksmbd_work *work, 454262306a36Sopenharmony_ci struct smb2_query_info_rsp *rsp, 454362306a36Sopenharmony_ci struct ksmbd_file *fp, 454462306a36Sopenharmony_ci void *rsp_org) 454562306a36Sopenharmony_ci{ 454662306a36Sopenharmony_ci struct ksmbd_conn *conn = work->conn; 454762306a36Sopenharmony_ci struct smb2_file_all_info *file_info; 454862306a36Sopenharmony_ci unsigned int delete_pending; 454962306a36Sopenharmony_ci struct inode *inode; 455062306a36Sopenharmony_ci struct kstat stat; 455162306a36Sopenharmony_ci int conv_len; 455262306a36Sopenharmony_ci char *filename; 455362306a36Sopenharmony_ci u64 time; 455462306a36Sopenharmony_ci 455562306a36Sopenharmony_ci if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { 455662306a36Sopenharmony_ci ksmbd_debug(SMB, "no right to read the attributes : 0x%x\n", 455762306a36Sopenharmony_ci fp->daccess); 455862306a36Sopenharmony_ci return -EACCES; 455962306a36Sopenharmony_ci } 456062306a36Sopenharmony_ci 456162306a36Sopenharmony_ci filename = convert_to_nt_pathname(work->tcon->share_conf, &fp->filp->f_path); 456262306a36Sopenharmony_ci if (IS_ERR(filename)) 456362306a36Sopenharmony_ci return PTR_ERR(filename); 456462306a36Sopenharmony_ci 456562306a36Sopenharmony_ci inode = file_inode(fp->filp); 456662306a36Sopenharmony_ci generic_fillattr(file_mnt_idmap(fp->filp), STATX_BASIC_STATS, inode, &stat); 456762306a36Sopenharmony_ci 456862306a36Sopenharmony_ci ksmbd_debug(SMB, "filename = %s\n", filename); 456962306a36Sopenharmony_ci delete_pending = ksmbd_inode_pending_delete(fp); 457062306a36Sopenharmony_ci file_info = (struct smb2_file_all_info *)rsp->Buffer; 457162306a36Sopenharmony_ci 457262306a36Sopenharmony_ci file_info->CreationTime = cpu_to_le64(fp->create_time); 457362306a36Sopenharmony_ci time = ksmbd_UnixTimeToNT(stat.atime); 457462306a36Sopenharmony_ci file_info->LastAccessTime = cpu_to_le64(time); 457562306a36Sopenharmony_ci time = ksmbd_UnixTimeToNT(stat.mtime); 457662306a36Sopenharmony_ci file_info->LastWriteTime = cpu_to_le64(time); 457762306a36Sopenharmony_ci time = ksmbd_UnixTimeToNT(stat.ctime); 457862306a36Sopenharmony_ci file_info->ChangeTime = cpu_to_le64(time); 457962306a36Sopenharmony_ci file_info->Attributes = fp->f_ci->m_fattr; 458062306a36Sopenharmony_ci file_info->Pad1 = 0; 458162306a36Sopenharmony_ci file_info->AllocationSize = 458262306a36Sopenharmony_ci cpu_to_le64(inode->i_blocks << 9); 458362306a36Sopenharmony_ci file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); 458462306a36Sopenharmony_ci file_info->NumberOfLinks = 458562306a36Sopenharmony_ci cpu_to_le32(get_nlink(&stat) - delete_pending); 458662306a36Sopenharmony_ci file_info->DeletePending = delete_pending; 458762306a36Sopenharmony_ci file_info->Directory = S_ISDIR(stat.mode) ? 1 : 0; 458862306a36Sopenharmony_ci file_info->Pad2 = 0; 458962306a36Sopenharmony_ci file_info->IndexNumber = cpu_to_le64(stat.ino); 459062306a36Sopenharmony_ci file_info->EASize = 0; 459162306a36Sopenharmony_ci file_info->AccessFlags = fp->daccess; 459262306a36Sopenharmony_ci file_info->CurrentByteOffset = cpu_to_le64(fp->filp->f_pos); 459362306a36Sopenharmony_ci file_info->Mode = fp->coption; 459462306a36Sopenharmony_ci file_info->AlignmentRequirement = 0; 459562306a36Sopenharmony_ci conv_len = smbConvertToUTF16((__le16 *)file_info->FileName, filename, 459662306a36Sopenharmony_ci PATH_MAX, conn->local_nls, 0); 459762306a36Sopenharmony_ci conv_len *= 2; 459862306a36Sopenharmony_ci file_info->FileNameLength = cpu_to_le32(conv_len); 459962306a36Sopenharmony_ci rsp->OutputBufferLength = 460062306a36Sopenharmony_ci cpu_to_le32(sizeof(struct smb2_file_all_info) + conv_len - 1); 460162306a36Sopenharmony_ci kfree(filename); 460262306a36Sopenharmony_ci return 0; 460362306a36Sopenharmony_ci} 460462306a36Sopenharmony_ci 460562306a36Sopenharmony_cistatic void get_file_alternate_info(struct ksmbd_work *work, 460662306a36Sopenharmony_ci struct smb2_query_info_rsp *rsp, 460762306a36Sopenharmony_ci struct ksmbd_file *fp, 460862306a36Sopenharmony_ci void *rsp_org) 460962306a36Sopenharmony_ci{ 461062306a36Sopenharmony_ci struct ksmbd_conn *conn = work->conn; 461162306a36Sopenharmony_ci struct smb2_file_alt_name_info *file_info; 461262306a36Sopenharmony_ci struct dentry *dentry = fp->filp->f_path.dentry; 461362306a36Sopenharmony_ci int conv_len; 461462306a36Sopenharmony_ci 461562306a36Sopenharmony_ci spin_lock(&dentry->d_lock); 461662306a36Sopenharmony_ci file_info = (struct smb2_file_alt_name_info *)rsp->Buffer; 461762306a36Sopenharmony_ci conv_len = ksmbd_extract_shortname(conn, 461862306a36Sopenharmony_ci dentry->d_name.name, 461962306a36Sopenharmony_ci file_info->FileName); 462062306a36Sopenharmony_ci spin_unlock(&dentry->d_lock); 462162306a36Sopenharmony_ci file_info->FileNameLength = cpu_to_le32(conv_len); 462262306a36Sopenharmony_ci rsp->OutputBufferLength = 462362306a36Sopenharmony_ci cpu_to_le32(sizeof(struct smb2_file_alt_name_info) + conv_len); 462462306a36Sopenharmony_ci} 462562306a36Sopenharmony_ci 462662306a36Sopenharmony_cistatic void get_file_stream_info(struct ksmbd_work *work, 462762306a36Sopenharmony_ci struct smb2_query_info_rsp *rsp, 462862306a36Sopenharmony_ci struct ksmbd_file *fp, 462962306a36Sopenharmony_ci void *rsp_org) 463062306a36Sopenharmony_ci{ 463162306a36Sopenharmony_ci struct ksmbd_conn *conn = work->conn; 463262306a36Sopenharmony_ci struct smb2_file_stream_info *file_info; 463362306a36Sopenharmony_ci char *stream_name, *xattr_list = NULL, *stream_buf; 463462306a36Sopenharmony_ci struct kstat stat; 463562306a36Sopenharmony_ci const struct path *path = &fp->filp->f_path; 463662306a36Sopenharmony_ci ssize_t xattr_list_len; 463762306a36Sopenharmony_ci int nbytes = 0, streamlen, stream_name_len, next, idx = 0; 463862306a36Sopenharmony_ci int buf_free_len; 463962306a36Sopenharmony_ci struct smb2_query_info_req *req = ksmbd_req_buf_next(work); 464062306a36Sopenharmony_ci 464162306a36Sopenharmony_ci generic_fillattr(file_mnt_idmap(fp->filp), STATX_BASIC_STATS, 464262306a36Sopenharmony_ci file_inode(fp->filp), &stat); 464362306a36Sopenharmony_ci file_info = (struct smb2_file_stream_info *)rsp->Buffer; 464462306a36Sopenharmony_ci 464562306a36Sopenharmony_ci buf_free_len = 464662306a36Sopenharmony_ci smb2_calc_max_out_buf_len(work, 8, 464762306a36Sopenharmony_ci le32_to_cpu(req->OutputBufferLength)); 464862306a36Sopenharmony_ci if (buf_free_len < 0) 464962306a36Sopenharmony_ci goto out; 465062306a36Sopenharmony_ci 465162306a36Sopenharmony_ci xattr_list_len = ksmbd_vfs_listxattr(path->dentry, &xattr_list); 465262306a36Sopenharmony_ci if (xattr_list_len < 0) { 465362306a36Sopenharmony_ci goto out; 465462306a36Sopenharmony_ci } else if (!xattr_list_len) { 465562306a36Sopenharmony_ci ksmbd_debug(SMB, "empty xattr in the file\n"); 465662306a36Sopenharmony_ci goto out; 465762306a36Sopenharmony_ci } 465862306a36Sopenharmony_ci 465962306a36Sopenharmony_ci while (idx < xattr_list_len) { 466062306a36Sopenharmony_ci stream_name = xattr_list + idx; 466162306a36Sopenharmony_ci streamlen = strlen(stream_name); 466262306a36Sopenharmony_ci idx += streamlen + 1; 466362306a36Sopenharmony_ci 466462306a36Sopenharmony_ci ksmbd_debug(SMB, "%s, len %d\n", stream_name, streamlen); 466562306a36Sopenharmony_ci 466662306a36Sopenharmony_ci if (strncmp(&stream_name[XATTR_USER_PREFIX_LEN], 466762306a36Sopenharmony_ci STREAM_PREFIX, STREAM_PREFIX_LEN)) 466862306a36Sopenharmony_ci continue; 466962306a36Sopenharmony_ci 467062306a36Sopenharmony_ci stream_name_len = streamlen - (XATTR_USER_PREFIX_LEN + 467162306a36Sopenharmony_ci STREAM_PREFIX_LEN); 467262306a36Sopenharmony_ci streamlen = stream_name_len; 467362306a36Sopenharmony_ci 467462306a36Sopenharmony_ci /* plus : size */ 467562306a36Sopenharmony_ci streamlen += 1; 467662306a36Sopenharmony_ci stream_buf = kmalloc(streamlen + 1, GFP_KERNEL); 467762306a36Sopenharmony_ci if (!stream_buf) 467862306a36Sopenharmony_ci break; 467962306a36Sopenharmony_ci 468062306a36Sopenharmony_ci streamlen = snprintf(stream_buf, streamlen + 1, 468162306a36Sopenharmony_ci ":%s", &stream_name[XATTR_NAME_STREAM_LEN]); 468262306a36Sopenharmony_ci 468362306a36Sopenharmony_ci next = sizeof(struct smb2_file_stream_info) + streamlen * 2; 468462306a36Sopenharmony_ci if (next > buf_free_len) { 468562306a36Sopenharmony_ci kfree(stream_buf); 468662306a36Sopenharmony_ci break; 468762306a36Sopenharmony_ci } 468862306a36Sopenharmony_ci 468962306a36Sopenharmony_ci file_info = (struct smb2_file_stream_info *)&rsp->Buffer[nbytes]; 469062306a36Sopenharmony_ci streamlen = smbConvertToUTF16((__le16 *)file_info->StreamName, 469162306a36Sopenharmony_ci stream_buf, streamlen, 469262306a36Sopenharmony_ci conn->local_nls, 0); 469362306a36Sopenharmony_ci streamlen *= 2; 469462306a36Sopenharmony_ci kfree(stream_buf); 469562306a36Sopenharmony_ci file_info->StreamNameLength = cpu_to_le32(streamlen); 469662306a36Sopenharmony_ci file_info->StreamSize = cpu_to_le64(stream_name_len); 469762306a36Sopenharmony_ci file_info->StreamAllocationSize = cpu_to_le64(stream_name_len); 469862306a36Sopenharmony_ci 469962306a36Sopenharmony_ci nbytes += next; 470062306a36Sopenharmony_ci buf_free_len -= next; 470162306a36Sopenharmony_ci file_info->NextEntryOffset = cpu_to_le32(next); 470262306a36Sopenharmony_ci } 470362306a36Sopenharmony_ci 470462306a36Sopenharmony_ciout: 470562306a36Sopenharmony_ci if (!S_ISDIR(stat.mode) && 470662306a36Sopenharmony_ci buf_free_len >= sizeof(struct smb2_file_stream_info) + 7 * 2) { 470762306a36Sopenharmony_ci file_info = (struct smb2_file_stream_info *) 470862306a36Sopenharmony_ci &rsp->Buffer[nbytes]; 470962306a36Sopenharmony_ci streamlen = smbConvertToUTF16((__le16 *)file_info->StreamName, 471062306a36Sopenharmony_ci "::$DATA", 7, conn->local_nls, 0); 471162306a36Sopenharmony_ci streamlen *= 2; 471262306a36Sopenharmony_ci file_info->StreamNameLength = cpu_to_le32(streamlen); 471362306a36Sopenharmony_ci file_info->StreamSize = cpu_to_le64(stat.size); 471462306a36Sopenharmony_ci file_info->StreamAllocationSize = cpu_to_le64(stat.blocks << 9); 471562306a36Sopenharmony_ci nbytes += sizeof(struct smb2_file_stream_info) + streamlen; 471662306a36Sopenharmony_ci } 471762306a36Sopenharmony_ci 471862306a36Sopenharmony_ci /* last entry offset should be 0 */ 471962306a36Sopenharmony_ci file_info->NextEntryOffset = 0; 472062306a36Sopenharmony_ci kvfree(xattr_list); 472162306a36Sopenharmony_ci 472262306a36Sopenharmony_ci rsp->OutputBufferLength = cpu_to_le32(nbytes); 472362306a36Sopenharmony_ci} 472462306a36Sopenharmony_ci 472562306a36Sopenharmony_cistatic void get_file_internal_info(struct smb2_query_info_rsp *rsp, 472662306a36Sopenharmony_ci struct ksmbd_file *fp, void *rsp_org) 472762306a36Sopenharmony_ci{ 472862306a36Sopenharmony_ci struct smb2_file_internal_info *file_info; 472962306a36Sopenharmony_ci struct kstat stat; 473062306a36Sopenharmony_ci 473162306a36Sopenharmony_ci generic_fillattr(file_mnt_idmap(fp->filp), STATX_BASIC_STATS, 473262306a36Sopenharmony_ci file_inode(fp->filp), &stat); 473362306a36Sopenharmony_ci file_info = (struct smb2_file_internal_info *)rsp->Buffer; 473462306a36Sopenharmony_ci file_info->IndexNumber = cpu_to_le64(stat.ino); 473562306a36Sopenharmony_ci rsp->OutputBufferLength = 473662306a36Sopenharmony_ci cpu_to_le32(sizeof(struct smb2_file_internal_info)); 473762306a36Sopenharmony_ci} 473862306a36Sopenharmony_ci 473962306a36Sopenharmony_cistatic int get_file_network_open_info(struct smb2_query_info_rsp *rsp, 474062306a36Sopenharmony_ci struct ksmbd_file *fp, void *rsp_org) 474162306a36Sopenharmony_ci{ 474262306a36Sopenharmony_ci struct smb2_file_ntwrk_info *file_info; 474362306a36Sopenharmony_ci struct inode *inode; 474462306a36Sopenharmony_ci struct kstat stat; 474562306a36Sopenharmony_ci u64 time; 474662306a36Sopenharmony_ci 474762306a36Sopenharmony_ci if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { 474862306a36Sopenharmony_ci pr_err("no right to read the attributes : 0x%x\n", 474962306a36Sopenharmony_ci fp->daccess); 475062306a36Sopenharmony_ci return -EACCES; 475162306a36Sopenharmony_ci } 475262306a36Sopenharmony_ci 475362306a36Sopenharmony_ci file_info = (struct smb2_file_ntwrk_info *)rsp->Buffer; 475462306a36Sopenharmony_ci 475562306a36Sopenharmony_ci inode = file_inode(fp->filp); 475662306a36Sopenharmony_ci generic_fillattr(file_mnt_idmap(fp->filp), STATX_BASIC_STATS, inode, &stat); 475762306a36Sopenharmony_ci 475862306a36Sopenharmony_ci file_info->CreationTime = cpu_to_le64(fp->create_time); 475962306a36Sopenharmony_ci time = ksmbd_UnixTimeToNT(stat.atime); 476062306a36Sopenharmony_ci file_info->LastAccessTime = cpu_to_le64(time); 476162306a36Sopenharmony_ci time = ksmbd_UnixTimeToNT(stat.mtime); 476262306a36Sopenharmony_ci file_info->LastWriteTime = cpu_to_le64(time); 476362306a36Sopenharmony_ci time = ksmbd_UnixTimeToNT(stat.ctime); 476462306a36Sopenharmony_ci file_info->ChangeTime = cpu_to_le64(time); 476562306a36Sopenharmony_ci file_info->Attributes = fp->f_ci->m_fattr; 476662306a36Sopenharmony_ci file_info->AllocationSize = 476762306a36Sopenharmony_ci cpu_to_le64(inode->i_blocks << 9); 476862306a36Sopenharmony_ci file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); 476962306a36Sopenharmony_ci file_info->Reserved = cpu_to_le32(0); 477062306a36Sopenharmony_ci rsp->OutputBufferLength = 477162306a36Sopenharmony_ci cpu_to_le32(sizeof(struct smb2_file_ntwrk_info)); 477262306a36Sopenharmony_ci return 0; 477362306a36Sopenharmony_ci} 477462306a36Sopenharmony_ci 477562306a36Sopenharmony_cistatic void get_file_ea_info(struct smb2_query_info_rsp *rsp, void *rsp_org) 477662306a36Sopenharmony_ci{ 477762306a36Sopenharmony_ci struct smb2_file_ea_info *file_info; 477862306a36Sopenharmony_ci 477962306a36Sopenharmony_ci file_info = (struct smb2_file_ea_info *)rsp->Buffer; 478062306a36Sopenharmony_ci file_info->EASize = 0; 478162306a36Sopenharmony_ci rsp->OutputBufferLength = 478262306a36Sopenharmony_ci cpu_to_le32(sizeof(struct smb2_file_ea_info)); 478362306a36Sopenharmony_ci} 478462306a36Sopenharmony_ci 478562306a36Sopenharmony_cistatic void get_file_position_info(struct smb2_query_info_rsp *rsp, 478662306a36Sopenharmony_ci struct ksmbd_file *fp, void *rsp_org) 478762306a36Sopenharmony_ci{ 478862306a36Sopenharmony_ci struct smb2_file_pos_info *file_info; 478962306a36Sopenharmony_ci 479062306a36Sopenharmony_ci file_info = (struct smb2_file_pos_info *)rsp->Buffer; 479162306a36Sopenharmony_ci file_info->CurrentByteOffset = cpu_to_le64(fp->filp->f_pos); 479262306a36Sopenharmony_ci rsp->OutputBufferLength = 479362306a36Sopenharmony_ci cpu_to_le32(sizeof(struct smb2_file_pos_info)); 479462306a36Sopenharmony_ci} 479562306a36Sopenharmony_ci 479662306a36Sopenharmony_cistatic void get_file_mode_info(struct smb2_query_info_rsp *rsp, 479762306a36Sopenharmony_ci struct ksmbd_file *fp, void *rsp_org) 479862306a36Sopenharmony_ci{ 479962306a36Sopenharmony_ci struct smb2_file_mode_info *file_info; 480062306a36Sopenharmony_ci 480162306a36Sopenharmony_ci file_info = (struct smb2_file_mode_info *)rsp->Buffer; 480262306a36Sopenharmony_ci file_info->Mode = fp->coption & FILE_MODE_INFO_MASK; 480362306a36Sopenharmony_ci rsp->OutputBufferLength = 480462306a36Sopenharmony_ci cpu_to_le32(sizeof(struct smb2_file_mode_info)); 480562306a36Sopenharmony_ci} 480662306a36Sopenharmony_ci 480762306a36Sopenharmony_cistatic void get_file_compression_info(struct smb2_query_info_rsp *rsp, 480862306a36Sopenharmony_ci struct ksmbd_file *fp, void *rsp_org) 480962306a36Sopenharmony_ci{ 481062306a36Sopenharmony_ci struct smb2_file_comp_info *file_info; 481162306a36Sopenharmony_ci struct kstat stat; 481262306a36Sopenharmony_ci 481362306a36Sopenharmony_ci generic_fillattr(file_mnt_idmap(fp->filp), STATX_BASIC_STATS, 481462306a36Sopenharmony_ci file_inode(fp->filp), &stat); 481562306a36Sopenharmony_ci 481662306a36Sopenharmony_ci file_info = (struct smb2_file_comp_info *)rsp->Buffer; 481762306a36Sopenharmony_ci file_info->CompressedFileSize = cpu_to_le64(stat.blocks << 9); 481862306a36Sopenharmony_ci file_info->CompressionFormat = COMPRESSION_FORMAT_NONE; 481962306a36Sopenharmony_ci file_info->CompressionUnitShift = 0; 482062306a36Sopenharmony_ci file_info->ChunkShift = 0; 482162306a36Sopenharmony_ci file_info->ClusterShift = 0; 482262306a36Sopenharmony_ci memset(&file_info->Reserved[0], 0, 3); 482362306a36Sopenharmony_ci 482462306a36Sopenharmony_ci rsp->OutputBufferLength = 482562306a36Sopenharmony_ci cpu_to_le32(sizeof(struct smb2_file_comp_info)); 482662306a36Sopenharmony_ci} 482762306a36Sopenharmony_ci 482862306a36Sopenharmony_cistatic int get_file_attribute_tag_info(struct smb2_query_info_rsp *rsp, 482962306a36Sopenharmony_ci struct ksmbd_file *fp, void *rsp_org) 483062306a36Sopenharmony_ci{ 483162306a36Sopenharmony_ci struct smb2_file_attr_tag_info *file_info; 483262306a36Sopenharmony_ci 483362306a36Sopenharmony_ci if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { 483462306a36Sopenharmony_ci pr_err("no right to read the attributes : 0x%x\n", 483562306a36Sopenharmony_ci fp->daccess); 483662306a36Sopenharmony_ci return -EACCES; 483762306a36Sopenharmony_ci } 483862306a36Sopenharmony_ci 483962306a36Sopenharmony_ci file_info = (struct smb2_file_attr_tag_info *)rsp->Buffer; 484062306a36Sopenharmony_ci file_info->FileAttributes = fp->f_ci->m_fattr; 484162306a36Sopenharmony_ci file_info->ReparseTag = 0; 484262306a36Sopenharmony_ci rsp->OutputBufferLength = 484362306a36Sopenharmony_ci cpu_to_le32(sizeof(struct smb2_file_attr_tag_info)); 484462306a36Sopenharmony_ci return 0; 484562306a36Sopenharmony_ci} 484662306a36Sopenharmony_ci 484762306a36Sopenharmony_cistatic void find_file_posix_info(struct smb2_query_info_rsp *rsp, 484862306a36Sopenharmony_ci struct ksmbd_file *fp, void *rsp_org) 484962306a36Sopenharmony_ci{ 485062306a36Sopenharmony_ci struct smb311_posix_qinfo *file_info; 485162306a36Sopenharmony_ci struct inode *inode = file_inode(fp->filp); 485262306a36Sopenharmony_ci struct mnt_idmap *idmap = file_mnt_idmap(fp->filp); 485362306a36Sopenharmony_ci vfsuid_t vfsuid = i_uid_into_vfsuid(idmap, inode); 485462306a36Sopenharmony_ci vfsgid_t vfsgid = i_gid_into_vfsgid(idmap, inode); 485562306a36Sopenharmony_ci u64 time; 485662306a36Sopenharmony_ci int out_buf_len = sizeof(struct smb311_posix_qinfo) + 32; 485762306a36Sopenharmony_ci 485862306a36Sopenharmony_ci file_info = (struct smb311_posix_qinfo *)rsp->Buffer; 485962306a36Sopenharmony_ci file_info->CreationTime = cpu_to_le64(fp->create_time); 486062306a36Sopenharmony_ci time = ksmbd_UnixTimeToNT(inode_get_atime(inode)); 486162306a36Sopenharmony_ci file_info->LastAccessTime = cpu_to_le64(time); 486262306a36Sopenharmony_ci time = ksmbd_UnixTimeToNT(inode_get_mtime(inode)); 486362306a36Sopenharmony_ci file_info->LastWriteTime = cpu_to_le64(time); 486462306a36Sopenharmony_ci time = ksmbd_UnixTimeToNT(inode_get_ctime(inode)); 486562306a36Sopenharmony_ci file_info->ChangeTime = cpu_to_le64(time); 486662306a36Sopenharmony_ci file_info->DosAttributes = fp->f_ci->m_fattr; 486762306a36Sopenharmony_ci file_info->Inode = cpu_to_le64(inode->i_ino); 486862306a36Sopenharmony_ci file_info->EndOfFile = cpu_to_le64(inode->i_size); 486962306a36Sopenharmony_ci file_info->AllocationSize = cpu_to_le64(inode->i_blocks << 9); 487062306a36Sopenharmony_ci file_info->HardLinks = cpu_to_le32(inode->i_nlink); 487162306a36Sopenharmony_ci file_info->Mode = cpu_to_le32(inode->i_mode & 0777); 487262306a36Sopenharmony_ci file_info->DeviceId = cpu_to_le32(inode->i_rdev); 487362306a36Sopenharmony_ci 487462306a36Sopenharmony_ci /* 487562306a36Sopenharmony_ci * Sids(32) contain two sids(Domain sid(16), UNIX group sid(16)). 487662306a36Sopenharmony_ci * UNIX sid(16) = revision(1) + num_subauth(1) + authority(6) + 487762306a36Sopenharmony_ci * sub_auth(4 * 1(num_subauth)) + RID(4). 487862306a36Sopenharmony_ci */ 487962306a36Sopenharmony_ci id_to_sid(from_kuid_munged(&init_user_ns, vfsuid_into_kuid(vfsuid)), 488062306a36Sopenharmony_ci SIDUNIX_USER, (struct smb_sid *)&file_info->Sids[0]); 488162306a36Sopenharmony_ci id_to_sid(from_kgid_munged(&init_user_ns, vfsgid_into_kgid(vfsgid)), 488262306a36Sopenharmony_ci SIDUNIX_GROUP, (struct smb_sid *)&file_info->Sids[16]); 488362306a36Sopenharmony_ci 488462306a36Sopenharmony_ci rsp->OutputBufferLength = cpu_to_le32(out_buf_len); 488562306a36Sopenharmony_ci} 488662306a36Sopenharmony_ci 488762306a36Sopenharmony_cistatic int smb2_get_info_file(struct ksmbd_work *work, 488862306a36Sopenharmony_ci struct smb2_query_info_req *req, 488962306a36Sopenharmony_ci struct smb2_query_info_rsp *rsp) 489062306a36Sopenharmony_ci{ 489162306a36Sopenharmony_ci struct ksmbd_file *fp; 489262306a36Sopenharmony_ci int fileinfoclass = 0; 489362306a36Sopenharmony_ci int rc = 0; 489462306a36Sopenharmony_ci unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; 489562306a36Sopenharmony_ci 489662306a36Sopenharmony_ci if (test_share_config_flag(work->tcon->share_conf, 489762306a36Sopenharmony_ci KSMBD_SHARE_FLAG_PIPE)) { 489862306a36Sopenharmony_ci /* smb2 info file called for pipe */ 489962306a36Sopenharmony_ci return smb2_get_info_file_pipe(work->sess, req, rsp, 490062306a36Sopenharmony_ci work->response_buf); 490162306a36Sopenharmony_ci } 490262306a36Sopenharmony_ci 490362306a36Sopenharmony_ci if (work->next_smb2_rcv_hdr_off) { 490462306a36Sopenharmony_ci if (!has_file_id(req->VolatileFileId)) { 490562306a36Sopenharmony_ci ksmbd_debug(SMB, "Compound request set FID = %llu\n", 490662306a36Sopenharmony_ci work->compound_fid); 490762306a36Sopenharmony_ci id = work->compound_fid; 490862306a36Sopenharmony_ci pid = work->compound_pfid; 490962306a36Sopenharmony_ci } 491062306a36Sopenharmony_ci } 491162306a36Sopenharmony_ci 491262306a36Sopenharmony_ci if (!has_file_id(id)) { 491362306a36Sopenharmony_ci id = req->VolatileFileId; 491462306a36Sopenharmony_ci pid = req->PersistentFileId; 491562306a36Sopenharmony_ci } 491662306a36Sopenharmony_ci 491762306a36Sopenharmony_ci fp = ksmbd_lookup_fd_slow(work, id, pid); 491862306a36Sopenharmony_ci if (!fp) 491962306a36Sopenharmony_ci return -ENOENT; 492062306a36Sopenharmony_ci 492162306a36Sopenharmony_ci fileinfoclass = req->FileInfoClass; 492262306a36Sopenharmony_ci 492362306a36Sopenharmony_ci switch (fileinfoclass) { 492462306a36Sopenharmony_ci case FILE_ACCESS_INFORMATION: 492562306a36Sopenharmony_ci get_file_access_info(rsp, fp, work->response_buf); 492662306a36Sopenharmony_ci break; 492762306a36Sopenharmony_ci 492862306a36Sopenharmony_ci case FILE_BASIC_INFORMATION: 492962306a36Sopenharmony_ci rc = get_file_basic_info(rsp, fp, work->response_buf); 493062306a36Sopenharmony_ci break; 493162306a36Sopenharmony_ci 493262306a36Sopenharmony_ci case FILE_STANDARD_INFORMATION: 493362306a36Sopenharmony_ci get_file_standard_info(rsp, fp, work->response_buf); 493462306a36Sopenharmony_ci break; 493562306a36Sopenharmony_ci 493662306a36Sopenharmony_ci case FILE_ALIGNMENT_INFORMATION: 493762306a36Sopenharmony_ci get_file_alignment_info(rsp, work->response_buf); 493862306a36Sopenharmony_ci break; 493962306a36Sopenharmony_ci 494062306a36Sopenharmony_ci case FILE_ALL_INFORMATION: 494162306a36Sopenharmony_ci rc = get_file_all_info(work, rsp, fp, work->response_buf); 494262306a36Sopenharmony_ci break; 494362306a36Sopenharmony_ci 494462306a36Sopenharmony_ci case FILE_ALTERNATE_NAME_INFORMATION: 494562306a36Sopenharmony_ci get_file_alternate_info(work, rsp, fp, work->response_buf); 494662306a36Sopenharmony_ci break; 494762306a36Sopenharmony_ci 494862306a36Sopenharmony_ci case FILE_STREAM_INFORMATION: 494962306a36Sopenharmony_ci get_file_stream_info(work, rsp, fp, work->response_buf); 495062306a36Sopenharmony_ci break; 495162306a36Sopenharmony_ci 495262306a36Sopenharmony_ci case FILE_INTERNAL_INFORMATION: 495362306a36Sopenharmony_ci get_file_internal_info(rsp, fp, work->response_buf); 495462306a36Sopenharmony_ci break; 495562306a36Sopenharmony_ci 495662306a36Sopenharmony_ci case FILE_NETWORK_OPEN_INFORMATION: 495762306a36Sopenharmony_ci rc = get_file_network_open_info(rsp, fp, work->response_buf); 495862306a36Sopenharmony_ci break; 495962306a36Sopenharmony_ci 496062306a36Sopenharmony_ci case FILE_EA_INFORMATION: 496162306a36Sopenharmony_ci get_file_ea_info(rsp, work->response_buf); 496262306a36Sopenharmony_ci break; 496362306a36Sopenharmony_ci 496462306a36Sopenharmony_ci case FILE_FULL_EA_INFORMATION: 496562306a36Sopenharmony_ci rc = smb2_get_ea(work, fp, req, rsp, work->response_buf); 496662306a36Sopenharmony_ci break; 496762306a36Sopenharmony_ci 496862306a36Sopenharmony_ci case FILE_POSITION_INFORMATION: 496962306a36Sopenharmony_ci get_file_position_info(rsp, fp, work->response_buf); 497062306a36Sopenharmony_ci break; 497162306a36Sopenharmony_ci 497262306a36Sopenharmony_ci case FILE_MODE_INFORMATION: 497362306a36Sopenharmony_ci get_file_mode_info(rsp, fp, work->response_buf); 497462306a36Sopenharmony_ci break; 497562306a36Sopenharmony_ci 497662306a36Sopenharmony_ci case FILE_COMPRESSION_INFORMATION: 497762306a36Sopenharmony_ci get_file_compression_info(rsp, fp, work->response_buf); 497862306a36Sopenharmony_ci break; 497962306a36Sopenharmony_ci 498062306a36Sopenharmony_ci case FILE_ATTRIBUTE_TAG_INFORMATION: 498162306a36Sopenharmony_ci rc = get_file_attribute_tag_info(rsp, fp, work->response_buf); 498262306a36Sopenharmony_ci break; 498362306a36Sopenharmony_ci case SMB_FIND_FILE_POSIX_INFO: 498462306a36Sopenharmony_ci if (!work->tcon->posix_extensions) { 498562306a36Sopenharmony_ci pr_err("client doesn't negotiate with SMB3.1.1 POSIX Extensions\n"); 498662306a36Sopenharmony_ci rc = -EOPNOTSUPP; 498762306a36Sopenharmony_ci } else { 498862306a36Sopenharmony_ci find_file_posix_info(rsp, fp, work->response_buf); 498962306a36Sopenharmony_ci } 499062306a36Sopenharmony_ci break; 499162306a36Sopenharmony_ci default: 499262306a36Sopenharmony_ci ksmbd_debug(SMB, "fileinfoclass %d not supported yet\n", 499362306a36Sopenharmony_ci fileinfoclass); 499462306a36Sopenharmony_ci rc = -EOPNOTSUPP; 499562306a36Sopenharmony_ci } 499662306a36Sopenharmony_ci if (!rc) 499762306a36Sopenharmony_ci rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), 499862306a36Sopenharmony_ci rsp, work->response_buf); 499962306a36Sopenharmony_ci ksmbd_fd_put(work, fp); 500062306a36Sopenharmony_ci return rc; 500162306a36Sopenharmony_ci} 500262306a36Sopenharmony_ci 500362306a36Sopenharmony_cistatic int smb2_get_info_filesystem(struct ksmbd_work *work, 500462306a36Sopenharmony_ci struct smb2_query_info_req *req, 500562306a36Sopenharmony_ci struct smb2_query_info_rsp *rsp) 500662306a36Sopenharmony_ci{ 500762306a36Sopenharmony_ci struct ksmbd_session *sess = work->sess; 500862306a36Sopenharmony_ci struct ksmbd_conn *conn = work->conn; 500962306a36Sopenharmony_ci struct ksmbd_share_config *share = work->tcon->share_conf; 501062306a36Sopenharmony_ci int fsinfoclass = 0; 501162306a36Sopenharmony_ci struct kstatfs stfs; 501262306a36Sopenharmony_ci struct path path; 501362306a36Sopenharmony_ci int rc = 0, len; 501462306a36Sopenharmony_ci 501562306a36Sopenharmony_ci if (!share->path) 501662306a36Sopenharmony_ci return -EIO; 501762306a36Sopenharmony_ci 501862306a36Sopenharmony_ci rc = kern_path(share->path, LOOKUP_NO_SYMLINKS, &path); 501962306a36Sopenharmony_ci if (rc) { 502062306a36Sopenharmony_ci pr_err("cannot create vfs path\n"); 502162306a36Sopenharmony_ci return -EIO; 502262306a36Sopenharmony_ci } 502362306a36Sopenharmony_ci 502462306a36Sopenharmony_ci rc = vfs_statfs(&path, &stfs); 502562306a36Sopenharmony_ci if (rc) { 502662306a36Sopenharmony_ci pr_err("cannot do stat of path %s\n", share->path); 502762306a36Sopenharmony_ci path_put(&path); 502862306a36Sopenharmony_ci return -EIO; 502962306a36Sopenharmony_ci } 503062306a36Sopenharmony_ci 503162306a36Sopenharmony_ci fsinfoclass = req->FileInfoClass; 503262306a36Sopenharmony_ci 503362306a36Sopenharmony_ci switch (fsinfoclass) { 503462306a36Sopenharmony_ci case FS_DEVICE_INFORMATION: 503562306a36Sopenharmony_ci { 503662306a36Sopenharmony_ci struct filesystem_device_info *info; 503762306a36Sopenharmony_ci 503862306a36Sopenharmony_ci info = (struct filesystem_device_info *)rsp->Buffer; 503962306a36Sopenharmony_ci 504062306a36Sopenharmony_ci info->DeviceType = cpu_to_le32(stfs.f_type); 504162306a36Sopenharmony_ci info->DeviceCharacteristics = cpu_to_le32(0x00000020); 504262306a36Sopenharmony_ci rsp->OutputBufferLength = cpu_to_le32(8); 504362306a36Sopenharmony_ci break; 504462306a36Sopenharmony_ci } 504562306a36Sopenharmony_ci case FS_ATTRIBUTE_INFORMATION: 504662306a36Sopenharmony_ci { 504762306a36Sopenharmony_ci struct filesystem_attribute_info *info; 504862306a36Sopenharmony_ci size_t sz; 504962306a36Sopenharmony_ci 505062306a36Sopenharmony_ci info = (struct filesystem_attribute_info *)rsp->Buffer; 505162306a36Sopenharmony_ci info->Attributes = cpu_to_le32(FILE_SUPPORTS_OBJECT_IDS | 505262306a36Sopenharmony_ci FILE_PERSISTENT_ACLS | 505362306a36Sopenharmony_ci FILE_UNICODE_ON_DISK | 505462306a36Sopenharmony_ci FILE_CASE_PRESERVED_NAMES | 505562306a36Sopenharmony_ci FILE_CASE_SENSITIVE_SEARCH | 505662306a36Sopenharmony_ci FILE_SUPPORTS_BLOCK_REFCOUNTING); 505762306a36Sopenharmony_ci 505862306a36Sopenharmony_ci info->Attributes |= cpu_to_le32(server_conf.share_fake_fscaps); 505962306a36Sopenharmony_ci 506062306a36Sopenharmony_ci if (test_share_config_flag(work->tcon->share_conf, 506162306a36Sopenharmony_ci KSMBD_SHARE_FLAG_STREAMS)) 506262306a36Sopenharmony_ci info->Attributes |= cpu_to_le32(FILE_NAMED_STREAMS); 506362306a36Sopenharmony_ci 506462306a36Sopenharmony_ci info->MaxPathNameComponentLength = cpu_to_le32(stfs.f_namelen); 506562306a36Sopenharmony_ci len = smbConvertToUTF16((__le16 *)info->FileSystemName, 506662306a36Sopenharmony_ci "NTFS", PATH_MAX, conn->local_nls, 0); 506762306a36Sopenharmony_ci len = len * 2; 506862306a36Sopenharmony_ci info->FileSystemNameLen = cpu_to_le32(len); 506962306a36Sopenharmony_ci sz = sizeof(struct filesystem_attribute_info) - 2 + len; 507062306a36Sopenharmony_ci rsp->OutputBufferLength = cpu_to_le32(sz); 507162306a36Sopenharmony_ci break; 507262306a36Sopenharmony_ci } 507362306a36Sopenharmony_ci case FS_VOLUME_INFORMATION: 507462306a36Sopenharmony_ci { 507562306a36Sopenharmony_ci struct filesystem_vol_info *info; 507662306a36Sopenharmony_ci size_t sz; 507762306a36Sopenharmony_ci unsigned int serial_crc = 0; 507862306a36Sopenharmony_ci 507962306a36Sopenharmony_ci info = (struct filesystem_vol_info *)(rsp->Buffer); 508062306a36Sopenharmony_ci info->VolumeCreationTime = 0; 508162306a36Sopenharmony_ci serial_crc = crc32_le(serial_crc, share->name, 508262306a36Sopenharmony_ci strlen(share->name)); 508362306a36Sopenharmony_ci serial_crc = crc32_le(serial_crc, share->path, 508462306a36Sopenharmony_ci strlen(share->path)); 508562306a36Sopenharmony_ci serial_crc = crc32_le(serial_crc, ksmbd_netbios_name(), 508662306a36Sopenharmony_ci strlen(ksmbd_netbios_name())); 508762306a36Sopenharmony_ci /* Taking dummy value of serial number*/ 508862306a36Sopenharmony_ci info->SerialNumber = cpu_to_le32(serial_crc); 508962306a36Sopenharmony_ci len = smbConvertToUTF16((__le16 *)info->VolumeLabel, 509062306a36Sopenharmony_ci share->name, PATH_MAX, 509162306a36Sopenharmony_ci conn->local_nls, 0); 509262306a36Sopenharmony_ci len = len * 2; 509362306a36Sopenharmony_ci info->VolumeLabelSize = cpu_to_le32(len); 509462306a36Sopenharmony_ci info->Reserved = 0; 509562306a36Sopenharmony_ci sz = sizeof(struct filesystem_vol_info) - 2 + len; 509662306a36Sopenharmony_ci rsp->OutputBufferLength = cpu_to_le32(sz); 509762306a36Sopenharmony_ci break; 509862306a36Sopenharmony_ci } 509962306a36Sopenharmony_ci case FS_SIZE_INFORMATION: 510062306a36Sopenharmony_ci { 510162306a36Sopenharmony_ci struct filesystem_info *info; 510262306a36Sopenharmony_ci 510362306a36Sopenharmony_ci info = (struct filesystem_info *)(rsp->Buffer); 510462306a36Sopenharmony_ci info->TotalAllocationUnits = cpu_to_le64(stfs.f_blocks); 510562306a36Sopenharmony_ci info->FreeAllocationUnits = cpu_to_le64(stfs.f_bfree); 510662306a36Sopenharmony_ci info->SectorsPerAllocationUnit = cpu_to_le32(1); 510762306a36Sopenharmony_ci info->BytesPerSector = cpu_to_le32(stfs.f_bsize); 510862306a36Sopenharmony_ci rsp->OutputBufferLength = cpu_to_le32(24); 510962306a36Sopenharmony_ci break; 511062306a36Sopenharmony_ci } 511162306a36Sopenharmony_ci case FS_FULL_SIZE_INFORMATION: 511262306a36Sopenharmony_ci { 511362306a36Sopenharmony_ci struct smb2_fs_full_size_info *info; 511462306a36Sopenharmony_ci 511562306a36Sopenharmony_ci info = (struct smb2_fs_full_size_info *)(rsp->Buffer); 511662306a36Sopenharmony_ci info->TotalAllocationUnits = cpu_to_le64(stfs.f_blocks); 511762306a36Sopenharmony_ci info->CallerAvailableAllocationUnits = 511862306a36Sopenharmony_ci cpu_to_le64(stfs.f_bavail); 511962306a36Sopenharmony_ci info->ActualAvailableAllocationUnits = 512062306a36Sopenharmony_ci cpu_to_le64(stfs.f_bfree); 512162306a36Sopenharmony_ci info->SectorsPerAllocationUnit = cpu_to_le32(1); 512262306a36Sopenharmony_ci info->BytesPerSector = cpu_to_le32(stfs.f_bsize); 512362306a36Sopenharmony_ci rsp->OutputBufferLength = cpu_to_le32(32); 512462306a36Sopenharmony_ci break; 512562306a36Sopenharmony_ci } 512662306a36Sopenharmony_ci case FS_OBJECT_ID_INFORMATION: 512762306a36Sopenharmony_ci { 512862306a36Sopenharmony_ci struct object_id_info *info; 512962306a36Sopenharmony_ci 513062306a36Sopenharmony_ci info = (struct object_id_info *)(rsp->Buffer); 513162306a36Sopenharmony_ci 513262306a36Sopenharmony_ci if (!user_guest(sess->user)) 513362306a36Sopenharmony_ci memcpy(info->objid, user_passkey(sess->user), 16); 513462306a36Sopenharmony_ci else 513562306a36Sopenharmony_ci memset(info->objid, 0, 16); 513662306a36Sopenharmony_ci 513762306a36Sopenharmony_ci info->extended_info.magic = cpu_to_le32(EXTENDED_INFO_MAGIC); 513862306a36Sopenharmony_ci info->extended_info.version = cpu_to_le32(1); 513962306a36Sopenharmony_ci info->extended_info.release = cpu_to_le32(1); 514062306a36Sopenharmony_ci info->extended_info.rel_date = 0; 514162306a36Sopenharmony_ci memcpy(info->extended_info.version_string, "1.1.0", strlen("1.1.0")); 514262306a36Sopenharmony_ci rsp->OutputBufferLength = cpu_to_le32(64); 514362306a36Sopenharmony_ci break; 514462306a36Sopenharmony_ci } 514562306a36Sopenharmony_ci case FS_SECTOR_SIZE_INFORMATION: 514662306a36Sopenharmony_ci { 514762306a36Sopenharmony_ci struct smb3_fs_ss_info *info; 514862306a36Sopenharmony_ci unsigned int sector_size = 514962306a36Sopenharmony_ci min_t(unsigned int, path.mnt->mnt_sb->s_blocksize, 4096); 515062306a36Sopenharmony_ci 515162306a36Sopenharmony_ci info = (struct smb3_fs_ss_info *)(rsp->Buffer); 515262306a36Sopenharmony_ci 515362306a36Sopenharmony_ci info->LogicalBytesPerSector = cpu_to_le32(sector_size); 515462306a36Sopenharmony_ci info->PhysicalBytesPerSectorForAtomicity = 515562306a36Sopenharmony_ci cpu_to_le32(sector_size); 515662306a36Sopenharmony_ci info->PhysicalBytesPerSectorForPerf = cpu_to_le32(sector_size); 515762306a36Sopenharmony_ci info->FSEffPhysicalBytesPerSectorForAtomicity = 515862306a36Sopenharmony_ci cpu_to_le32(sector_size); 515962306a36Sopenharmony_ci info->Flags = cpu_to_le32(SSINFO_FLAGS_ALIGNED_DEVICE | 516062306a36Sopenharmony_ci SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE); 516162306a36Sopenharmony_ci info->ByteOffsetForSectorAlignment = 0; 516262306a36Sopenharmony_ci info->ByteOffsetForPartitionAlignment = 0; 516362306a36Sopenharmony_ci rsp->OutputBufferLength = cpu_to_le32(28); 516462306a36Sopenharmony_ci break; 516562306a36Sopenharmony_ci } 516662306a36Sopenharmony_ci case FS_CONTROL_INFORMATION: 516762306a36Sopenharmony_ci { 516862306a36Sopenharmony_ci /* 516962306a36Sopenharmony_ci * TODO : The current implementation is based on 517062306a36Sopenharmony_ci * test result with win7(NTFS) server. It's need to 517162306a36Sopenharmony_ci * modify this to get valid Quota values 517262306a36Sopenharmony_ci * from Linux kernel 517362306a36Sopenharmony_ci */ 517462306a36Sopenharmony_ci struct smb2_fs_control_info *info; 517562306a36Sopenharmony_ci 517662306a36Sopenharmony_ci info = (struct smb2_fs_control_info *)(rsp->Buffer); 517762306a36Sopenharmony_ci info->FreeSpaceStartFiltering = 0; 517862306a36Sopenharmony_ci info->FreeSpaceThreshold = 0; 517962306a36Sopenharmony_ci info->FreeSpaceStopFiltering = 0; 518062306a36Sopenharmony_ci info->DefaultQuotaThreshold = cpu_to_le64(SMB2_NO_FID); 518162306a36Sopenharmony_ci info->DefaultQuotaLimit = cpu_to_le64(SMB2_NO_FID); 518262306a36Sopenharmony_ci info->Padding = 0; 518362306a36Sopenharmony_ci rsp->OutputBufferLength = cpu_to_le32(48); 518462306a36Sopenharmony_ci break; 518562306a36Sopenharmony_ci } 518662306a36Sopenharmony_ci case FS_POSIX_INFORMATION: 518762306a36Sopenharmony_ci { 518862306a36Sopenharmony_ci struct filesystem_posix_info *info; 518962306a36Sopenharmony_ci 519062306a36Sopenharmony_ci if (!work->tcon->posix_extensions) { 519162306a36Sopenharmony_ci pr_err("client doesn't negotiate with SMB3.1.1 POSIX Extensions\n"); 519262306a36Sopenharmony_ci rc = -EOPNOTSUPP; 519362306a36Sopenharmony_ci } else { 519462306a36Sopenharmony_ci info = (struct filesystem_posix_info *)(rsp->Buffer); 519562306a36Sopenharmony_ci info->OptimalTransferSize = cpu_to_le32(stfs.f_bsize); 519662306a36Sopenharmony_ci info->BlockSize = cpu_to_le32(stfs.f_bsize); 519762306a36Sopenharmony_ci info->TotalBlocks = cpu_to_le64(stfs.f_blocks); 519862306a36Sopenharmony_ci info->BlocksAvail = cpu_to_le64(stfs.f_bfree); 519962306a36Sopenharmony_ci info->UserBlocksAvail = cpu_to_le64(stfs.f_bavail); 520062306a36Sopenharmony_ci info->TotalFileNodes = cpu_to_le64(stfs.f_files); 520162306a36Sopenharmony_ci info->FreeFileNodes = cpu_to_le64(stfs.f_ffree); 520262306a36Sopenharmony_ci rsp->OutputBufferLength = cpu_to_le32(56); 520362306a36Sopenharmony_ci } 520462306a36Sopenharmony_ci break; 520562306a36Sopenharmony_ci } 520662306a36Sopenharmony_ci default: 520762306a36Sopenharmony_ci path_put(&path); 520862306a36Sopenharmony_ci return -EOPNOTSUPP; 520962306a36Sopenharmony_ci } 521062306a36Sopenharmony_ci rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), 521162306a36Sopenharmony_ci rsp, work->response_buf); 521262306a36Sopenharmony_ci path_put(&path); 521362306a36Sopenharmony_ci return rc; 521462306a36Sopenharmony_ci} 521562306a36Sopenharmony_ci 521662306a36Sopenharmony_cistatic int smb2_get_info_sec(struct ksmbd_work *work, 521762306a36Sopenharmony_ci struct smb2_query_info_req *req, 521862306a36Sopenharmony_ci struct smb2_query_info_rsp *rsp) 521962306a36Sopenharmony_ci{ 522062306a36Sopenharmony_ci struct ksmbd_file *fp; 522162306a36Sopenharmony_ci struct mnt_idmap *idmap; 522262306a36Sopenharmony_ci struct smb_ntsd *pntsd = (struct smb_ntsd *)rsp->Buffer, *ppntsd = NULL; 522362306a36Sopenharmony_ci struct smb_fattr fattr = {{0}}; 522462306a36Sopenharmony_ci struct inode *inode; 522562306a36Sopenharmony_ci __u32 secdesclen = 0; 522662306a36Sopenharmony_ci unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; 522762306a36Sopenharmony_ci int addition_info = le32_to_cpu(req->AdditionalInformation); 522862306a36Sopenharmony_ci int rc = 0, ppntsd_size = 0; 522962306a36Sopenharmony_ci 523062306a36Sopenharmony_ci if (addition_info & ~(OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO | 523162306a36Sopenharmony_ci PROTECTED_DACL_SECINFO | 523262306a36Sopenharmony_ci UNPROTECTED_DACL_SECINFO)) { 523362306a36Sopenharmony_ci ksmbd_debug(SMB, "Unsupported addition info: 0x%x)\n", 523462306a36Sopenharmony_ci addition_info); 523562306a36Sopenharmony_ci 523662306a36Sopenharmony_ci pntsd->revision = cpu_to_le16(1); 523762306a36Sopenharmony_ci pntsd->type = cpu_to_le16(SELF_RELATIVE | DACL_PROTECTED); 523862306a36Sopenharmony_ci pntsd->osidoffset = 0; 523962306a36Sopenharmony_ci pntsd->gsidoffset = 0; 524062306a36Sopenharmony_ci pntsd->sacloffset = 0; 524162306a36Sopenharmony_ci pntsd->dacloffset = 0; 524262306a36Sopenharmony_ci 524362306a36Sopenharmony_ci secdesclen = sizeof(struct smb_ntsd); 524462306a36Sopenharmony_ci rsp->OutputBufferLength = cpu_to_le32(secdesclen); 524562306a36Sopenharmony_ci 524662306a36Sopenharmony_ci return 0; 524762306a36Sopenharmony_ci } 524862306a36Sopenharmony_ci 524962306a36Sopenharmony_ci if (work->next_smb2_rcv_hdr_off) { 525062306a36Sopenharmony_ci if (!has_file_id(req->VolatileFileId)) { 525162306a36Sopenharmony_ci ksmbd_debug(SMB, "Compound request set FID = %llu\n", 525262306a36Sopenharmony_ci work->compound_fid); 525362306a36Sopenharmony_ci id = work->compound_fid; 525462306a36Sopenharmony_ci pid = work->compound_pfid; 525562306a36Sopenharmony_ci } 525662306a36Sopenharmony_ci } 525762306a36Sopenharmony_ci 525862306a36Sopenharmony_ci if (!has_file_id(id)) { 525962306a36Sopenharmony_ci id = req->VolatileFileId; 526062306a36Sopenharmony_ci pid = req->PersistentFileId; 526162306a36Sopenharmony_ci } 526262306a36Sopenharmony_ci 526362306a36Sopenharmony_ci fp = ksmbd_lookup_fd_slow(work, id, pid); 526462306a36Sopenharmony_ci if (!fp) 526562306a36Sopenharmony_ci return -ENOENT; 526662306a36Sopenharmony_ci 526762306a36Sopenharmony_ci idmap = file_mnt_idmap(fp->filp); 526862306a36Sopenharmony_ci inode = file_inode(fp->filp); 526962306a36Sopenharmony_ci ksmbd_acls_fattr(&fattr, idmap, inode); 527062306a36Sopenharmony_ci 527162306a36Sopenharmony_ci if (test_share_config_flag(work->tcon->share_conf, 527262306a36Sopenharmony_ci KSMBD_SHARE_FLAG_ACL_XATTR)) 527362306a36Sopenharmony_ci ppntsd_size = ksmbd_vfs_get_sd_xattr(work->conn, idmap, 527462306a36Sopenharmony_ci fp->filp->f_path.dentry, 527562306a36Sopenharmony_ci &ppntsd); 527662306a36Sopenharmony_ci 527762306a36Sopenharmony_ci /* Check if sd buffer size exceeds response buffer size */ 527862306a36Sopenharmony_ci if (smb2_resp_buf_len(work, 8) > ppntsd_size) 527962306a36Sopenharmony_ci rc = build_sec_desc(idmap, pntsd, ppntsd, ppntsd_size, 528062306a36Sopenharmony_ci addition_info, &secdesclen, &fattr); 528162306a36Sopenharmony_ci posix_acl_release(fattr.cf_acls); 528262306a36Sopenharmony_ci posix_acl_release(fattr.cf_dacls); 528362306a36Sopenharmony_ci kfree(ppntsd); 528462306a36Sopenharmony_ci ksmbd_fd_put(work, fp); 528562306a36Sopenharmony_ci if (rc) 528662306a36Sopenharmony_ci return rc; 528762306a36Sopenharmony_ci 528862306a36Sopenharmony_ci rsp->OutputBufferLength = cpu_to_le32(secdesclen); 528962306a36Sopenharmony_ci return 0; 529062306a36Sopenharmony_ci} 529162306a36Sopenharmony_ci 529262306a36Sopenharmony_ci/** 529362306a36Sopenharmony_ci * smb2_query_info() - handler for smb2 query info command 529462306a36Sopenharmony_ci * @work: smb work containing query info request buffer 529562306a36Sopenharmony_ci * 529662306a36Sopenharmony_ci * Return: 0 on success, otherwise error 529762306a36Sopenharmony_ci */ 529862306a36Sopenharmony_ciint smb2_query_info(struct ksmbd_work *work) 529962306a36Sopenharmony_ci{ 530062306a36Sopenharmony_ci struct smb2_query_info_req *req; 530162306a36Sopenharmony_ci struct smb2_query_info_rsp *rsp; 530262306a36Sopenharmony_ci int rc = 0; 530362306a36Sopenharmony_ci 530462306a36Sopenharmony_ci WORK_BUFFERS(work, req, rsp); 530562306a36Sopenharmony_ci 530662306a36Sopenharmony_ci ksmbd_debug(SMB, "GOT query info request\n"); 530762306a36Sopenharmony_ci 530862306a36Sopenharmony_ci switch (req->InfoType) { 530962306a36Sopenharmony_ci case SMB2_O_INFO_FILE: 531062306a36Sopenharmony_ci ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILE\n"); 531162306a36Sopenharmony_ci rc = smb2_get_info_file(work, req, rsp); 531262306a36Sopenharmony_ci break; 531362306a36Sopenharmony_ci case SMB2_O_INFO_FILESYSTEM: 531462306a36Sopenharmony_ci ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILESYSTEM\n"); 531562306a36Sopenharmony_ci rc = smb2_get_info_filesystem(work, req, rsp); 531662306a36Sopenharmony_ci break; 531762306a36Sopenharmony_ci case SMB2_O_INFO_SECURITY: 531862306a36Sopenharmony_ci ksmbd_debug(SMB, "GOT SMB2_O_INFO_SECURITY\n"); 531962306a36Sopenharmony_ci rc = smb2_get_info_sec(work, req, rsp); 532062306a36Sopenharmony_ci break; 532162306a36Sopenharmony_ci default: 532262306a36Sopenharmony_ci ksmbd_debug(SMB, "InfoType %d not supported yet\n", 532362306a36Sopenharmony_ci req->InfoType); 532462306a36Sopenharmony_ci rc = -EOPNOTSUPP; 532562306a36Sopenharmony_ci } 532662306a36Sopenharmony_ci 532762306a36Sopenharmony_ci if (!rc) { 532862306a36Sopenharmony_ci rsp->StructureSize = cpu_to_le16(9); 532962306a36Sopenharmony_ci rsp->OutputBufferOffset = cpu_to_le16(72); 533062306a36Sopenharmony_ci rc = ksmbd_iov_pin_rsp(work, (void *)rsp, 533162306a36Sopenharmony_ci offsetof(struct smb2_query_info_rsp, Buffer) + 533262306a36Sopenharmony_ci le32_to_cpu(rsp->OutputBufferLength)); 533362306a36Sopenharmony_ci } 533462306a36Sopenharmony_ci 533562306a36Sopenharmony_ci if (rc < 0) { 533662306a36Sopenharmony_ci if (rc == -EACCES) 533762306a36Sopenharmony_ci rsp->hdr.Status = STATUS_ACCESS_DENIED; 533862306a36Sopenharmony_ci else if (rc == -ENOENT) 533962306a36Sopenharmony_ci rsp->hdr.Status = STATUS_FILE_CLOSED; 534062306a36Sopenharmony_ci else if (rc == -EIO) 534162306a36Sopenharmony_ci rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; 534262306a36Sopenharmony_ci else if (rc == -ENOMEM) 534362306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; 534462306a36Sopenharmony_ci else if (rc == -EOPNOTSUPP || rsp->hdr.Status == 0) 534562306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_INFO_CLASS; 534662306a36Sopenharmony_ci smb2_set_err_rsp(work); 534762306a36Sopenharmony_ci 534862306a36Sopenharmony_ci ksmbd_debug(SMB, "error while processing smb2 query rc = %d\n", 534962306a36Sopenharmony_ci rc); 535062306a36Sopenharmony_ci return rc; 535162306a36Sopenharmony_ci } 535262306a36Sopenharmony_ci return 0; 535362306a36Sopenharmony_ci} 535462306a36Sopenharmony_ci 535562306a36Sopenharmony_ci/** 535662306a36Sopenharmony_ci * smb2_close_pipe() - handler for closing IPC pipe 535762306a36Sopenharmony_ci * @work: smb work containing close request buffer 535862306a36Sopenharmony_ci * 535962306a36Sopenharmony_ci * Return: 0 536062306a36Sopenharmony_ci */ 536162306a36Sopenharmony_cistatic noinline int smb2_close_pipe(struct ksmbd_work *work) 536262306a36Sopenharmony_ci{ 536362306a36Sopenharmony_ci u64 id; 536462306a36Sopenharmony_ci struct smb2_close_req *req; 536562306a36Sopenharmony_ci struct smb2_close_rsp *rsp; 536662306a36Sopenharmony_ci 536762306a36Sopenharmony_ci WORK_BUFFERS(work, req, rsp); 536862306a36Sopenharmony_ci 536962306a36Sopenharmony_ci id = req->VolatileFileId; 537062306a36Sopenharmony_ci ksmbd_session_rpc_close(work->sess, id); 537162306a36Sopenharmony_ci 537262306a36Sopenharmony_ci rsp->StructureSize = cpu_to_le16(60); 537362306a36Sopenharmony_ci rsp->Flags = 0; 537462306a36Sopenharmony_ci rsp->Reserved = 0; 537562306a36Sopenharmony_ci rsp->CreationTime = 0; 537662306a36Sopenharmony_ci rsp->LastAccessTime = 0; 537762306a36Sopenharmony_ci rsp->LastWriteTime = 0; 537862306a36Sopenharmony_ci rsp->ChangeTime = 0; 537962306a36Sopenharmony_ci rsp->AllocationSize = 0; 538062306a36Sopenharmony_ci rsp->EndOfFile = 0; 538162306a36Sopenharmony_ci rsp->Attributes = 0; 538262306a36Sopenharmony_ci 538362306a36Sopenharmony_ci return ksmbd_iov_pin_rsp(work, (void *)rsp, 538462306a36Sopenharmony_ci sizeof(struct smb2_close_rsp)); 538562306a36Sopenharmony_ci} 538662306a36Sopenharmony_ci 538762306a36Sopenharmony_ci/** 538862306a36Sopenharmony_ci * smb2_close() - handler for smb2 close file command 538962306a36Sopenharmony_ci * @work: smb work containing close request buffer 539062306a36Sopenharmony_ci * 539162306a36Sopenharmony_ci * Return: 0 539262306a36Sopenharmony_ci */ 539362306a36Sopenharmony_ciint smb2_close(struct ksmbd_work *work) 539462306a36Sopenharmony_ci{ 539562306a36Sopenharmony_ci u64 volatile_id = KSMBD_NO_FID; 539662306a36Sopenharmony_ci u64 sess_id; 539762306a36Sopenharmony_ci struct smb2_close_req *req; 539862306a36Sopenharmony_ci struct smb2_close_rsp *rsp; 539962306a36Sopenharmony_ci struct ksmbd_conn *conn = work->conn; 540062306a36Sopenharmony_ci struct ksmbd_file *fp; 540162306a36Sopenharmony_ci struct inode *inode; 540262306a36Sopenharmony_ci u64 time; 540362306a36Sopenharmony_ci int err = 0; 540462306a36Sopenharmony_ci 540562306a36Sopenharmony_ci WORK_BUFFERS(work, req, rsp); 540662306a36Sopenharmony_ci 540762306a36Sopenharmony_ci if (test_share_config_flag(work->tcon->share_conf, 540862306a36Sopenharmony_ci KSMBD_SHARE_FLAG_PIPE)) { 540962306a36Sopenharmony_ci ksmbd_debug(SMB, "IPC pipe close request\n"); 541062306a36Sopenharmony_ci return smb2_close_pipe(work); 541162306a36Sopenharmony_ci } 541262306a36Sopenharmony_ci 541362306a36Sopenharmony_ci sess_id = le64_to_cpu(req->hdr.SessionId); 541462306a36Sopenharmony_ci if (req->hdr.Flags & SMB2_FLAGS_RELATED_OPERATIONS) 541562306a36Sopenharmony_ci sess_id = work->compound_sid; 541662306a36Sopenharmony_ci 541762306a36Sopenharmony_ci work->compound_sid = 0; 541862306a36Sopenharmony_ci if (check_session_id(conn, sess_id)) { 541962306a36Sopenharmony_ci work->compound_sid = sess_id; 542062306a36Sopenharmony_ci } else { 542162306a36Sopenharmony_ci rsp->hdr.Status = STATUS_USER_SESSION_DELETED; 542262306a36Sopenharmony_ci if (req->hdr.Flags & SMB2_FLAGS_RELATED_OPERATIONS) 542362306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_PARAMETER; 542462306a36Sopenharmony_ci err = -EBADF; 542562306a36Sopenharmony_ci goto out; 542662306a36Sopenharmony_ci } 542762306a36Sopenharmony_ci 542862306a36Sopenharmony_ci if (work->next_smb2_rcv_hdr_off && 542962306a36Sopenharmony_ci !has_file_id(req->VolatileFileId)) { 543062306a36Sopenharmony_ci if (!has_file_id(work->compound_fid)) { 543162306a36Sopenharmony_ci /* file already closed, return FILE_CLOSED */ 543262306a36Sopenharmony_ci ksmbd_debug(SMB, "file already closed\n"); 543362306a36Sopenharmony_ci rsp->hdr.Status = STATUS_FILE_CLOSED; 543462306a36Sopenharmony_ci err = -EBADF; 543562306a36Sopenharmony_ci goto out; 543662306a36Sopenharmony_ci } else { 543762306a36Sopenharmony_ci ksmbd_debug(SMB, 543862306a36Sopenharmony_ci "Compound request set FID = %llu:%llu\n", 543962306a36Sopenharmony_ci work->compound_fid, 544062306a36Sopenharmony_ci work->compound_pfid); 544162306a36Sopenharmony_ci volatile_id = work->compound_fid; 544262306a36Sopenharmony_ci 544362306a36Sopenharmony_ci /* file closed, stored id is not valid anymore */ 544462306a36Sopenharmony_ci work->compound_fid = KSMBD_NO_FID; 544562306a36Sopenharmony_ci work->compound_pfid = KSMBD_NO_FID; 544662306a36Sopenharmony_ci } 544762306a36Sopenharmony_ci } else { 544862306a36Sopenharmony_ci volatile_id = req->VolatileFileId; 544962306a36Sopenharmony_ci } 545062306a36Sopenharmony_ci ksmbd_debug(SMB, "volatile_id = %llu\n", volatile_id); 545162306a36Sopenharmony_ci 545262306a36Sopenharmony_ci rsp->StructureSize = cpu_to_le16(60); 545362306a36Sopenharmony_ci rsp->Reserved = 0; 545462306a36Sopenharmony_ci 545562306a36Sopenharmony_ci if (req->Flags == SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB) { 545662306a36Sopenharmony_ci fp = ksmbd_lookup_fd_fast(work, volatile_id); 545762306a36Sopenharmony_ci if (!fp) { 545862306a36Sopenharmony_ci err = -ENOENT; 545962306a36Sopenharmony_ci goto out; 546062306a36Sopenharmony_ci } 546162306a36Sopenharmony_ci 546262306a36Sopenharmony_ci inode = file_inode(fp->filp); 546362306a36Sopenharmony_ci rsp->Flags = SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB; 546462306a36Sopenharmony_ci rsp->AllocationSize = S_ISDIR(inode->i_mode) ? 0 : 546562306a36Sopenharmony_ci cpu_to_le64(inode->i_blocks << 9); 546662306a36Sopenharmony_ci rsp->EndOfFile = cpu_to_le64(inode->i_size); 546762306a36Sopenharmony_ci rsp->Attributes = fp->f_ci->m_fattr; 546862306a36Sopenharmony_ci rsp->CreationTime = cpu_to_le64(fp->create_time); 546962306a36Sopenharmony_ci time = ksmbd_UnixTimeToNT(inode_get_atime(inode)); 547062306a36Sopenharmony_ci rsp->LastAccessTime = cpu_to_le64(time); 547162306a36Sopenharmony_ci time = ksmbd_UnixTimeToNT(inode_get_mtime(inode)); 547262306a36Sopenharmony_ci rsp->LastWriteTime = cpu_to_le64(time); 547362306a36Sopenharmony_ci time = ksmbd_UnixTimeToNT(inode_get_ctime(inode)); 547462306a36Sopenharmony_ci rsp->ChangeTime = cpu_to_le64(time); 547562306a36Sopenharmony_ci ksmbd_fd_put(work, fp); 547662306a36Sopenharmony_ci } else { 547762306a36Sopenharmony_ci rsp->Flags = 0; 547862306a36Sopenharmony_ci rsp->AllocationSize = 0; 547962306a36Sopenharmony_ci rsp->EndOfFile = 0; 548062306a36Sopenharmony_ci rsp->Attributes = 0; 548162306a36Sopenharmony_ci rsp->CreationTime = 0; 548262306a36Sopenharmony_ci rsp->LastAccessTime = 0; 548362306a36Sopenharmony_ci rsp->LastWriteTime = 0; 548462306a36Sopenharmony_ci rsp->ChangeTime = 0; 548562306a36Sopenharmony_ci } 548662306a36Sopenharmony_ci 548762306a36Sopenharmony_ci err = ksmbd_close_fd(work, volatile_id); 548862306a36Sopenharmony_ciout: 548962306a36Sopenharmony_ci if (!err) 549062306a36Sopenharmony_ci err = ksmbd_iov_pin_rsp(work, (void *)rsp, 549162306a36Sopenharmony_ci sizeof(struct smb2_close_rsp)); 549262306a36Sopenharmony_ci 549362306a36Sopenharmony_ci if (err) { 549462306a36Sopenharmony_ci if (rsp->hdr.Status == 0) 549562306a36Sopenharmony_ci rsp->hdr.Status = STATUS_FILE_CLOSED; 549662306a36Sopenharmony_ci smb2_set_err_rsp(work); 549762306a36Sopenharmony_ci } 549862306a36Sopenharmony_ci 549962306a36Sopenharmony_ci return err; 550062306a36Sopenharmony_ci} 550162306a36Sopenharmony_ci 550262306a36Sopenharmony_ci/** 550362306a36Sopenharmony_ci * smb2_echo() - handler for smb2 echo(ping) command 550462306a36Sopenharmony_ci * @work: smb work containing echo request buffer 550562306a36Sopenharmony_ci * 550662306a36Sopenharmony_ci * Return: 0 550762306a36Sopenharmony_ci */ 550862306a36Sopenharmony_ciint smb2_echo(struct ksmbd_work *work) 550962306a36Sopenharmony_ci{ 551062306a36Sopenharmony_ci struct smb2_echo_rsp *rsp = smb2_get_msg(work->response_buf); 551162306a36Sopenharmony_ci 551262306a36Sopenharmony_ci if (work->next_smb2_rcv_hdr_off) 551362306a36Sopenharmony_ci rsp = ksmbd_resp_buf_next(work); 551462306a36Sopenharmony_ci 551562306a36Sopenharmony_ci rsp->StructureSize = cpu_to_le16(4); 551662306a36Sopenharmony_ci rsp->Reserved = 0; 551762306a36Sopenharmony_ci return ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_echo_rsp)); 551862306a36Sopenharmony_ci} 551962306a36Sopenharmony_ci 552062306a36Sopenharmony_cistatic int smb2_rename(struct ksmbd_work *work, 552162306a36Sopenharmony_ci struct ksmbd_file *fp, 552262306a36Sopenharmony_ci struct smb2_file_rename_info *file_info, 552362306a36Sopenharmony_ci struct nls_table *local_nls) 552462306a36Sopenharmony_ci{ 552562306a36Sopenharmony_ci struct ksmbd_share_config *share = fp->tcon->share_conf; 552662306a36Sopenharmony_ci char *new_name = NULL; 552762306a36Sopenharmony_ci int rc, flags = 0; 552862306a36Sopenharmony_ci 552962306a36Sopenharmony_ci ksmbd_debug(SMB, "setting FILE_RENAME_INFO\n"); 553062306a36Sopenharmony_ci new_name = smb2_get_name(file_info->FileName, 553162306a36Sopenharmony_ci le32_to_cpu(file_info->FileNameLength), 553262306a36Sopenharmony_ci local_nls); 553362306a36Sopenharmony_ci if (IS_ERR(new_name)) 553462306a36Sopenharmony_ci return PTR_ERR(new_name); 553562306a36Sopenharmony_ci 553662306a36Sopenharmony_ci if (strchr(new_name, ':')) { 553762306a36Sopenharmony_ci int s_type; 553862306a36Sopenharmony_ci char *xattr_stream_name, *stream_name = NULL; 553962306a36Sopenharmony_ci size_t xattr_stream_size; 554062306a36Sopenharmony_ci int len; 554162306a36Sopenharmony_ci 554262306a36Sopenharmony_ci rc = parse_stream_name(new_name, &stream_name, &s_type); 554362306a36Sopenharmony_ci if (rc < 0) 554462306a36Sopenharmony_ci goto out; 554562306a36Sopenharmony_ci 554662306a36Sopenharmony_ci len = strlen(new_name); 554762306a36Sopenharmony_ci if (len > 0 && new_name[len - 1] != '/') { 554862306a36Sopenharmony_ci pr_err("not allow base filename in rename\n"); 554962306a36Sopenharmony_ci rc = -ESHARE; 555062306a36Sopenharmony_ci goto out; 555162306a36Sopenharmony_ci } 555262306a36Sopenharmony_ci 555362306a36Sopenharmony_ci rc = ksmbd_vfs_xattr_stream_name(stream_name, 555462306a36Sopenharmony_ci &xattr_stream_name, 555562306a36Sopenharmony_ci &xattr_stream_size, 555662306a36Sopenharmony_ci s_type); 555762306a36Sopenharmony_ci if (rc) 555862306a36Sopenharmony_ci goto out; 555962306a36Sopenharmony_ci 556062306a36Sopenharmony_ci rc = ksmbd_vfs_setxattr(file_mnt_idmap(fp->filp), 556162306a36Sopenharmony_ci &fp->filp->f_path, 556262306a36Sopenharmony_ci xattr_stream_name, 556362306a36Sopenharmony_ci NULL, 0, 0, true); 556462306a36Sopenharmony_ci if (rc < 0) { 556562306a36Sopenharmony_ci pr_err("failed to store stream name in xattr: %d\n", 556662306a36Sopenharmony_ci rc); 556762306a36Sopenharmony_ci rc = -EINVAL; 556862306a36Sopenharmony_ci goto out; 556962306a36Sopenharmony_ci } 557062306a36Sopenharmony_ci 557162306a36Sopenharmony_ci goto out; 557262306a36Sopenharmony_ci } 557362306a36Sopenharmony_ci 557462306a36Sopenharmony_ci ksmbd_debug(SMB, "new name %s\n", new_name); 557562306a36Sopenharmony_ci if (ksmbd_share_veto_filename(share, new_name)) { 557662306a36Sopenharmony_ci rc = -ENOENT; 557762306a36Sopenharmony_ci ksmbd_debug(SMB, "Can't rename vetoed file: %s\n", new_name); 557862306a36Sopenharmony_ci goto out; 557962306a36Sopenharmony_ci } 558062306a36Sopenharmony_ci 558162306a36Sopenharmony_ci if (!file_info->ReplaceIfExists) 558262306a36Sopenharmony_ci flags = RENAME_NOREPLACE; 558362306a36Sopenharmony_ci 558462306a36Sopenharmony_ci smb_break_all_levII_oplock(work, fp, 0); 558562306a36Sopenharmony_ci rc = ksmbd_vfs_rename(work, &fp->filp->f_path, new_name, flags); 558662306a36Sopenharmony_ciout: 558762306a36Sopenharmony_ci kfree(new_name); 558862306a36Sopenharmony_ci return rc; 558962306a36Sopenharmony_ci} 559062306a36Sopenharmony_ci 559162306a36Sopenharmony_cistatic int smb2_create_link(struct ksmbd_work *work, 559262306a36Sopenharmony_ci struct ksmbd_share_config *share, 559362306a36Sopenharmony_ci struct smb2_file_link_info *file_info, 559462306a36Sopenharmony_ci unsigned int buf_len, struct file *filp, 559562306a36Sopenharmony_ci struct nls_table *local_nls) 559662306a36Sopenharmony_ci{ 559762306a36Sopenharmony_ci char *link_name = NULL, *target_name = NULL, *pathname = NULL; 559862306a36Sopenharmony_ci struct path path, parent_path; 559962306a36Sopenharmony_ci bool file_present = false; 560062306a36Sopenharmony_ci int rc; 560162306a36Sopenharmony_ci 560262306a36Sopenharmony_ci if (buf_len < (u64)sizeof(struct smb2_file_link_info) + 560362306a36Sopenharmony_ci le32_to_cpu(file_info->FileNameLength)) 560462306a36Sopenharmony_ci return -EINVAL; 560562306a36Sopenharmony_ci 560662306a36Sopenharmony_ci ksmbd_debug(SMB, "setting FILE_LINK_INFORMATION\n"); 560762306a36Sopenharmony_ci pathname = kmalloc(PATH_MAX, GFP_KERNEL); 560862306a36Sopenharmony_ci if (!pathname) 560962306a36Sopenharmony_ci return -ENOMEM; 561062306a36Sopenharmony_ci 561162306a36Sopenharmony_ci link_name = smb2_get_name(file_info->FileName, 561262306a36Sopenharmony_ci le32_to_cpu(file_info->FileNameLength), 561362306a36Sopenharmony_ci local_nls); 561462306a36Sopenharmony_ci if (IS_ERR(link_name) || S_ISDIR(file_inode(filp)->i_mode)) { 561562306a36Sopenharmony_ci rc = -EINVAL; 561662306a36Sopenharmony_ci goto out; 561762306a36Sopenharmony_ci } 561862306a36Sopenharmony_ci 561962306a36Sopenharmony_ci ksmbd_debug(SMB, "link name is %s\n", link_name); 562062306a36Sopenharmony_ci target_name = file_path(filp, pathname, PATH_MAX); 562162306a36Sopenharmony_ci if (IS_ERR(target_name)) { 562262306a36Sopenharmony_ci rc = -EINVAL; 562362306a36Sopenharmony_ci goto out; 562462306a36Sopenharmony_ci } 562562306a36Sopenharmony_ci 562662306a36Sopenharmony_ci ksmbd_debug(SMB, "target name is %s\n", target_name); 562762306a36Sopenharmony_ci rc = ksmbd_vfs_kern_path_locked(work, link_name, LOOKUP_NO_SYMLINKS, 562862306a36Sopenharmony_ci &parent_path, &path, 0); 562962306a36Sopenharmony_ci if (rc) { 563062306a36Sopenharmony_ci if (rc != -ENOENT) 563162306a36Sopenharmony_ci goto out; 563262306a36Sopenharmony_ci } else 563362306a36Sopenharmony_ci file_present = true; 563462306a36Sopenharmony_ci 563562306a36Sopenharmony_ci if (file_info->ReplaceIfExists) { 563662306a36Sopenharmony_ci if (file_present) { 563762306a36Sopenharmony_ci rc = ksmbd_vfs_remove_file(work, &path); 563862306a36Sopenharmony_ci if (rc) { 563962306a36Sopenharmony_ci rc = -EINVAL; 564062306a36Sopenharmony_ci ksmbd_debug(SMB, "cannot delete %s\n", 564162306a36Sopenharmony_ci link_name); 564262306a36Sopenharmony_ci goto out; 564362306a36Sopenharmony_ci } 564462306a36Sopenharmony_ci } 564562306a36Sopenharmony_ci } else { 564662306a36Sopenharmony_ci if (file_present) { 564762306a36Sopenharmony_ci rc = -EEXIST; 564862306a36Sopenharmony_ci ksmbd_debug(SMB, "link already exists\n"); 564962306a36Sopenharmony_ci goto out; 565062306a36Sopenharmony_ci } 565162306a36Sopenharmony_ci } 565262306a36Sopenharmony_ci 565362306a36Sopenharmony_ci rc = ksmbd_vfs_link(work, target_name, link_name); 565462306a36Sopenharmony_ci if (rc) 565562306a36Sopenharmony_ci rc = -EINVAL; 565662306a36Sopenharmony_ciout: 565762306a36Sopenharmony_ci if (file_present) 565862306a36Sopenharmony_ci ksmbd_vfs_kern_path_unlock(&parent_path, &path); 565962306a36Sopenharmony_ci 566062306a36Sopenharmony_ci if (!IS_ERR(link_name)) 566162306a36Sopenharmony_ci kfree(link_name); 566262306a36Sopenharmony_ci kfree(pathname); 566362306a36Sopenharmony_ci return rc; 566462306a36Sopenharmony_ci} 566562306a36Sopenharmony_ci 566662306a36Sopenharmony_cistatic int set_file_basic_info(struct ksmbd_file *fp, 566762306a36Sopenharmony_ci struct smb2_file_basic_info *file_info, 566862306a36Sopenharmony_ci struct ksmbd_share_config *share) 566962306a36Sopenharmony_ci{ 567062306a36Sopenharmony_ci struct iattr attrs; 567162306a36Sopenharmony_ci struct file *filp; 567262306a36Sopenharmony_ci struct inode *inode; 567362306a36Sopenharmony_ci struct mnt_idmap *idmap; 567462306a36Sopenharmony_ci int rc = 0; 567562306a36Sopenharmony_ci 567662306a36Sopenharmony_ci if (!(fp->daccess & FILE_WRITE_ATTRIBUTES_LE)) 567762306a36Sopenharmony_ci return -EACCES; 567862306a36Sopenharmony_ci 567962306a36Sopenharmony_ci attrs.ia_valid = 0; 568062306a36Sopenharmony_ci filp = fp->filp; 568162306a36Sopenharmony_ci inode = file_inode(filp); 568262306a36Sopenharmony_ci idmap = file_mnt_idmap(filp); 568362306a36Sopenharmony_ci 568462306a36Sopenharmony_ci if (file_info->CreationTime) 568562306a36Sopenharmony_ci fp->create_time = le64_to_cpu(file_info->CreationTime); 568662306a36Sopenharmony_ci 568762306a36Sopenharmony_ci if (file_info->LastAccessTime) { 568862306a36Sopenharmony_ci attrs.ia_atime = ksmbd_NTtimeToUnix(file_info->LastAccessTime); 568962306a36Sopenharmony_ci attrs.ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET); 569062306a36Sopenharmony_ci } 569162306a36Sopenharmony_ci 569262306a36Sopenharmony_ci attrs.ia_valid |= ATTR_CTIME; 569362306a36Sopenharmony_ci if (file_info->ChangeTime) 569462306a36Sopenharmony_ci attrs.ia_ctime = ksmbd_NTtimeToUnix(file_info->ChangeTime); 569562306a36Sopenharmony_ci else 569662306a36Sopenharmony_ci attrs.ia_ctime = inode_get_ctime(inode); 569762306a36Sopenharmony_ci 569862306a36Sopenharmony_ci if (file_info->LastWriteTime) { 569962306a36Sopenharmony_ci attrs.ia_mtime = ksmbd_NTtimeToUnix(file_info->LastWriteTime); 570062306a36Sopenharmony_ci attrs.ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET); 570162306a36Sopenharmony_ci } 570262306a36Sopenharmony_ci 570362306a36Sopenharmony_ci if (file_info->Attributes) { 570462306a36Sopenharmony_ci if (!S_ISDIR(inode->i_mode) && 570562306a36Sopenharmony_ci file_info->Attributes & FILE_ATTRIBUTE_DIRECTORY_LE) { 570662306a36Sopenharmony_ci pr_err("can't change a file to a directory\n"); 570762306a36Sopenharmony_ci return -EINVAL; 570862306a36Sopenharmony_ci } 570962306a36Sopenharmony_ci 571062306a36Sopenharmony_ci if (!(S_ISDIR(inode->i_mode) && file_info->Attributes == FILE_ATTRIBUTE_NORMAL_LE)) 571162306a36Sopenharmony_ci fp->f_ci->m_fattr = file_info->Attributes | 571262306a36Sopenharmony_ci (fp->f_ci->m_fattr & FILE_ATTRIBUTE_DIRECTORY_LE); 571362306a36Sopenharmony_ci } 571462306a36Sopenharmony_ci 571562306a36Sopenharmony_ci if (test_share_config_flag(share, KSMBD_SHARE_FLAG_STORE_DOS_ATTRS) && 571662306a36Sopenharmony_ci (file_info->CreationTime || file_info->Attributes)) { 571762306a36Sopenharmony_ci struct xattr_dos_attrib da = {0}; 571862306a36Sopenharmony_ci 571962306a36Sopenharmony_ci da.version = 4; 572062306a36Sopenharmony_ci da.itime = fp->itime; 572162306a36Sopenharmony_ci da.create_time = fp->create_time; 572262306a36Sopenharmony_ci da.attr = le32_to_cpu(fp->f_ci->m_fattr); 572362306a36Sopenharmony_ci da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME | 572462306a36Sopenharmony_ci XATTR_DOSINFO_ITIME; 572562306a36Sopenharmony_ci 572662306a36Sopenharmony_ci rc = ksmbd_vfs_set_dos_attrib_xattr(idmap, &filp->f_path, &da, 572762306a36Sopenharmony_ci true); 572862306a36Sopenharmony_ci if (rc) 572962306a36Sopenharmony_ci ksmbd_debug(SMB, 573062306a36Sopenharmony_ci "failed to restore file attribute in EA\n"); 573162306a36Sopenharmony_ci rc = 0; 573262306a36Sopenharmony_ci } 573362306a36Sopenharmony_ci 573462306a36Sopenharmony_ci if (attrs.ia_valid) { 573562306a36Sopenharmony_ci struct dentry *dentry = filp->f_path.dentry; 573662306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 573762306a36Sopenharmony_ci 573862306a36Sopenharmony_ci if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) 573962306a36Sopenharmony_ci return -EACCES; 574062306a36Sopenharmony_ci 574162306a36Sopenharmony_ci inode_lock(inode); 574262306a36Sopenharmony_ci inode_set_ctime_to_ts(inode, attrs.ia_ctime); 574362306a36Sopenharmony_ci attrs.ia_valid &= ~ATTR_CTIME; 574462306a36Sopenharmony_ci rc = notify_change(idmap, dentry, &attrs, NULL); 574562306a36Sopenharmony_ci inode_unlock(inode); 574662306a36Sopenharmony_ci } 574762306a36Sopenharmony_ci return rc; 574862306a36Sopenharmony_ci} 574962306a36Sopenharmony_ci 575062306a36Sopenharmony_cistatic int set_file_allocation_info(struct ksmbd_work *work, 575162306a36Sopenharmony_ci struct ksmbd_file *fp, 575262306a36Sopenharmony_ci struct smb2_file_alloc_info *file_alloc_info) 575362306a36Sopenharmony_ci{ 575462306a36Sopenharmony_ci /* 575562306a36Sopenharmony_ci * TODO : It's working fine only when store dos attributes 575662306a36Sopenharmony_ci * is not yes. need to implement a logic which works 575762306a36Sopenharmony_ci * properly with any smb.conf option 575862306a36Sopenharmony_ci */ 575962306a36Sopenharmony_ci 576062306a36Sopenharmony_ci loff_t alloc_blks; 576162306a36Sopenharmony_ci struct inode *inode; 576262306a36Sopenharmony_ci int rc; 576362306a36Sopenharmony_ci 576462306a36Sopenharmony_ci if (!(fp->daccess & FILE_WRITE_DATA_LE)) 576562306a36Sopenharmony_ci return -EACCES; 576662306a36Sopenharmony_ci 576762306a36Sopenharmony_ci alloc_blks = (le64_to_cpu(file_alloc_info->AllocationSize) + 511) >> 9; 576862306a36Sopenharmony_ci inode = file_inode(fp->filp); 576962306a36Sopenharmony_ci 577062306a36Sopenharmony_ci if (alloc_blks > inode->i_blocks) { 577162306a36Sopenharmony_ci smb_break_all_levII_oplock(work, fp, 1); 577262306a36Sopenharmony_ci rc = vfs_fallocate(fp->filp, FALLOC_FL_KEEP_SIZE, 0, 577362306a36Sopenharmony_ci alloc_blks * 512); 577462306a36Sopenharmony_ci if (rc && rc != -EOPNOTSUPP) { 577562306a36Sopenharmony_ci pr_err("vfs_fallocate is failed : %d\n", rc); 577662306a36Sopenharmony_ci return rc; 577762306a36Sopenharmony_ci } 577862306a36Sopenharmony_ci } else if (alloc_blks < inode->i_blocks) { 577962306a36Sopenharmony_ci loff_t size; 578062306a36Sopenharmony_ci 578162306a36Sopenharmony_ci /* 578262306a36Sopenharmony_ci * Allocation size could be smaller than original one 578362306a36Sopenharmony_ci * which means allocated blocks in file should be 578462306a36Sopenharmony_ci * deallocated. use truncate to cut out it, but inode 578562306a36Sopenharmony_ci * size is also updated with truncate offset. 578662306a36Sopenharmony_ci * inode size is retained by backup inode size. 578762306a36Sopenharmony_ci */ 578862306a36Sopenharmony_ci size = i_size_read(inode); 578962306a36Sopenharmony_ci rc = ksmbd_vfs_truncate(work, fp, alloc_blks * 512); 579062306a36Sopenharmony_ci if (rc) { 579162306a36Sopenharmony_ci pr_err("truncate failed!, err %d\n", rc); 579262306a36Sopenharmony_ci return rc; 579362306a36Sopenharmony_ci } 579462306a36Sopenharmony_ci if (size < alloc_blks * 512) 579562306a36Sopenharmony_ci i_size_write(inode, size); 579662306a36Sopenharmony_ci } 579762306a36Sopenharmony_ci return 0; 579862306a36Sopenharmony_ci} 579962306a36Sopenharmony_ci 580062306a36Sopenharmony_cistatic int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp, 580162306a36Sopenharmony_ci struct smb2_file_eof_info *file_eof_info) 580262306a36Sopenharmony_ci{ 580362306a36Sopenharmony_ci loff_t newsize; 580462306a36Sopenharmony_ci struct inode *inode; 580562306a36Sopenharmony_ci int rc; 580662306a36Sopenharmony_ci 580762306a36Sopenharmony_ci if (!(fp->daccess & FILE_WRITE_DATA_LE)) 580862306a36Sopenharmony_ci return -EACCES; 580962306a36Sopenharmony_ci 581062306a36Sopenharmony_ci newsize = le64_to_cpu(file_eof_info->EndOfFile); 581162306a36Sopenharmony_ci inode = file_inode(fp->filp); 581262306a36Sopenharmony_ci 581362306a36Sopenharmony_ci /* 581462306a36Sopenharmony_ci * If FILE_END_OF_FILE_INFORMATION of set_info_file is called 581562306a36Sopenharmony_ci * on FAT32 shared device, truncate execution time is too long 581662306a36Sopenharmony_ci * and network error could cause from windows client. because 581762306a36Sopenharmony_ci * truncate of some filesystem like FAT32 fill zero data in 581862306a36Sopenharmony_ci * truncated range. 581962306a36Sopenharmony_ci */ 582062306a36Sopenharmony_ci if (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC) { 582162306a36Sopenharmony_ci ksmbd_debug(SMB, "truncated to newsize %lld\n", newsize); 582262306a36Sopenharmony_ci rc = ksmbd_vfs_truncate(work, fp, newsize); 582362306a36Sopenharmony_ci if (rc) { 582462306a36Sopenharmony_ci ksmbd_debug(SMB, "truncate failed!, err %d\n", rc); 582562306a36Sopenharmony_ci if (rc != -EAGAIN) 582662306a36Sopenharmony_ci rc = -EBADF; 582762306a36Sopenharmony_ci return rc; 582862306a36Sopenharmony_ci } 582962306a36Sopenharmony_ci } 583062306a36Sopenharmony_ci return 0; 583162306a36Sopenharmony_ci} 583262306a36Sopenharmony_ci 583362306a36Sopenharmony_cistatic int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp, 583462306a36Sopenharmony_ci struct smb2_file_rename_info *rename_info, 583562306a36Sopenharmony_ci unsigned int buf_len) 583662306a36Sopenharmony_ci{ 583762306a36Sopenharmony_ci if (!(fp->daccess & FILE_DELETE_LE)) { 583862306a36Sopenharmony_ci pr_err("no right to delete : 0x%x\n", fp->daccess); 583962306a36Sopenharmony_ci return -EACCES; 584062306a36Sopenharmony_ci } 584162306a36Sopenharmony_ci 584262306a36Sopenharmony_ci if (buf_len < (u64)sizeof(struct smb2_file_rename_info) + 584362306a36Sopenharmony_ci le32_to_cpu(rename_info->FileNameLength)) 584462306a36Sopenharmony_ci return -EINVAL; 584562306a36Sopenharmony_ci 584662306a36Sopenharmony_ci if (!le32_to_cpu(rename_info->FileNameLength)) 584762306a36Sopenharmony_ci return -EINVAL; 584862306a36Sopenharmony_ci 584962306a36Sopenharmony_ci return smb2_rename(work, fp, rename_info, work->conn->local_nls); 585062306a36Sopenharmony_ci} 585162306a36Sopenharmony_ci 585262306a36Sopenharmony_cistatic int set_file_disposition_info(struct ksmbd_file *fp, 585362306a36Sopenharmony_ci struct smb2_file_disposition_info *file_info) 585462306a36Sopenharmony_ci{ 585562306a36Sopenharmony_ci struct inode *inode; 585662306a36Sopenharmony_ci 585762306a36Sopenharmony_ci if (!(fp->daccess & FILE_DELETE_LE)) { 585862306a36Sopenharmony_ci pr_err("no right to delete : 0x%x\n", fp->daccess); 585962306a36Sopenharmony_ci return -EACCES; 586062306a36Sopenharmony_ci } 586162306a36Sopenharmony_ci 586262306a36Sopenharmony_ci inode = file_inode(fp->filp); 586362306a36Sopenharmony_ci if (file_info->DeletePending) { 586462306a36Sopenharmony_ci if (S_ISDIR(inode->i_mode) && 586562306a36Sopenharmony_ci ksmbd_vfs_empty_dir(fp) == -ENOTEMPTY) 586662306a36Sopenharmony_ci return -EBUSY; 586762306a36Sopenharmony_ci ksmbd_set_inode_pending_delete(fp); 586862306a36Sopenharmony_ci } else { 586962306a36Sopenharmony_ci ksmbd_clear_inode_pending_delete(fp); 587062306a36Sopenharmony_ci } 587162306a36Sopenharmony_ci return 0; 587262306a36Sopenharmony_ci} 587362306a36Sopenharmony_ci 587462306a36Sopenharmony_cistatic int set_file_position_info(struct ksmbd_file *fp, 587562306a36Sopenharmony_ci struct smb2_file_pos_info *file_info) 587662306a36Sopenharmony_ci{ 587762306a36Sopenharmony_ci loff_t current_byte_offset; 587862306a36Sopenharmony_ci unsigned long sector_size; 587962306a36Sopenharmony_ci struct inode *inode; 588062306a36Sopenharmony_ci 588162306a36Sopenharmony_ci inode = file_inode(fp->filp); 588262306a36Sopenharmony_ci current_byte_offset = le64_to_cpu(file_info->CurrentByteOffset); 588362306a36Sopenharmony_ci sector_size = inode->i_sb->s_blocksize; 588462306a36Sopenharmony_ci 588562306a36Sopenharmony_ci if (current_byte_offset < 0 || 588662306a36Sopenharmony_ci (fp->coption == FILE_NO_INTERMEDIATE_BUFFERING_LE && 588762306a36Sopenharmony_ci current_byte_offset & (sector_size - 1))) { 588862306a36Sopenharmony_ci pr_err("CurrentByteOffset is not valid : %llu\n", 588962306a36Sopenharmony_ci current_byte_offset); 589062306a36Sopenharmony_ci return -EINVAL; 589162306a36Sopenharmony_ci } 589262306a36Sopenharmony_ci 589362306a36Sopenharmony_ci fp->filp->f_pos = current_byte_offset; 589462306a36Sopenharmony_ci return 0; 589562306a36Sopenharmony_ci} 589662306a36Sopenharmony_ci 589762306a36Sopenharmony_cistatic int set_file_mode_info(struct ksmbd_file *fp, 589862306a36Sopenharmony_ci struct smb2_file_mode_info *file_info) 589962306a36Sopenharmony_ci{ 590062306a36Sopenharmony_ci __le32 mode; 590162306a36Sopenharmony_ci 590262306a36Sopenharmony_ci mode = file_info->Mode; 590362306a36Sopenharmony_ci 590462306a36Sopenharmony_ci if ((mode & ~FILE_MODE_INFO_MASK)) { 590562306a36Sopenharmony_ci pr_err("Mode is not valid : 0x%x\n", le32_to_cpu(mode)); 590662306a36Sopenharmony_ci return -EINVAL; 590762306a36Sopenharmony_ci } 590862306a36Sopenharmony_ci 590962306a36Sopenharmony_ci /* 591062306a36Sopenharmony_ci * TODO : need to implement consideration for 591162306a36Sopenharmony_ci * FILE_SYNCHRONOUS_IO_ALERT and FILE_SYNCHRONOUS_IO_NONALERT 591262306a36Sopenharmony_ci */ 591362306a36Sopenharmony_ci ksmbd_vfs_set_fadvise(fp->filp, mode); 591462306a36Sopenharmony_ci fp->coption = mode; 591562306a36Sopenharmony_ci return 0; 591662306a36Sopenharmony_ci} 591762306a36Sopenharmony_ci 591862306a36Sopenharmony_ci/** 591962306a36Sopenharmony_ci * smb2_set_info_file() - handler for smb2 set info command 592062306a36Sopenharmony_ci * @work: smb work containing set info command buffer 592162306a36Sopenharmony_ci * @fp: ksmbd_file pointer 592262306a36Sopenharmony_ci * @req: request buffer pointer 592362306a36Sopenharmony_ci * @share: ksmbd_share_config pointer 592462306a36Sopenharmony_ci * 592562306a36Sopenharmony_ci * Return: 0 on success, otherwise error 592662306a36Sopenharmony_ci * TODO: need to implement an error handling for STATUS_INFO_LENGTH_MISMATCH 592762306a36Sopenharmony_ci */ 592862306a36Sopenharmony_cistatic int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, 592962306a36Sopenharmony_ci struct smb2_set_info_req *req, 593062306a36Sopenharmony_ci struct ksmbd_share_config *share) 593162306a36Sopenharmony_ci{ 593262306a36Sopenharmony_ci unsigned int buf_len = le32_to_cpu(req->BufferLength); 593362306a36Sopenharmony_ci 593462306a36Sopenharmony_ci switch (req->FileInfoClass) { 593562306a36Sopenharmony_ci case FILE_BASIC_INFORMATION: 593662306a36Sopenharmony_ci { 593762306a36Sopenharmony_ci if (buf_len < sizeof(struct smb2_file_basic_info)) 593862306a36Sopenharmony_ci return -EINVAL; 593962306a36Sopenharmony_ci 594062306a36Sopenharmony_ci return set_file_basic_info(fp, (struct smb2_file_basic_info *)req->Buffer, share); 594162306a36Sopenharmony_ci } 594262306a36Sopenharmony_ci case FILE_ALLOCATION_INFORMATION: 594362306a36Sopenharmony_ci { 594462306a36Sopenharmony_ci if (buf_len < sizeof(struct smb2_file_alloc_info)) 594562306a36Sopenharmony_ci return -EINVAL; 594662306a36Sopenharmony_ci 594762306a36Sopenharmony_ci return set_file_allocation_info(work, fp, 594862306a36Sopenharmony_ci (struct smb2_file_alloc_info *)req->Buffer); 594962306a36Sopenharmony_ci } 595062306a36Sopenharmony_ci case FILE_END_OF_FILE_INFORMATION: 595162306a36Sopenharmony_ci { 595262306a36Sopenharmony_ci if (buf_len < sizeof(struct smb2_file_eof_info)) 595362306a36Sopenharmony_ci return -EINVAL; 595462306a36Sopenharmony_ci 595562306a36Sopenharmony_ci return set_end_of_file_info(work, fp, 595662306a36Sopenharmony_ci (struct smb2_file_eof_info *)req->Buffer); 595762306a36Sopenharmony_ci } 595862306a36Sopenharmony_ci case FILE_RENAME_INFORMATION: 595962306a36Sopenharmony_ci { 596062306a36Sopenharmony_ci if (buf_len < sizeof(struct smb2_file_rename_info)) 596162306a36Sopenharmony_ci return -EINVAL; 596262306a36Sopenharmony_ci 596362306a36Sopenharmony_ci return set_rename_info(work, fp, 596462306a36Sopenharmony_ci (struct smb2_file_rename_info *)req->Buffer, 596562306a36Sopenharmony_ci buf_len); 596662306a36Sopenharmony_ci } 596762306a36Sopenharmony_ci case FILE_LINK_INFORMATION: 596862306a36Sopenharmony_ci { 596962306a36Sopenharmony_ci if (buf_len < sizeof(struct smb2_file_link_info)) 597062306a36Sopenharmony_ci return -EINVAL; 597162306a36Sopenharmony_ci 597262306a36Sopenharmony_ci return smb2_create_link(work, work->tcon->share_conf, 597362306a36Sopenharmony_ci (struct smb2_file_link_info *)req->Buffer, 597462306a36Sopenharmony_ci buf_len, fp->filp, 597562306a36Sopenharmony_ci work->conn->local_nls); 597662306a36Sopenharmony_ci } 597762306a36Sopenharmony_ci case FILE_DISPOSITION_INFORMATION: 597862306a36Sopenharmony_ci { 597962306a36Sopenharmony_ci if (buf_len < sizeof(struct smb2_file_disposition_info)) 598062306a36Sopenharmony_ci return -EINVAL; 598162306a36Sopenharmony_ci 598262306a36Sopenharmony_ci return set_file_disposition_info(fp, 598362306a36Sopenharmony_ci (struct smb2_file_disposition_info *)req->Buffer); 598462306a36Sopenharmony_ci } 598562306a36Sopenharmony_ci case FILE_FULL_EA_INFORMATION: 598662306a36Sopenharmony_ci { 598762306a36Sopenharmony_ci if (!(fp->daccess & FILE_WRITE_EA_LE)) { 598862306a36Sopenharmony_ci pr_err("Not permitted to write ext attr: 0x%x\n", 598962306a36Sopenharmony_ci fp->daccess); 599062306a36Sopenharmony_ci return -EACCES; 599162306a36Sopenharmony_ci } 599262306a36Sopenharmony_ci 599362306a36Sopenharmony_ci if (buf_len < sizeof(struct smb2_ea_info)) 599462306a36Sopenharmony_ci return -EINVAL; 599562306a36Sopenharmony_ci 599662306a36Sopenharmony_ci return smb2_set_ea((struct smb2_ea_info *)req->Buffer, 599762306a36Sopenharmony_ci buf_len, &fp->filp->f_path, true); 599862306a36Sopenharmony_ci } 599962306a36Sopenharmony_ci case FILE_POSITION_INFORMATION: 600062306a36Sopenharmony_ci { 600162306a36Sopenharmony_ci if (buf_len < sizeof(struct smb2_file_pos_info)) 600262306a36Sopenharmony_ci return -EINVAL; 600362306a36Sopenharmony_ci 600462306a36Sopenharmony_ci return set_file_position_info(fp, (struct smb2_file_pos_info *)req->Buffer); 600562306a36Sopenharmony_ci } 600662306a36Sopenharmony_ci case FILE_MODE_INFORMATION: 600762306a36Sopenharmony_ci { 600862306a36Sopenharmony_ci if (buf_len < sizeof(struct smb2_file_mode_info)) 600962306a36Sopenharmony_ci return -EINVAL; 601062306a36Sopenharmony_ci 601162306a36Sopenharmony_ci return set_file_mode_info(fp, (struct smb2_file_mode_info *)req->Buffer); 601262306a36Sopenharmony_ci } 601362306a36Sopenharmony_ci } 601462306a36Sopenharmony_ci 601562306a36Sopenharmony_ci pr_err("Unimplemented Fileinfoclass :%d\n", req->FileInfoClass); 601662306a36Sopenharmony_ci return -EOPNOTSUPP; 601762306a36Sopenharmony_ci} 601862306a36Sopenharmony_ci 601962306a36Sopenharmony_cistatic int smb2_set_info_sec(struct ksmbd_file *fp, int addition_info, 602062306a36Sopenharmony_ci char *buffer, int buf_len) 602162306a36Sopenharmony_ci{ 602262306a36Sopenharmony_ci struct smb_ntsd *pntsd = (struct smb_ntsd *)buffer; 602362306a36Sopenharmony_ci 602462306a36Sopenharmony_ci fp->saccess |= FILE_SHARE_DELETE_LE; 602562306a36Sopenharmony_ci 602662306a36Sopenharmony_ci return set_info_sec(fp->conn, fp->tcon, &fp->filp->f_path, pntsd, 602762306a36Sopenharmony_ci buf_len, false, true); 602862306a36Sopenharmony_ci} 602962306a36Sopenharmony_ci 603062306a36Sopenharmony_ci/** 603162306a36Sopenharmony_ci * smb2_set_info() - handler for smb2 set info command handler 603262306a36Sopenharmony_ci * @work: smb work containing set info request buffer 603362306a36Sopenharmony_ci * 603462306a36Sopenharmony_ci * Return: 0 on success, otherwise error 603562306a36Sopenharmony_ci */ 603662306a36Sopenharmony_ciint smb2_set_info(struct ksmbd_work *work) 603762306a36Sopenharmony_ci{ 603862306a36Sopenharmony_ci struct smb2_set_info_req *req; 603962306a36Sopenharmony_ci struct smb2_set_info_rsp *rsp; 604062306a36Sopenharmony_ci struct ksmbd_file *fp = NULL; 604162306a36Sopenharmony_ci int rc = 0; 604262306a36Sopenharmony_ci unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; 604362306a36Sopenharmony_ci 604462306a36Sopenharmony_ci ksmbd_debug(SMB, "Received set info request\n"); 604562306a36Sopenharmony_ci 604662306a36Sopenharmony_ci if (work->next_smb2_rcv_hdr_off) { 604762306a36Sopenharmony_ci req = ksmbd_req_buf_next(work); 604862306a36Sopenharmony_ci rsp = ksmbd_resp_buf_next(work); 604962306a36Sopenharmony_ci if (!has_file_id(req->VolatileFileId)) { 605062306a36Sopenharmony_ci ksmbd_debug(SMB, "Compound request set FID = %llu\n", 605162306a36Sopenharmony_ci work->compound_fid); 605262306a36Sopenharmony_ci id = work->compound_fid; 605362306a36Sopenharmony_ci pid = work->compound_pfid; 605462306a36Sopenharmony_ci } 605562306a36Sopenharmony_ci } else { 605662306a36Sopenharmony_ci req = smb2_get_msg(work->request_buf); 605762306a36Sopenharmony_ci rsp = smb2_get_msg(work->response_buf); 605862306a36Sopenharmony_ci } 605962306a36Sopenharmony_ci 606062306a36Sopenharmony_ci if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { 606162306a36Sopenharmony_ci ksmbd_debug(SMB, "User does not have write permission\n"); 606262306a36Sopenharmony_ci pr_err("User does not have write permission\n"); 606362306a36Sopenharmony_ci rc = -EACCES; 606462306a36Sopenharmony_ci goto err_out; 606562306a36Sopenharmony_ci } 606662306a36Sopenharmony_ci 606762306a36Sopenharmony_ci if (!has_file_id(id)) { 606862306a36Sopenharmony_ci id = req->VolatileFileId; 606962306a36Sopenharmony_ci pid = req->PersistentFileId; 607062306a36Sopenharmony_ci } 607162306a36Sopenharmony_ci 607262306a36Sopenharmony_ci fp = ksmbd_lookup_fd_slow(work, id, pid); 607362306a36Sopenharmony_ci if (!fp) { 607462306a36Sopenharmony_ci ksmbd_debug(SMB, "Invalid id for close: %u\n", id); 607562306a36Sopenharmony_ci rc = -ENOENT; 607662306a36Sopenharmony_ci goto err_out; 607762306a36Sopenharmony_ci } 607862306a36Sopenharmony_ci 607962306a36Sopenharmony_ci switch (req->InfoType) { 608062306a36Sopenharmony_ci case SMB2_O_INFO_FILE: 608162306a36Sopenharmony_ci ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILE\n"); 608262306a36Sopenharmony_ci rc = smb2_set_info_file(work, fp, req, work->tcon->share_conf); 608362306a36Sopenharmony_ci break; 608462306a36Sopenharmony_ci case SMB2_O_INFO_SECURITY: 608562306a36Sopenharmony_ci ksmbd_debug(SMB, "GOT SMB2_O_INFO_SECURITY\n"); 608662306a36Sopenharmony_ci if (ksmbd_override_fsids(work)) { 608762306a36Sopenharmony_ci rc = -ENOMEM; 608862306a36Sopenharmony_ci goto err_out; 608962306a36Sopenharmony_ci } 609062306a36Sopenharmony_ci rc = smb2_set_info_sec(fp, 609162306a36Sopenharmony_ci le32_to_cpu(req->AdditionalInformation), 609262306a36Sopenharmony_ci req->Buffer, 609362306a36Sopenharmony_ci le32_to_cpu(req->BufferLength)); 609462306a36Sopenharmony_ci ksmbd_revert_fsids(work); 609562306a36Sopenharmony_ci break; 609662306a36Sopenharmony_ci default: 609762306a36Sopenharmony_ci rc = -EOPNOTSUPP; 609862306a36Sopenharmony_ci } 609962306a36Sopenharmony_ci 610062306a36Sopenharmony_ci if (rc < 0) 610162306a36Sopenharmony_ci goto err_out; 610262306a36Sopenharmony_ci 610362306a36Sopenharmony_ci rsp->StructureSize = cpu_to_le16(2); 610462306a36Sopenharmony_ci rc = ksmbd_iov_pin_rsp(work, (void *)rsp, 610562306a36Sopenharmony_ci sizeof(struct smb2_set_info_rsp)); 610662306a36Sopenharmony_ci if (rc) 610762306a36Sopenharmony_ci goto err_out; 610862306a36Sopenharmony_ci ksmbd_fd_put(work, fp); 610962306a36Sopenharmony_ci return 0; 611062306a36Sopenharmony_ci 611162306a36Sopenharmony_cierr_out: 611262306a36Sopenharmony_ci if (rc == -EACCES || rc == -EPERM || rc == -EXDEV) 611362306a36Sopenharmony_ci rsp->hdr.Status = STATUS_ACCESS_DENIED; 611462306a36Sopenharmony_ci else if (rc == -EINVAL) 611562306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_PARAMETER; 611662306a36Sopenharmony_ci else if (rc == -ESHARE) 611762306a36Sopenharmony_ci rsp->hdr.Status = STATUS_SHARING_VIOLATION; 611862306a36Sopenharmony_ci else if (rc == -ENOENT) 611962306a36Sopenharmony_ci rsp->hdr.Status = STATUS_OBJECT_NAME_INVALID; 612062306a36Sopenharmony_ci else if (rc == -EBUSY || rc == -ENOTEMPTY) 612162306a36Sopenharmony_ci rsp->hdr.Status = STATUS_DIRECTORY_NOT_EMPTY; 612262306a36Sopenharmony_ci else if (rc == -EAGAIN) 612362306a36Sopenharmony_ci rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT; 612462306a36Sopenharmony_ci else if (rc == -EBADF || rc == -ESTALE) 612562306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_HANDLE; 612662306a36Sopenharmony_ci else if (rc == -EEXIST) 612762306a36Sopenharmony_ci rsp->hdr.Status = STATUS_OBJECT_NAME_COLLISION; 612862306a36Sopenharmony_ci else if (rsp->hdr.Status == 0 || rc == -EOPNOTSUPP) 612962306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_INFO_CLASS; 613062306a36Sopenharmony_ci smb2_set_err_rsp(work); 613162306a36Sopenharmony_ci ksmbd_fd_put(work, fp); 613262306a36Sopenharmony_ci ksmbd_debug(SMB, "error while processing smb2 query rc = %d\n", rc); 613362306a36Sopenharmony_ci return rc; 613462306a36Sopenharmony_ci} 613562306a36Sopenharmony_ci 613662306a36Sopenharmony_ci/** 613762306a36Sopenharmony_ci * smb2_read_pipe() - handler for smb2 read from IPC pipe 613862306a36Sopenharmony_ci * @work: smb work containing read IPC pipe command buffer 613962306a36Sopenharmony_ci * 614062306a36Sopenharmony_ci * Return: 0 on success, otherwise error 614162306a36Sopenharmony_ci */ 614262306a36Sopenharmony_cistatic noinline int smb2_read_pipe(struct ksmbd_work *work) 614362306a36Sopenharmony_ci{ 614462306a36Sopenharmony_ci int nbytes = 0, err; 614562306a36Sopenharmony_ci u64 id; 614662306a36Sopenharmony_ci struct ksmbd_rpc_command *rpc_resp; 614762306a36Sopenharmony_ci struct smb2_read_req *req; 614862306a36Sopenharmony_ci struct smb2_read_rsp *rsp; 614962306a36Sopenharmony_ci 615062306a36Sopenharmony_ci WORK_BUFFERS(work, req, rsp); 615162306a36Sopenharmony_ci 615262306a36Sopenharmony_ci id = req->VolatileFileId; 615362306a36Sopenharmony_ci 615462306a36Sopenharmony_ci rpc_resp = ksmbd_rpc_read(work->sess, id); 615562306a36Sopenharmony_ci if (rpc_resp) { 615662306a36Sopenharmony_ci void *aux_payload_buf; 615762306a36Sopenharmony_ci 615862306a36Sopenharmony_ci if (rpc_resp->flags != KSMBD_RPC_OK) { 615962306a36Sopenharmony_ci err = -EINVAL; 616062306a36Sopenharmony_ci goto out; 616162306a36Sopenharmony_ci } 616262306a36Sopenharmony_ci 616362306a36Sopenharmony_ci aux_payload_buf = 616462306a36Sopenharmony_ci kvmalloc(rpc_resp->payload_sz, GFP_KERNEL); 616562306a36Sopenharmony_ci if (!aux_payload_buf) { 616662306a36Sopenharmony_ci err = -ENOMEM; 616762306a36Sopenharmony_ci goto out; 616862306a36Sopenharmony_ci } 616962306a36Sopenharmony_ci 617062306a36Sopenharmony_ci memcpy(aux_payload_buf, rpc_resp->payload, rpc_resp->payload_sz); 617162306a36Sopenharmony_ci 617262306a36Sopenharmony_ci nbytes = rpc_resp->payload_sz; 617362306a36Sopenharmony_ci err = ksmbd_iov_pin_rsp_read(work, (void *)rsp, 617462306a36Sopenharmony_ci offsetof(struct smb2_read_rsp, Buffer), 617562306a36Sopenharmony_ci aux_payload_buf, nbytes); 617662306a36Sopenharmony_ci if (err) { 617762306a36Sopenharmony_ci kvfree(aux_payload_buf); 617862306a36Sopenharmony_ci goto out; 617962306a36Sopenharmony_ci } 618062306a36Sopenharmony_ci kvfree(rpc_resp); 618162306a36Sopenharmony_ci } else { 618262306a36Sopenharmony_ci err = ksmbd_iov_pin_rsp(work, (void *)rsp, 618362306a36Sopenharmony_ci offsetof(struct smb2_read_rsp, Buffer)); 618462306a36Sopenharmony_ci if (err) 618562306a36Sopenharmony_ci goto out; 618662306a36Sopenharmony_ci } 618762306a36Sopenharmony_ci 618862306a36Sopenharmony_ci rsp->StructureSize = cpu_to_le16(17); 618962306a36Sopenharmony_ci rsp->DataOffset = 80; 619062306a36Sopenharmony_ci rsp->Reserved = 0; 619162306a36Sopenharmony_ci rsp->DataLength = cpu_to_le32(nbytes); 619262306a36Sopenharmony_ci rsp->DataRemaining = 0; 619362306a36Sopenharmony_ci rsp->Flags = 0; 619462306a36Sopenharmony_ci return 0; 619562306a36Sopenharmony_ci 619662306a36Sopenharmony_ciout: 619762306a36Sopenharmony_ci rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; 619862306a36Sopenharmony_ci smb2_set_err_rsp(work); 619962306a36Sopenharmony_ci kvfree(rpc_resp); 620062306a36Sopenharmony_ci return err; 620162306a36Sopenharmony_ci} 620262306a36Sopenharmony_ci 620362306a36Sopenharmony_cistatic int smb2_set_remote_key_for_rdma(struct ksmbd_work *work, 620462306a36Sopenharmony_ci struct smb2_buffer_desc_v1 *desc, 620562306a36Sopenharmony_ci __le32 Channel, 620662306a36Sopenharmony_ci __le16 ChannelInfoLength) 620762306a36Sopenharmony_ci{ 620862306a36Sopenharmony_ci unsigned int i, ch_count; 620962306a36Sopenharmony_ci 621062306a36Sopenharmony_ci if (work->conn->dialect == SMB30_PROT_ID && 621162306a36Sopenharmony_ci Channel != SMB2_CHANNEL_RDMA_V1) 621262306a36Sopenharmony_ci return -EINVAL; 621362306a36Sopenharmony_ci 621462306a36Sopenharmony_ci ch_count = le16_to_cpu(ChannelInfoLength) / sizeof(*desc); 621562306a36Sopenharmony_ci if (ksmbd_debug_types & KSMBD_DEBUG_RDMA) { 621662306a36Sopenharmony_ci for (i = 0; i < ch_count; i++) { 621762306a36Sopenharmony_ci pr_info("RDMA r/w request %#x: token %#x, length %#x\n", 621862306a36Sopenharmony_ci i, 621962306a36Sopenharmony_ci le32_to_cpu(desc[i].token), 622062306a36Sopenharmony_ci le32_to_cpu(desc[i].length)); 622162306a36Sopenharmony_ci } 622262306a36Sopenharmony_ci } 622362306a36Sopenharmony_ci if (!ch_count) 622462306a36Sopenharmony_ci return -EINVAL; 622562306a36Sopenharmony_ci 622662306a36Sopenharmony_ci work->need_invalidate_rkey = 622762306a36Sopenharmony_ci (Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE); 622862306a36Sopenharmony_ci if (Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE) 622962306a36Sopenharmony_ci work->remote_key = le32_to_cpu(desc->token); 623062306a36Sopenharmony_ci return 0; 623162306a36Sopenharmony_ci} 623262306a36Sopenharmony_ci 623362306a36Sopenharmony_cistatic ssize_t smb2_read_rdma_channel(struct ksmbd_work *work, 623462306a36Sopenharmony_ci struct smb2_read_req *req, void *data_buf, 623562306a36Sopenharmony_ci size_t length) 623662306a36Sopenharmony_ci{ 623762306a36Sopenharmony_ci int err; 623862306a36Sopenharmony_ci 623962306a36Sopenharmony_ci err = ksmbd_conn_rdma_write(work->conn, data_buf, length, 624062306a36Sopenharmony_ci (struct smb2_buffer_desc_v1 *) 624162306a36Sopenharmony_ci ((char *)req + le16_to_cpu(req->ReadChannelInfoOffset)), 624262306a36Sopenharmony_ci le16_to_cpu(req->ReadChannelInfoLength)); 624362306a36Sopenharmony_ci if (err) 624462306a36Sopenharmony_ci return err; 624562306a36Sopenharmony_ci 624662306a36Sopenharmony_ci return length; 624762306a36Sopenharmony_ci} 624862306a36Sopenharmony_ci 624962306a36Sopenharmony_ci/** 625062306a36Sopenharmony_ci * smb2_read() - handler for smb2 read from file 625162306a36Sopenharmony_ci * @work: smb work containing read command buffer 625262306a36Sopenharmony_ci * 625362306a36Sopenharmony_ci * Return: 0 on success, otherwise error 625462306a36Sopenharmony_ci */ 625562306a36Sopenharmony_ciint smb2_read(struct ksmbd_work *work) 625662306a36Sopenharmony_ci{ 625762306a36Sopenharmony_ci struct ksmbd_conn *conn = work->conn; 625862306a36Sopenharmony_ci struct smb2_read_req *req; 625962306a36Sopenharmony_ci struct smb2_read_rsp *rsp; 626062306a36Sopenharmony_ci struct ksmbd_file *fp = NULL; 626162306a36Sopenharmony_ci loff_t offset; 626262306a36Sopenharmony_ci size_t length, mincount; 626362306a36Sopenharmony_ci ssize_t nbytes = 0, remain_bytes = 0; 626462306a36Sopenharmony_ci int err = 0; 626562306a36Sopenharmony_ci bool is_rdma_channel = false; 626662306a36Sopenharmony_ci unsigned int max_read_size = conn->vals->max_read_size; 626762306a36Sopenharmony_ci unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; 626862306a36Sopenharmony_ci void *aux_payload_buf; 626962306a36Sopenharmony_ci 627062306a36Sopenharmony_ci if (test_share_config_flag(work->tcon->share_conf, 627162306a36Sopenharmony_ci KSMBD_SHARE_FLAG_PIPE)) { 627262306a36Sopenharmony_ci ksmbd_debug(SMB, "IPC pipe read request\n"); 627362306a36Sopenharmony_ci return smb2_read_pipe(work); 627462306a36Sopenharmony_ci } 627562306a36Sopenharmony_ci 627662306a36Sopenharmony_ci if (work->next_smb2_rcv_hdr_off) { 627762306a36Sopenharmony_ci req = ksmbd_req_buf_next(work); 627862306a36Sopenharmony_ci rsp = ksmbd_resp_buf_next(work); 627962306a36Sopenharmony_ci if (!has_file_id(req->VolatileFileId)) { 628062306a36Sopenharmony_ci ksmbd_debug(SMB, "Compound request set FID = %llu\n", 628162306a36Sopenharmony_ci work->compound_fid); 628262306a36Sopenharmony_ci id = work->compound_fid; 628362306a36Sopenharmony_ci pid = work->compound_pfid; 628462306a36Sopenharmony_ci } 628562306a36Sopenharmony_ci } else { 628662306a36Sopenharmony_ci req = smb2_get_msg(work->request_buf); 628762306a36Sopenharmony_ci rsp = smb2_get_msg(work->response_buf); 628862306a36Sopenharmony_ci } 628962306a36Sopenharmony_ci 629062306a36Sopenharmony_ci if (!has_file_id(id)) { 629162306a36Sopenharmony_ci id = req->VolatileFileId; 629262306a36Sopenharmony_ci pid = req->PersistentFileId; 629362306a36Sopenharmony_ci } 629462306a36Sopenharmony_ci 629562306a36Sopenharmony_ci if (req->Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE || 629662306a36Sopenharmony_ci req->Channel == SMB2_CHANNEL_RDMA_V1) { 629762306a36Sopenharmony_ci is_rdma_channel = true; 629862306a36Sopenharmony_ci max_read_size = get_smbd_max_read_write_size(); 629962306a36Sopenharmony_ci } 630062306a36Sopenharmony_ci 630162306a36Sopenharmony_ci if (is_rdma_channel == true) { 630262306a36Sopenharmony_ci unsigned int ch_offset = le16_to_cpu(req->ReadChannelInfoOffset); 630362306a36Sopenharmony_ci 630462306a36Sopenharmony_ci if (ch_offset < offsetof(struct smb2_read_req, Buffer)) { 630562306a36Sopenharmony_ci err = -EINVAL; 630662306a36Sopenharmony_ci goto out; 630762306a36Sopenharmony_ci } 630862306a36Sopenharmony_ci err = smb2_set_remote_key_for_rdma(work, 630962306a36Sopenharmony_ci (struct smb2_buffer_desc_v1 *) 631062306a36Sopenharmony_ci ((char *)req + ch_offset), 631162306a36Sopenharmony_ci req->Channel, 631262306a36Sopenharmony_ci req->ReadChannelInfoLength); 631362306a36Sopenharmony_ci if (err) 631462306a36Sopenharmony_ci goto out; 631562306a36Sopenharmony_ci } 631662306a36Sopenharmony_ci 631762306a36Sopenharmony_ci fp = ksmbd_lookup_fd_slow(work, id, pid); 631862306a36Sopenharmony_ci if (!fp) { 631962306a36Sopenharmony_ci err = -ENOENT; 632062306a36Sopenharmony_ci goto out; 632162306a36Sopenharmony_ci } 632262306a36Sopenharmony_ci 632362306a36Sopenharmony_ci if (!(fp->daccess & (FILE_READ_DATA_LE | FILE_READ_ATTRIBUTES_LE))) { 632462306a36Sopenharmony_ci pr_err("Not permitted to read : 0x%x\n", fp->daccess); 632562306a36Sopenharmony_ci err = -EACCES; 632662306a36Sopenharmony_ci goto out; 632762306a36Sopenharmony_ci } 632862306a36Sopenharmony_ci 632962306a36Sopenharmony_ci offset = le64_to_cpu(req->Offset); 633062306a36Sopenharmony_ci length = le32_to_cpu(req->Length); 633162306a36Sopenharmony_ci mincount = le32_to_cpu(req->MinimumCount); 633262306a36Sopenharmony_ci 633362306a36Sopenharmony_ci if (length > max_read_size) { 633462306a36Sopenharmony_ci ksmbd_debug(SMB, "limiting read size to max size(%u)\n", 633562306a36Sopenharmony_ci max_read_size); 633662306a36Sopenharmony_ci err = -EINVAL; 633762306a36Sopenharmony_ci goto out; 633862306a36Sopenharmony_ci } 633962306a36Sopenharmony_ci 634062306a36Sopenharmony_ci ksmbd_debug(SMB, "filename %pD, offset %lld, len %zu\n", 634162306a36Sopenharmony_ci fp->filp, offset, length); 634262306a36Sopenharmony_ci 634362306a36Sopenharmony_ci aux_payload_buf = kvzalloc(length, GFP_KERNEL); 634462306a36Sopenharmony_ci if (!aux_payload_buf) { 634562306a36Sopenharmony_ci err = -ENOMEM; 634662306a36Sopenharmony_ci goto out; 634762306a36Sopenharmony_ci } 634862306a36Sopenharmony_ci 634962306a36Sopenharmony_ci nbytes = ksmbd_vfs_read(work, fp, length, &offset, aux_payload_buf); 635062306a36Sopenharmony_ci if (nbytes < 0) { 635162306a36Sopenharmony_ci err = nbytes; 635262306a36Sopenharmony_ci goto out; 635362306a36Sopenharmony_ci } 635462306a36Sopenharmony_ci 635562306a36Sopenharmony_ci if ((nbytes == 0 && length != 0) || nbytes < mincount) { 635662306a36Sopenharmony_ci kvfree(aux_payload_buf); 635762306a36Sopenharmony_ci rsp->hdr.Status = STATUS_END_OF_FILE; 635862306a36Sopenharmony_ci smb2_set_err_rsp(work); 635962306a36Sopenharmony_ci ksmbd_fd_put(work, fp); 636062306a36Sopenharmony_ci return 0; 636162306a36Sopenharmony_ci } 636262306a36Sopenharmony_ci 636362306a36Sopenharmony_ci ksmbd_debug(SMB, "nbytes %zu, offset %lld mincount %zu\n", 636462306a36Sopenharmony_ci nbytes, offset, mincount); 636562306a36Sopenharmony_ci 636662306a36Sopenharmony_ci if (is_rdma_channel == true) { 636762306a36Sopenharmony_ci /* write data to the client using rdma channel */ 636862306a36Sopenharmony_ci remain_bytes = smb2_read_rdma_channel(work, req, 636962306a36Sopenharmony_ci aux_payload_buf, 637062306a36Sopenharmony_ci nbytes); 637162306a36Sopenharmony_ci kvfree(aux_payload_buf); 637262306a36Sopenharmony_ci aux_payload_buf = NULL; 637362306a36Sopenharmony_ci nbytes = 0; 637462306a36Sopenharmony_ci if (remain_bytes < 0) { 637562306a36Sopenharmony_ci err = (int)remain_bytes; 637662306a36Sopenharmony_ci goto out; 637762306a36Sopenharmony_ci } 637862306a36Sopenharmony_ci } 637962306a36Sopenharmony_ci 638062306a36Sopenharmony_ci rsp->StructureSize = cpu_to_le16(17); 638162306a36Sopenharmony_ci rsp->DataOffset = 80; 638262306a36Sopenharmony_ci rsp->Reserved = 0; 638362306a36Sopenharmony_ci rsp->DataLength = cpu_to_le32(nbytes); 638462306a36Sopenharmony_ci rsp->DataRemaining = cpu_to_le32(remain_bytes); 638562306a36Sopenharmony_ci rsp->Flags = 0; 638662306a36Sopenharmony_ci err = ksmbd_iov_pin_rsp_read(work, (void *)rsp, 638762306a36Sopenharmony_ci offsetof(struct smb2_read_rsp, Buffer), 638862306a36Sopenharmony_ci aux_payload_buf, nbytes); 638962306a36Sopenharmony_ci if (err) { 639062306a36Sopenharmony_ci kvfree(aux_payload_buf); 639162306a36Sopenharmony_ci goto out; 639262306a36Sopenharmony_ci } 639362306a36Sopenharmony_ci ksmbd_fd_put(work, fp); 639462306a36Sopenharmony_ci return 0; 639562306a36Sopenharmony_ci 639662306a36Sopenharmony_ciout: 639762306a36Sopenharmony_ci if (err) { 639862306a36Sopenharmony_ci if (err == -EISDIR) 639962306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_DEVICE_REQUEST; 640062306a36Sopenharmony_ci else if (err == -EAGAIN) 640162306a36Sopenharmony_ci rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT; 640262306a36Sopenharmony_ci else if (err == -ENOENT) 640362306a36Sopenharmony_ci rsp->hdr.Status = STATUS_FILE_CLOSED; 640462306a36Sopenharmony_ci else if (err == -EACCES) 640562306a36Sopenharmony_ci rsp->hdr.Status = STATUS_ACCESS_DENIED; 640662306a36Sopenharmony_ci else if (err == -ESHARE) 640762306a36Sopenharmony_ci rsp->hdr.Status = STATUS_SHARING_VIOLATION; 640862306a36Sopenharmony_ci else if (err == -EINVAL) 640962306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_PARAMETER; 641062306a36Sopenharmony_ci else 641162306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_HANDLE; 641262306a36Sopenharmony_ci 641362306a36Sopenharmony_ci smb2_set_err_rsp(work); 641462306a36Sopenharmony_ci } 641562306a36Sopenharmony_ci ksmbd_fd_put(work, fp); 641662306a36Sopenharmony_ci return err; 641762306a36Sopenharmony_ci} 641862306a36Sopenharmony_ci 641962306a36Sopenharmony_ci/** 642062306a36Sopenharmony_ci * smb2_write_pipe() - handler for smb2 write on IPC pipe 642162306a36Sopenharmony_ci * @work: smb work containing write IPC pipe command buffer 642262306a36Sopenharmony_ci * 642362306a36Sopenharmony_ci * Return: 0 on success, otherwise error 642462306a36Sopenharmony_ci */ 642562306a36Sopenharmony_cistatic noinline int smb2_write_pipe(struct ksmbd_work *work) 642662306a36Sopenharmony_ci{ 642762306a36Sopenharmony_ci struct smb2_write_req *req; 642862306a36Sopenharmony_ci struct smb2_write_rsp *rsp; 642962306a36Sopenharmony_ci struct ksmbd_rpc_command *rpc_resp; 643062306a36Sopenharmony_ci u64 id = 0; 643162306a36Sopenharmony_ci int err = 0, ret = 0; 643262306a36Sopenharmony_ci char *data_buf; 643362306a36Sopenharmony_ci size_t length; 643462306a36Sopenharmony_ci 643562306a36Sopenharmony_ci WORK_BUFFERS(work, req, rsp); 643662306a36Sopenharmony_ci 643762306a36Sopenharmony_ci length = le32_to_cpu(req->Length); 643862306a36Sopenharmony_ci id = req->VolatileFileId; 643962306a36Sopenharmony_ci 644062306a36Sopenharmony_ci if ((u64)le16_to_cpu(req->DataOffset) + length > 644162306a36Sopenharmony_ci get_rfc1002_len(work->request_buf)) { 644262306a36Sopenharmony_ci pr_err("invalid write data offset %u, smb_len %u\n", 644362306a36Sopenharmony_ci le16_to_cpu(req->DataOffset), 644462306a36Sopenharmony_ci get_rfc1002_len(work->request_buf)); 644562306a36Sopenharmony_ci err = -EINVAL; 644662306a36Sopenharmony_ci goto out; 644762306a36Sopenharmony_ci } 644862306a36Sopenharmony_ci 644962306a36Sopenharmony_ci data_buf = (char *)(((char *)&req->hdr.ProtocolId) + 645062306a36Sopenharmony_ci le16_to_cpu(req->DataOffset)); 645162306a36Sopenharmony_ci 645262306a36Sopenharmony_ci rpc_resp = ksmbd_rpc_write(work->sess, id, data_buf, length); 645362306a36Sopenharmony_ci if (rpc_resp) { 645462306a36Sopenharmony_ci if (rpc_resp->flags == KSMBD_RPC_ENOTIMPLEMENTED) { 645562306a36Sopenharmony_ci rsp->hdr.Status = STATUS_NOT_SUPPORTED; 645662306a36Sopenharmony_ci kvfree(rpc_resp); 645762306a36Sopenharmony_ci smb2_set_err_rsp(work); 645862306a36Sopenharmony_ci return -EOPNOTSUPP; 645962306a36Sopenharmony_ci } 646062306a36Sopenharmony_ci if (rpc_resp->flags != KSMBD_RPC_OK) { 646162306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_HANDLE; 646262306a36Sopenharmony_ci smb2_set_err_rsp(work); 646362306a36Sopenharmony_ci kvfree(rpc_resp); 646462306a36Sopenharmony_ci return ret; 646562306a36Sopenharmony_ci } 646662306a36Sopenharmony_ci kvfree(rpc_resp); 646762306a36Sopenharmony_ci } 646862306a36Sopenharmony_ci 646962306a36Sopenharmony_ci rsp->StructureSize = cpu_to_le16(17); 647062306a36Sopenharmony_ci rsp->DataOffset = 0; 647162306a36Sopenharmony_ci rsp->Reserved = 0; 647262306a36Sopenharmony_ci rsp->DataLength = cpu_to_le32(length); 647362306a36Sopenharmony_ci rsp->DataRemaining = 0; 647462306a36Sopenharmony_ci rsp->Reserved2 = 0; 647562306a36Sopenharmony_ci err = ksmbd_iov_pin_rsp(work, (void *)rsp, 647662306a36Sopenharmony_ci offsetof(struct smb2_write_rsp, Buffer)); 647762306a36Sopenharmony_ciout: 647862306a36Sopenharmony_ci if (err) { 647962306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_HANDLE; 648062306a36Sopenharmony_ci smb2_set_err_rsp(work); 648162306a36Sopenharmony_ci } 648262306a36Sopenharmony_ci 648362306a36Sopenharmony_ci return err; 648462306a36Sopenharmony_ci} 648562306a36Sopenharmony_ci 648662306a36Sopenharmony_cistatic ssize_t smb2_write_rdma_channel(struct ksmbd_work *work, 648762306a36Sopenharmony_ci struct smb2_write_req *req, 648862306a36Sopenharmony_ci struct ksmbd_file *fp, 648962306a36Sopenharmony_ci loff_t offset, size_t length, bool sync) 649062306a36Sopenharmony_ci{ 649162306a36Sopenharmony_ci char *data_buf; 649262306a36Sopenharmony_ci int ret; 649362306a36Sopenharmony_ci ssize_t nbytes; 649462306a36Sopenharmony_ci 649562306a36Sopenharmony_ci data_buf = kvzalloc(length, GFP_KERNEL); 649662306a36Sopenharmony_ci if (!data_buf) 649762306a36Sopenharmony_ci return -ENOMEM; 649862306a36Sopenharmony_ci 649962306a36Sopenharmony_ci ret = ksmbd_conn_rdma_read(work->conn, data_buf, length, 650062306a36Sopenharmony_ci (struct smb2_buffer_desc_v1 *) 650162306a36Sopenharmony_ci ((char *)req + le16_to_cpu(req->WriteChannelInfoOffset)), 650262306a36Sopenharmony_ci le16_to_cpu(req->WriteChannelInfoLength)); 650362306a36Sopenharmony_ci if (ret < 0) { 650462306a36Sopenharmony_ci kvfree(data_buf); 650562306a36Sopenharmony_ci return ret; 650662306a36Sopenharmony_ci } 650762306a36Sopenharmony_ci 650862306a36Sopenharmony_ci ret = ksmbd_vfs_write(work, fp, data_buf, length, &offset, sync, &nbytes); 650962306a36Sopenharmony_ci kvfree(data_buf); 651062306a36Sopenharmony_ci if (ret < 0) 651162306a36Sopenharmony_ci return ret; 651262306a36Sopenharmony_ci 651362306a36Sopenharmony_ci return nbytes; 651462306a36Sopenharmony_ci} 651562306a36Sopenharmony_ci 651662306a36Sopenharmony_ci/** 651762306a36Sopenharmony_ci * smb2_write() - handler for smb2 write from file 651862306a36Sopenharmony_ci * @work: smb work containing write command buffer 651962306a36Sopenharmony_ci * 652062306a36Sopenharmony_ci * Return: 0 on success, otherwise error 652162306a36Sopenharmony_ci */ 652262306a36Sopenharmony_ciint smb2_write(struct ksmbd_work *work) 652362306a36Sopenharmony_ci{ 652462306a36Sopenharmony_ci struct smb2_write_req *req; 652562306a36Sopenharmony_ci struct smb2_write_rsp *rsp; 652662306a36Sopenharmony_ci struct ksmbd_file *fp = NULL; 652762306a36Sopenharmony_ci loff_t offset; 652862306a36Sopenharmony_ci size_t length; 652962306a36Sopenharmony_ci ssize_t nbytes; 653062306a36Sopenharmony_ci char *data_buf; 653162306a36Sopenharmony_ci bool writethrough = false, is_rdma_channel = false; 653262306a36Sopenharmony_ci int err = 0; 653362306a36Sopenharmony_ci unsigned int max_write_size = work->conn->vals->max_write_size; 653462306a36Sopenharmony_ci 653562306a36Sopenharmony_ci WORK_BUFFERS(work, req, rsp); 653662306a36Sopenharmony_ci 653762306a36Sopenharmony_ci if (test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_PIPE)) { 653862306a36Sopenharmony_ci ksmbd_debug(SMB, "IPC pipe write request\n"); 653962306a36Sopenharmony_ci return smb2_write_pipe(work); 654062306a36Sopenharmony_ci } 654162306a36Sopenharmony_ci 654262306a36Sopenharmony_ci offset = le64_to_cpu(req->Offset); 654362306a36Sopenharmony_ci length = le32_to_cpu(req->Length); 654462306a36Sopenharmony_ci 654562306a36Sopenharmony_ci if (req->Channel == SMB2_CHANNEL_RDMA_V1 || 654662306a36Sopenharmony_ci req->Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE) { 654762306a36Sopenharmony_ci is_rdma_channel = true; 654862306a36Sopenharmony_ci max_write_size = get_smbd_max_read_write_size(); 654962306a36Sopenharmony_ci length = le32_to_cpu(req->RemainingBytes); 655062306a36Sopenharmony_ci } 655162306a36Sopenharmony_ci 655262306a36Sopenharmony_ci if (is_rdma_channel == true) { 655362306a36Sopenharmony_ci unsigned int ch_offset = le16_to_cpu(req->WriteChannelInfoOffset); 655462306a36Sopenharmony_ci 655562306a36Sopenharmony_ci if (req->Length != 0 || req->DataOffset != 0 || 655662306a36Sopenharmony_ci ch_offset < offsetof(struct smb2_write_req, Buffer)) { 655762306a36Sopenharmony_ci err = -EINVAL; 655862306a36Sopenharmony_ci goto out; 655962306a36Sopenharmony_ci } 656062306a36Sopenharmony_ci err = smb2_set_remote_key_for_rdma(work, 656162306a36Sopenharmony_ci (struct smb2_buffer_desc_v1 *) 656262306a36Sopenharmony_ci ((char *)req + ch_offset), 656362306a36Sopenharmony_ci req->Channel, 656462306a36Sopenharmony_ci req->WriteChannelInfoLength); 656562306a36Sopenharmony_ci if (err) 656662306a36Sopenharmony_ci goto out; 656762306a36Sopenharmony_ci } 656862306a36Sopenharmony_ci 656962306a36Sopenharmony_ci if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { 657062306a36Sopenharmony_ci ksmbd_debug(SMB, "User does not have write permission\n"); 657162306a36Sopenharmony_ci err = -EACCES; 657262306a36Sopenharmony_ci goto out; 657362306a36Sopenharmony_ci } 657462306a36Sopenharmony_ci 657562306a36Sopenharmony_ci fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); 657662306a36Sopenharmony_ci if (!fp) { 657762306a36Sopenharmony_ci err = -ENOENT; 657862306a36Sopenharmony_ci goto out; 657962306a36Sopenharmony_ci } 658062306a36Sopenharmony_ci 658162306a36Sopenharmony_ci if (!(fp->daccess & (FILE_WRITE_DATA_LE | FILE_READ_ATTRIBUTES_LE))) { 658262306a36Sopenharmony_ci pr_err("Not permitted to write : 0x%x\n", fp->daccess); 658362306a36Sopenharmony_ci err = -EACCES; 658462306a36Sopenharmony_ci goto out; 658562306a36Sopenharmony_ci } 658662306a36Sopenharmony_ci 658762306a36Sopenharmony_ci if (length > max_write_size) { 658862306a36Sopenharmony_ci ksmbd_debug(SMB, "limiting write size to max size(%u)\n", 658962306a36Sopenharmony_ci max_write_size); 659062306a36Sopenharmony_ci err = -EINVAL; 659162306a36Sopenharmony_ci goto out; 659262306a36Sopenharmony_ci } 659362306a36Sopenharmony_ci 659462306a36Sopenharmony_ci ksmbd_debug(SMB, "flags %u\n", le32_to_cpu(req->Flags)); 659562306a36Sopenharmony_ci if (le32_to_cpu(req->Flags) & SMB2_WRITEFLAG_WRITE_THROUGH) 659662306a36Sopenharmony_ci writethrough = true; 659762306a36Sopenharmony_ci 659862306a36Sopenharmony_ci if (is_rdma_channel == false) { 659962306a36Sopenharmony_ci if (le16_to_cpu(req->DataOffset) < 660062306a36Sopenharmony_ci offsetof(struct smb2_write_req, Buffer)) { 660162306a36Sopenharmony_ci err = -EINVAL; 660262306a36Sopenharmony_ci goto out; 660362306a36Sopenharmony_ci } 660462306a36Sopenharmony_ci 660562306a36Sopenharmony_ci data_buf = (char *)(((char *)&req->hdr.ProtocolId) + 660662306a36Sopenharmony_ci le16_to_cpu(req->DataOffset)); 660762306a36Sopenharmony_ci 660862306a36Sopenharmony_ci ksmbd_debug(SMB, "filename %pD, offset %lld, len %zu\n", 660962306a36Sopenharmony_ci fp->filp, offset, length); 661062306a36Sopenharmony_ci err = ksmbd_vfs_write(work, fp, data_buf, length, &offset, 661162306a36Sopenharmony_ci writethrough, &nbytes); 661262306a36Sopenharmony_ci if (err < 0) 661362306a36Sopenharmony_ci goto out; 661462306a36Sopenharmony_ci } else { 661562306a36Sopenharmony_ci /* read data from the client using rdma channel, and 661662306a36Sopenharmony_ci * write the data. 661762306a36Sopenharmony_ci */ 661862306a36Sopenharmony_ci nbytes = smb2_write_rdma_channel(work, req, fp, offset, length, 661962306a36Sopenharmony_ci writethrough); 662062306a36Sopenharmony_ci if (nbytes < 0) { 662162306a36Sopenharmony_ci err = (int)nbytes; 662262306a36Sopenharmony_ci goto out; 662362306a36Sopenharmony_ci } 662462306a36Sopenharmony_ci } 662562306a36Sopenharmony_ci 662662306a36Sopenharmony_ci rsp->StructureSize = cpu_to_le16(17); 662762306a36Sopenharmony_ci rsp->DataOffset = 0; 662862306a36Sopenharmony_ci rsp->Reserved = 0; 662962306a36Sopenharmony_ci rsp->DataLength = cpu_to_le32(nbytes); 663062306a36Sopenharmony_ci rsp->DataRemaining = 0; 663162306a36Sopenharmony_ci rsp->Reserved2 = 0; 663262306a36Sopenharmony_ci err = ksmbd_iov_pin_rsp(work, rsp, offsetof(struct smb2_write_rsp, Buffer)); 663362306a36Sopenharmony_ci if (err) 663462306a36Sopenharmony_ci goto out; 663562306a36Sopenharmony_ci ksmbd_fd_put(work, fp); 663662306a36Sopenharmony_ci return 0; 663762306a36Sopenharmony_ci 663862306a36Sopenharmony_ciout: 663962306a36Sopenharmony_ci if (err == -EAGAIN) 664062306a36Sopenharmony_ci rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT; 664162306a36Sopenharmony_ci else if (err == -ENOSPC || err == -EFBIG) 664262306a36Sopenharmony_ci rsp->hdr.Status = STATUS_DISK_FULL; 664362306a36Sopenharmony_ci else if (err == -ENOENT) 664462306a36Sopenharmony_ci rsp->hdr.Status = STATUS_FILE_CLOSED; 664562306a36Sopenharmony_ci else if (err == -EACCES) 664662306a36Sopenharmony_ci rsp->hdr.Status = STATUS_ACCESS_DENIED; 664762306a36Sopenharmony_ci else if (err == -ESHARE) 664862306a36Sopenharmony_ci rsp->hdr.Status = STATUS_SHARING_VIOLATION; 664962306a36Sopenharmony_ci else if (err == -EINVAL) 665062306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_PARAMETER; 665162306a36Sopenharmony_ci else 665262306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_HANDLE; 665362306a36Sopenharmony_ci 665462306a36Sopenharmony_ci smb2_set_err_rsp(work); 665562306a36Sopenharmony_ci ksmbd_fd_put(work, fp); 665662306a36Sopenharmony_ci return err; 665762306a36Sopenharmony_ci} 665862306a36Sopenharmony_ci 665962306a36Sopenharmony_ci/** 666062306a36Sopenharmony_ci * smb2_flush() - handler for smb2 flush file - fsync 666162306a36Sopenharmony_ci * @work: smb work containing flush command buffer 666262306a36Sopenharmony_ci * 666362306a36Sopenharmony_ci * Return: 0 on success, otherwise error 666462306a36Sopenharmony_ci */ 666562306a36Sopenharmony_ciint smb2_flush(struct ksmbd_work *work) 666662306a36Sopenharmony_ci{ 666762306a36Sopenharmony_ci struct smb2_flush_req *req; 666862306a36Sopenharmony_ci struct smb2_flush_rsp *rsp; 666962306a36Sopenharmony_ci int err; 667062306a36Sopenharmony_ci 667162306a36Sopenharmony_ci WORK_BUFFERS(work, req, rsp); 667262306a36Sopenharmony_ci 667362306a36Sopenharmony_ci ksmbd_debug(SMB, "SMB2_FLUSH called for fid %llu\n", req->VolatileFileId); 667462306a36Sopenharmony_ci 667562306a36Sopenharmony_ci err = ksmbd_vfs_fsync(work, req->VolatileFileId, req->PersistentFileId); 667662306a36Sopenharmony_ci if (err) 667762306a36Sopenharmony_ci goto out; 667862306a36Sopenharmony_ci 667962306a36Sopenharmony_ci rsp->StructureSize = cpu_to_le16(4); 668062306a36Sopenharmony_ci rsp->Reserved = 0; 668162306a36Sopenharmony_ci return ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_flush_rsp)); 668262306a36Sopenharmony_ci 668362306a36Sopenharmony_ciout: 668462306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_HANDLE; 668562306a36Sopenharmony_ci smb2_set_err_rsp(work); 668662306a36Sopenharmony_ci return err; 668762306a36Sopenharmony_ci} 668862306a36Sopenharmony_ci 668962306a36Sopenharmony_ci/** 669062306a36Sopenharmony_ci * smb2_cancel() - handler for smb2 cancel command 669162306a36Sopenharmony_ci * @work: smb work containing cancel command buffer 669262306a36Sopenharmony_ci * 669362306a36Sopenharmony_ci * Return: 0 on success, otherwise error 669462306a36Sopenharmony_ci */ 669562306a36Sopenharmony_ciint smb2_cancel(struct ksmbd_work *work) 669662306a36Sopenharmony_ci{ 669762306a36Sopenharmony_ci struct ksmbd_conn *conn = work->conn; 669862306a36Sopenharmony_ci struct smb2_hdr *hdr = smb2_get_msg(work->request_buf); 669962306a36Sopenharmony_ci struct smb2_hdr *chdr; 670062306a36Sopenharmony_ci struct ksmbd_work *iter; 670162306a36Sopenharmony_ci struct list_head *command_list; 670262306a36Sopenharmony_ci 670362306a36Sopenharmony_ci if (work->next_smb2_rcv_hdr_off) 670462306a36Sopenharmony_ci hdr = ksmbd_resp_buf_next(work); 670562306a36Sopenharmony_ci 670662306a36Sopenharmony_ci ksmbd_debug(SMB, "smb2 cancel called on mid %llu, async flags 0x%x\n", 670762306a36Sopenharmony_ci hdr->MessageId, hdr->Flags); 670862306a36Sopenharmony_ci 670962306a36Sopenharmony_ci if (hdr->Flags & SMB2_FLAGS_ASYNC_COMMAND) { 671062306a36Sopenharmony_ci command_list = &conn->async_requests; 671162306a36Sopenharmony_ci 671262306a36Sopenharmony_ci spin_lock(&conn->request_lock); 671362306a36Sopenharmony_ci list_for_each_entry(iter, command_list, 671462306a36Sopenharmony_ci async_request_entry) { 671562306a36Sopenharmony_ci chdr = smb2_get_msg(iter->request_buf); 671662306a36Sopenharmony_ci 671762306a36Sopenharmony_ci if (iter->async_id != 671862306a36Sopenharmony_ci le64_to_cpu(hdr->Id.AsyncId)) 671962306a36Sopenharmony_ci continue; 672062306a36Sopenharmony_ci 672162306a36Sopenharmony_ci ksmbd_debug(SMB, 672262306a36Sopenharmony_ci "smb2 with AsyncId %llu cancelled command = 0x%x\n", 672362306a36Sopenharmony_ci le64_to_cpu(hdr->Id.AsyncId), 672462306a36Sopenharmony_ci le16_to_cpu(chdr->Command)); 672562306a36Sopenharmony_ci iter->state = KSMBD_WORK_CANCELLED; 672662306a36Sopenharmony_ci if (iter->cancel_fn) 672762306a36Sopenharmony_ci iter->cancel_fn(iter->cancel_argv); 672862306a36Sopenharmony_ci break; 672962306a36Sopenharmony_ci } 673062306a36Sopenharmony_ci spin_unlock(&conn->request_lock); 673162306a36Sopenharmony_ci } else { 673262306a36Sopenharmony_ci command_list = &conn->requests; 673362306a36Sopenharmony_ci 673462306a36Sopenharmony_ci spin_lock(&conn->request_lock); 673562306a36Sopenharmony_ci list_for_each_entry(iter, command_list, request_entry) { 673662306a36Sopenharmony_ci chdr = smb2_get_msg(iter->request_buf); 673762306a36Sopenharmony_ci 673862306a36Sopenharmony_ci if (chdr->MessageId != hdr->MessageId || 673962306a36Sopenharmony_ci iter == work) 674062306a36Sopenharmony_ci continue; 674162306a36Sopenharmony_ci 674262306a36Sopenharmony_ci ksmbd_debug(SMB, 674362306a36Sopenharmony_ci "smb2 with mid %llu cancelled command = 0x%x\n", 674462306a36Sopenharmony_ci le64_to_cpu(hdr->MessageId), 674562306a36Sopenharmony_ci le16_to_cpu(chdr->Command)); 674662306a36Sopenharmony_ci iter->state = KSMBD_WORK_CANCELLED; 674762306a36Sopenharmony_ci break; 674862306a36Sopenharmony_ci } 674962306a36Sopenharmony_ci spin_unlock(&conn->request_lock); 675062306a36Sopenharmony_ci } 675162306a36Sopenharmony_ci 675262306a36Sopenharmony_ci /* For SMB2_CANCEL command itself send no response*/ 675362306a36Sopenharmony_ci work->send_no_response = 1; 675462306a36Sopenharmony_ci return 0; 675562306a36Sopenharmony_ci} 675662306a36Sopenharmony_ci 675762306a36Sopenharmony_cistruct file_lock *smb_flock_init(struct file *f) 675862306a36Sopenharmony_ci{ 675962306a36Sopenharmony_ci struct file_lock *fl; 676062306a36Sopenharmony_ci 676162306a36Sopenharmony_ci fl = locks_alloc_lock(); 676262306a36Sopenharmony_ci if (!fl) 676362306a36Sopenharmony_ci goto out; 676462306a36Sopenharmony_ci 676562306a36Sopenharmony_ci locks_init_lock(fl); 676662306a36Sopenharmony_ci 676762306a36Sopenharmony_ci fl->fl_owner = f; 676862306a36Sopenharmony_ci fl->fl_pid = current->tgid; 676962306a36Sopenharmony_ci fl->fl_file = f; 677062306a36Sopenharmony_ci fl->fl_flags = FL_POSIX; 677162306a36Sopenharmony_ci fl->fl_ops = NULL; 677262306a36Sopenharmony_ci fl->fl_lmops = NULL; 677362306a36Sopenharmony_ci 677462306a36Sopenharmony_ciout: 677562306a36Sopenharmony_ci return fl; 677662306a36Sopenharmony_ci} 677762306a36Sopenharmony_ci 677862306a36Sopenharmony_cistatic int smb2_set_flock_flags(struct file_lock *flock, int flags) 677962306a36Sopenharmony_ci{ 678062306a36Sopenharmony_ci int cmd = -EINVAL; 678162306a36Sopenharmony_ci 678262306a36Sopenharmony_ci /* Checking for wrong flag combination during lock request*/ 678362306a36Sopenharmony_ci switch (flags) { 678462306a36Sopenharmony_ci case SMB2_LOCKFLAG_SHARED: 678562306a36Sopenharmony_ci ksmbd_debug(SMB, "received shared request\n"); 678662306a36Sopenharmony_ci cmd = F_SETLKW; 678762306a36Sopenharmony_ci flock->fl_type = F_RDLCK; 678862306a36Sopenharmony_ci flock->fl_flags |= FL_SLEEP; 678962306a36Sopenharmony_ci break; 679062306a36Sopenharmony_ci case SMB2_LOCKFLAG_EXCLUSIVE: 679162306a36Sopenharmony_ci ksmbd_debug(SMB, "received exclusive request\n"); 679262306a36Sopenharmony_ci cmd = F_SETLKW; 679362306a36Sopenharmony_ci flock->fl_type = F_WRLCK; 679462306a36Sopenharmony_ci flock->fl_flags |= FL_SLEEP; 679562306a36Sopenharmony_ci break; 679662306a36Sopenharmony_ci case SMB2_LOCKFLAG_SHARED | SMB2_LOCKFLAG_FAIL_IMMEDIATELY: 679762306a36Sopenharmony_ci ksmbd_debug(SMB, 679862306a36Sopenharmony_ci "received shared & fail immediately request\n"); 679962306a36Sopenharmony_ci cmd = F_SETLK; 680062306a36Sopenharmony_ci flock->fl_type = F_RDLCK; 680162306a36Sopenharmony_ci break; 680262306a36Sopenharmony_ci case SMB2_LOCKFLAG_EXCLUSIVE | SMB2_LOCKFLAG_FAIL_IMMEDIATELY: 680362306a36Sopenharmony_ci ksmbd_debug(SMB, 680462306a36Sopenharmony_ci "received exclusive & fail immediately request\n"); 680562306a36Sopenharmony_ci cmd = F_SETLK; 680662306a36Sopenharmony_ci flock->fl_type = F_WRLCK; 680762306a36Sopenharmony_ci break; 680862306a36Sopenharmony_ci case SMB2_LOCKFLAG_UNLOCK: 680962306a36Sopenharmony_ci ksmbd_debug(SMB, "received unlock request\n"); 681062306a36Sopenharmony_ci flock->fl_type = F_UNLCK; 681162306a36Sopenharmony_ci cmd = F_SETLK; 681262306a36Sopenharmony_ci break; 681362306a36Sopenharmony_ci } 681462306a36Sopenharmony_ci 681562306a36Sopenharmony_ci return cmd; 681662306a36Sopenharmony_ci} 681762306a36Sopenharmony_ci 681862306a36Sopenharmony_cistatic struct ksmbd_lock *smb2_lock_init(struct file_lock *flock, 681962306a36Sopenharmony_ci unsigned int cmd, int flags, 682062306a36Sopenharmony_ci struct list_head *lock_list) 682162306a36Sopenharmony_ci{ 682262306a36Sopenharmony_ci struct ksmbd_lock *lock; 682362306a36Sopenharmony_ci 682462306a36Sopenharmony_ci lock = kzalloc(sizeof(struct ksmbd_lock), GFP_KERNEL); 682562306a36Sopenharmony_ci if (!lock) 682662306a36Sopenharmony_ci return NULL; 682762306a36Sopenharmony_ci 682862306a36Sopenharmony_ci lock->cmd = cmd; 682962306a36Sopenharmony_ci lock->fl = flock; 683062306a36Sopenharmony_ci lock->start = flock->fl_start; 683162306a36Sopenharmony_ci lock->end = flock->fl_end; 683262306a36Sopenharmony_ci lock->flags = flags; 683362306a36Sopenharmony_ci if (lock->start == lock->end) 683462306a36Sopenharmony_ci lock->zero_len = 1; 683562306a36Sopenharmony_ci INIT_LIST_HEAD(&lock->clist); 683662306a36Sopenharmony_ci INIT_LIST_HEAD(&lock->flist); 683762306a36Sopenharmony_ci INIT_LIST_HEAD(&lock->llist); 683862306a36Sopenharmony_ci list_add_tail(&lock->llist, lock_list); 683962306a36Sopenharmony_ci 684062306a36Sopenharmony_ci return lock; 684162306a36Sopenharmony_ci} 684262306a36Sopenharmony_ci 684362306a36Sopenharmony_cistatic void smb2_remove_blocked_lock(void **argv) 684462306a36Sopenharmony_ci{ 684562306a36Sopenharmony_ci struct file_lock *flock = (struct file_lock *)argv[0]; 684662306a36Sopenharmony_ci 684762306a36Sopenharmony_ci ksmbd_vfs_posix_lock_unblock(flock); 684862306a36Sopenharmony_ci wake_up(&flock->fl_wait); 684962306a36Sopenharmony_ci} 685062306a36Sopenharmony_ci 685162306a36Sopenharmony_cistatic inline bool lock_defer_pending(struct file_lock *fl) 685262306a36Sopenharmony_ci{ 685362306a36Sopenharmony_ci /* check pending lock waiters */ 685462306a36Sopenharmony_ci return waitqueue_active(&fl->fl_wait); 685562306a36Sopenharmony_ci} 685662306a36Sopenharmony_ci 685762306a36Sopenharmony_ci/** 685862306a36Sopenharmony_ci * smb2_lock() - handler for smb2 file lock command 685962306a36Sopenharmony_ci * @work: smb work containing lock command buffer 686062306a36Sopenharmony_ci * 686162306a36Sopenharmony_ci * Return: 0 on success, otherwise error 686262306a36Sopenharmony_ci */ 686362306a36Sopenharmony_ciint smb2_lock(struct ksmbd_work *work) 686462306a36Sopenharmony_ci{ 686562306a36Sopenharmony_ci struct smb2_lock_req *req; 686662306a36Sopenharmony_ci struct smb2_lock_rsp *rsp; 686762306a36Sopenharmony_ci struct smb2_lock_element *lock_ele; 686862306a36Sopenharmony_ci struct ksmbd_file *fp = NULL; 686962306a36Sopenharmony_ci struct file_lock *flock = NULL; 687062306a36Sopenharmony_ci struct file *filp = NULL; 687162306a36Sopenharmony_ci int lock_count; 687262306a36Sopenharmony_ci int flags = 0; 687362306a36Sopenharmony_ci int cmd = 0; 687462306a36Sopenharmony_ci int err = -EIO, i, rc = 0; 687562306a36Sopenharmony_ci u64 lock_start, lock_length; 687662306a36Sopenharmony_ci struct ksmbd_lock *smb_lock = NULL, *cmp_lock, *tmp, *tmp2; 687762306a36Sopenharmony_ci struct ksmbd_conn *conn; 687862306a36Sopenharmony_ci int nolock = 0; 687962306a36Sopenharmony_ci LIST_HEAD(lock_list); 688062306a36Sopenharmony_ci LIST_HEAD(rollback_list); 688162306a36Sopenharmony_ci int prior_lock = 0; 688262306a36Sopenharmony_ci 688362306a36Sopenharmony_ci WORK_BUFFERS(work, req, rsp); 688462306a36Sopenharmony_ci 688562306a36Sopenharmony_ci ksmbd_debug(SMB, "Received lock request\n"); 688662306a36Sopenharmony_ci fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); 688762306a36Sopenharmony_ci if (!fp) { 688862306a36Sopenharmony_ci ksmbd_debug(SMB, "Invalid file id for lock : %llu\n", req->VolatileFileId); 688962306a36Sopenharmony_ci err = -ENOENT; 689062306a36Sopenharmony_ci goto out2; 689162306a36Sopenharmony_ci } 689262306a36Sopenharmony_ci 689362306a36Sopenharmony_ci filp = fp->filp; 689462306a36Sopenharmony_ci lock_count = le16_to_cpu(req->LockCount); 689562306a36Sopenharmony_ci lock_ele = req->locks; 689662306a36Sopenharmony_ci 689762306a36Sopenharmony_ci ksmbd_debug(SMB, "lock count is %d\n", lock_count); 689862306a36Sopenharmony_ci if (!lock_count) { 689962306a36Sopenharmony_ci err = -EINVAL; 690062306a36Sopenharmony_ci goto out2; 690162306a36Sopenharmony_ci } 690262306a36Sopenharmony_ci 690362306a36Sopenharmony_ci for (i = 0; i < lock_count; i++) { 690462306a36Sopenharmony_ci flags = le32_to_cpu(lock_ele[i].Flags); 690562306a36Sopenharmony_ci 690662306a36Sopenharmony_ci flock = smb_flock_init(filp); 690762306a36Sopenharmony_ci if (!flock) 690862306a36Sopenharmony_ci goto out; 690962306a36Sopenharmony_ci 691062306a36Sopenharmony_ci cmd = smb2_set_flock_flags(flock, flags); 691162306a36Sopenharmony_ci 691262306a36Sopenharmony_ci lock_start = le64_to_cpu(lock_ele[i].Offset); 691362306a36Sopenharmony_ci lock_length = le64_to_cpu(lock_ele[i].Length); 691462306a36Sopenharmony_ci if (lock_start > U64_MAX - lock_length) { 691562306a36Sopenharmony_ci pr_err("Invalid lock range requested\n"); 691662306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_LOCK_RANGE; 691762306a36Sopenharmony_ci locks_free_lock(flock); 691862306a36Sopenharmony_ci goto out; 691962306a36Sopenharmony_ci } 692062306a36Sopenharmony_ci 692162306a36Sopenharmony_ci if (lock_start > OFFSET_MAX) 692262306a36Sopenharmony_ci flock->fl_start = OFFSET_MAX; 692362306a36Sopenharmony_ci else 692462306a36Sopenharmony_ci flock->fl_start = lock_start; 692562306a36Sopenharmony_ci 692662306a36Sopenharmony_ci lock_length = le64_to_cpu(lock_ele[i].Length); 692762306a36Sopenharmony_ci if (lock_length > OFFSET_MAX - flock->fl_start) 692862306a36Sopenharmony_ci lock_length = OFFSET_MAX - flock->fl_start; 692962306a36Sopenharmony_ci 693062306a36Sopenharmony_ci flock->fl_end = flock->fl_start + lock_length; 693162306a36Sopenharmony_ci 693262306a36Sopenharmony_ci if (flock->fl_end < flock->fl_start) { 693362306a36Sopenharmony_ci ksmbd_debug(SMB, 693462306a36Sopenharmony_ci "the end offset(%llx) is smaller than the start offset(%llx)\n", 693562306a36Sopenharmony_ci flock->fl_end, flock->fl_start); 693662306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_LOCK_RANGE; 693762306a36Sopenharmony_ci locks_free_lock(flock); 693862306a36Sopenharmony_ci goto out; 693962306a36Sopenharmony_ci } 694062306a36Sopenharmony_ci 694162306a36Sopenharmony_ci /* Check conflict locks in one request */ 694262306a36Sopenharmony_ci list_for_each_entry(cmp_lock, &lock_list, llist) { 694362306a36Sopenharmony_ci if (cmp_lock->fl->fl_start <= flock->fl_start && 694462306a36Sopenharmony_ci cmp_lock->fl->fl_end >= flock->fl_end) { 694562306a36Sopenharmony_ci if (cmp_lock->fl->fl_type != F_UNLCK && 694662306a36Sopenharmony_ci flock->fl_type != F_UNLCK) { 694762306a36Sopenharmony_ci pr_err("conflict two locks in one request\n"); 694862306a36Sopenharmony_ci err = -EINVAL; 694962306a36Sopenharmony_ci locks_free_lock(flock); 695062306a36Sopenharmony_ci goto out; 695162306a36Sopenharmony_ci } 695262306a36Sopenharmony_ci } 695362306a36Sopenharmony_ci } 695462306a36Sopenharmony_ci 695562306a36Sopenharmony_ci smb_lock = smb2_lock_init(flock, cmd, flags, &lock_list); 695662306a36Sopenharmony_ci if (!smb_lock) { 695762306a36Sopenharmony_ci err = -EINVAL; 695862306a36Sopenharmony_ci locks_free_lock(flock); 695962306a36Sopenharmony_ci goto out; 696062306a36Sopenharmony_ci } 696162306a36Sopenharmony_ci } 696262306a36Sopenharmony_ci 696362306a36Sopenharmony_ci list_for_each_entry_safe(smb_lock, tmp, &lock_list, llist) { 696462306a36Sopenharmony_ci if (smb_lock->cmd < 0) { 696562306a36Sopenharmony_ci err = -EINVAL; 696662306a36Sopenharmony_ci goto out; 696762306a36Sopenharmony_ci } 696862306a36Sopenharmony_ci 696962306a36Sopenharmony_ci if (!(smb_lock->flags & SMB2_LOCKFLAG_MASK)) { 697062306a36Sopenharmony_ci err = -EINVAL; 697162306a36Sopenharmony_ci goto out; 697262306a36Sopenharmony_ci } 697362306a36Sopenharmony_ci 697462306a36Sopenharmony_ci if ((prior_lock & (SMB2_LOCKFLAG_EXCLUSIVE | SMB2_LOCKFLAG_SHARED) && 697562306a36Sopenharmony_ci smb_lock->flags & SMB2_LOCKFLAG_UNLOCK) || 697662306a36Sopenharmony_ci (prior_lock == SMB2_LOCKFLAG_UNLOCK && 697762306a36Sopenharmony_ci !(smb_lock->flags & SMB2_LOCKFLAG_UNLOCK))) { 697862306a36Sopenharmony_ci err = -EINVAL; 697962306a36Sopenharmony_ci goto out; 698062306a36Sopenharmony_ci } 698162306a36Sopenharmony_ci 698262306a36Sopenharmony_ci prior_lock = smb_lock->flags; 698362306a36Sopenharmony_ci 698462306a36Sopenharmony_ci if (!(smb_lock->flags & SMB2_LOCKFLAG_UNLOCK) && 698562306a36Sopenharmony_ci !(smb_lock->flags & SMB2_LOCKFLAG_FAIL_IMMEDIATELY)) 698662306a36Sopenharmony_ci goto no_check_cl; 698762306a36Sopenharmony_ci 698862306a36Sopenharmony_ci nolock = 1; 698962306a36Sopenharmony_ci /* check locks in connection list */ 699062306a36Sopenharmony_ci down_read(&conn_list_lock); 699162306a36Sopenharmony_ci list_for_each_entry(conn, &conn_list, conns_list) { 699262306a36Sopenharmony_ci spin_lock(&conn->llist_lock); 699362306a36Sopenharmony_ci list_for_each_entry_safe(cmp_lock, tmp2, &conn->lock_list, clist) { 699462306a36Sopenharmony_ci if (file_inode(cmp_lock->fl->fl_file) != 699562306a36Sopenharmony_ci file_inode(smb_lock->fl->fl_file)) 699662306a36Sopenharmony_ci continue; 699762306a36Sopenharmony_ci 699862306a36Sopenharmony_ci if (smb_lock->fl->fl_type == F_UNLCK) { 699962306a36Sopenharmony_ci if (cmp_lock->fl->fl_file == smb_lock->fl->fl_file && 700062306a36Sopenharmony_ci cmp_lock->start == smb_lock->start && 700162306a36Sopenharmony_ci cmp_lock->end == smb_lock->end && 700262306a36Sopenharmony_ci !lock_defer_pending(cmp_lock->fl)) { 700362306a36Sopenharmony_ci nolock = 0; 700462306a36Sopenharmony_ci list_del(&cmp_lock->flist); 700562306a36Sopenharmony_ci list_del(&cmp_lock->clist); 700662306a36Sopenharmony_ci spin_unlock(&conn->llist_lock); 700762306a36Sopenharmony_ci up_read(&conn_list_lock); 700862306a36Sopenharmony_ci 700962306a36Sopenharmony_ci locks_free_lock(cmp_lock->fl); 701062306a36Sopenharmony_ci kfree(cmp_lock); 701162306a36Sopenharmony_ci goto out_check_cl; 701262306a36Sopenharmony_ci } 701362306a36Sopenharmony_ci continue; 701462306a36Sopenharmony_ci } 701562306a36Sopenharmony_ci 701662306a36Sopenharmony_ci if (cmp_lock->fl->fl_file == smb_lock->fl->fl_file) { 701762306a36Sopenharmony_ci if (smb_lock->flags & SMB2_LOCKFLAG_SHARED) 701862306a36Sopenharmony_ci continue; 701962306a36Sopenharmony_ci } else { 702062306a36Sopenharmony_ci if (cmp_lock->flags & SMB2_LOCKFLAG_SHARED) 702162306a36Sopenharmony_ci continue; 702262306a36Sopenharmony_ci } 702362306a36Sopenharmony_ci 702462306a36Sopenharmony_ci /* check zero byte lock range */ 702562306a36Sopenharmony_ci if (cmp_lock->zero_len && !smb_lock->zero_len && 702662306a36Sopenharmony_ci cmp_lock->start > smb_lock->start && 702762306a36Sopenharmony_ci cmp_lock->start < smb_lock->end) { 702862306a36Sopenharmony_ci spin_unlock(&conn->llist_lock); 702962306a36Sopenharmony_ci up_read(&conn_list_lock); 703062306a36Sopenharmony_ci pr_err("previous lock conflict with zero byte lock range\n"); 703162306a36Sopenharmony_ci goto out; 703262306a36Sopenharmony_ci } 703362306a36Sopenharmony_ci 703462306a36Sopenharmony_ci if (smb_lock->zero_len && !cmp_lock->zero_len && 703562306a36Sopenharmony_ci smb_lock->start > cmp_lock->start && 703662306a36Sopenharmony_ci smb_lock->start < cmp_lock->end) { 703762306a36Sopenharmony_ci spin_unlock(&conn->llist_lock); 703862306a36Sopenharmony_ci up_read(&conn_list_lock); 703962306a36Sopenharmony_ci pr_err("current lock conflict with zero byte lock range\n"); 704062306a36Sopenharmony_ci goto out; 704162306a36Sopenharmony_ci } 704262306a36Sopenharmony_ci 704362306a36Sopenharmony_ci if (((cmp_lock->start <= smb_lock->start && 704462306a36Sopenharmony_ci cmp_lock->end > smb_lock->start) || 704562306a36Sopenharmony_ci (cmp_lock->start < smb_lock->end && 704662306a36Sopenharmony_ci cmp_lock->end >= smb_lock->end)) && 704762306a36Sopenharmony_ci !cmp_lock->zero_len && !smb_lock->zero_len) { 704862306a36Sopenharmony_ci spin_unlock(&conn->llist_lock); 704962306a36Sopenharmony_ci up_read(&conn_list_lock); 705062306a36Sopenharmony_ci pr_err("Not allow lock operation on exclusive lock range\n"); 705162306a36Sopenharmony_ci goto out; 705262306a36Sopenharmony_ci } 705362306a36Sopenharmony_ci } 705462306a36Sopenharmony_ci spin_unlock(&conn->llist_lock); 705562306a36Sopenharmony_ci } 705662306a36Sopenharmony_ci up_read(&conn_list_lock); 705762306a36Sopenharmony_ciout_check_cl: 705862306a36Sopenharmony_ci if (smb_lock->fl->fl_type == F_UNLCK && nolock) { 705962306a36Sopenharmony_ci pr_err("Try to unlock nolocked range\n"); 706062306a36Sopenharmony_ci rsp->hdr.Status = STATUS_RANGE_NOT_LOCKED; 706162306a36Sopenharmony_ci goto out; 706262306a36Sopenharmony_ci } 706362306a36Sopenharmony_ci 706462306a36Sopenharmony_cino_check_cl: 706562306a36Sopenharmony_ci if (smb_lock->zero_len) { 706662306a36Sopenharmony_ci err = 0; 706762306a36Sopenharmony_ci goto skip; 706862306a36Sopenharmony_ci } 706962306a36Sopenharmony_ci 707062306a36Sopenharmony_ci flock = smb_lock->fl; 707162306a36Sopenharmony_ci list_del(&smb_lock->llist); 707262306a36Sopenharmony_ciretry: 707362306a36Sopenharmony_ci rc = vfs_lock_file(filp, smb_lock->cmd, flock, NULL); 707462306a36Sopenharmony_ciskip: 707562306a36Sopenharmony_ci if (flags & SMB2_LOCKFLAG_UNLOCK) { 707662306a36Sopenharmony_ci if (!rc) { 707762306a36Sopenharmony_ci ksmbd_debug(SMB, "File unlocked\n"); 707862306a36Sopenharmony_ci } else if (rc == -ENOENT) { 707962306a36Sopenharmony_ci rsp->hdr.Status = STATUS_NOT_LOCKED; 708062306a36Sopenharmony_ci goto out; 708162306a36Sopenharmony_ci } 708262306a36Sopenharmony_ci locks_free_lock(flock); 708362306a36Sopenharmony_ci kfree(smb_lock); 708462306a36Sopenharmony_ci } else { 708562306a36Sopenharmony_ci if (rc == FILE_LOCK_DEFERRED) { 708662306a36Sopenharmony_ci void **argv; 708762306a36Sopenharmony_ci 708862306a36Sopenharmony_ci ksmbd_debug(SMB, 708962306a36Sopenharmony_ci "would have to wait for getting lock\n"); 709062306a36Sopenharmony_ci list_add(&smb_lock->llist, &rollback_list); 709162306a36Sopenharmony_ci 709262306a36Sopenharmony_ci argv = kmalloc(sizeof(void *), GFP_KERNEL); 709362306a36Sopenharmony_ci if (!argv) { 709462306a36Sopenharmony_ci err = -ENOMEM; 709562306a36Sopenharmony_ci goto out; 709662306a36Sopenharmony_ci } 709762306a36Sopenharmony_ci argv[0] = flock; 709862306a36Sopenharmony_ci 709962306a36Sopenharmony_ci rc = setup_async_work(work, 710062306a36Sopenharmony_ci smb2_remove_blocked_lock, 710162306a36Sopenharmony_ci argv); 710262306a36Sopenharmony_ci if (rc) { 710362306a36Sopenharmony_ci kfree(argv); 710462306a36Sopenharmony_ci err = -ENOMEM; 710562306a36Sopenharmony_ci goto out; 710662306a36Sopenharmony_ci } 710762306a36Sopenharmony_ci spin_lock(&fp->f_lock); 710862306a36Sopenharmony_ci list_add(&work->fp_entry, &fp->blocked_works); 710962306a36Sopenharmony_ci spin_unlock(&fp->f_lock); 711062306a36Sopenharmony_ci 711162306a36Sopenharmony_ci smb2_send_interim_resp(work, STATUS_PENDING); 711262306a36Sopenharmony_ci 711362306a36Sopenharmony_ci ksmbd_vfs_posix_lock_wait(flock); 711462306a36Sopenharmony_ci 711562306a36Sopenharmony_ci spin_lock(&fp->f_lock); 711662306a36Sopenharmony_ci list_del(&work->fp_entry); 711762306a36Sopenharmony_ci spin_unlock(&fp->f_lock); 711862306a36Sopenharmony_ci 711962306a36Sopenharmony_ci if (work->state != KSMBD_WORK_ACTIVE) { 712062306a36Sopenharmony_ci list_del(&smb_lock->llist); 712162306a36Sopenharmony_ci locks_free_lock(flock); 712262306a36Sopenharmony_ci 712362306a36Sopenharmony_ci if (work->state == KSMBD_WORK_CANCELLED) { 712462306a36Sopenharmony_ci rsp->hdr.Status = 712562306a36Sopenharmony_ci STATUS_CANCELLED; 712662306a36Sopenharmony_ci kfree(smb_lock); 712762306a36Sopenharmony_ci smb2_send_interim_resp(work, 712862306a36Sopenharmony_ci STATUS_CANCELLED); 712962306a36Sopenharmony_ci work->send_no_response = 1; 713062306a36Sopenharmony_ci goto out; 713162306a36Sopenharmony_ci } 713262306a36Sopenharmony_ci 713362306a36Sopenharmony_ci rsp->hdr.Status = 713462306a36Sopenharmony_ci STATUS_RANGE_NOT_LOCKED; 713562306a36Sopenharmony_ci kfree(smb_lock); 713662306a36Sopenharmony_ci goto out2; 713762306a36Sopenharmony_ci } 713862306a36Sopenharmony_ci 713962306a36Sopenharmony_ci list_del(&smb_lock->llist); 714062306a36Sopenharmony_ci release_async_work(work); 714162306a36Sopenharmony_ci goto retry; 714262306a36Sopenharmony_ci } else if (!rc) { 714362306a36Sopenharmony_ci list_add(&smb_lock->llist, &rollback_list); 714462306a36Sopenharmony_ci spin_lock(&work->conn->llist_lock); 714562306a36Sopenharmony_ci list_add_tail(&smb_lock->clist, 714662306a36Sopenharmony_ci &work->conn->lock_list); 714762306a36Sopenharmony_ci list_add_tail(&smb_lock->flist, 714862306a36Sopenharmony_ci &fp->lock_list); 714962306a36Sopenharmony_ci spin_unlock(&work->conn->llist_lock); 715062306a36Sopenharmony_ci ksmbd_debug(SMB, "successful in taking lock\n"); 715162306a36Sopenharmony_ci } else { 715262306a36Sopenharmony_ci goto out; 715362306a36Sopenharmony_ci } 715462306a36Sopenharmony_ci } 715562306a36Sopenharmony_ci } 715662306a36Sopenharmony_ci 715762306a36Sopenharmony_ci if (atomic_read(&fp->f_ci->op_count) > 1) 715862306a36Sopenharmony_ci smb_break_all_oplock(work, fp); 715962306a36Sopenharmony_ci 716062306a36Sopenharmony_ci rsp->StructureSize = cpu_to_le16(4); 716162306a36Sopenharmony_ci ksmbd_debug(SMB, "successful in taking lock\n"); 716262306a36Sopenharmony_ci rsp->hdr.Status = STATUS_SUCCESS; 716362306a36Sopenharmony_ci rsp->Reserved = 0; 716462306a36Sopenharmony_ci err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_lock_rsp)); 716562306a36Sopenharmony_ci if (err) 716662306a36Sopenharmony_ci goto out; 716762306a36Sopenharmony_ci 716862306a36Sopenharmony_ci ksmbd_fd_put(work, fp); 716962306a36Sopenharmony_ci return 0; 717062306a36Sopenharmony_ci 717162306a36Sopenharmony_ciout: 717262306a36Sopenharmony_ci list_for_each_entry_safe(smb_lock, tmp, &lock_list, llist) { 717362306a36Sopenharmony_ci locks_free_lock(smb_lock->fl); 717462306a36Sopenharmony_ci list_del(&smb_lock->llist); 717562306a36Sopenharmony_ci kfree(smb_lock); 717662306a36Sopenharmony_ci } 717762306a36Sopenharmony_ci 717862306a36Sopenharmony_ci list_for_each_entry_safe(smb_lock, tmp, &rollback_list, llist) { 717962306a36Sopenharmony_ci struct file_lock *rlock = NULL; 718062306a36Sopenharmony_ci 718162306a36Sopenharmony_ci rlock = smb_flock_init(filp); 718262306a36Sopenharmony_ci rlock->fl_type = F_UNLCK; 718362306a36Sopenharmony_ci rlock->fl_start = smb_lock->start; 718462306a36Sopenharmony_ci rlock->fl_end = smb_lock->end; 718562306a36Sopenharmony_ci 718662306a36Sopenharmony_ci rc = vfs_lock_file(filp, F_SETLK, rlock, NULL); 718762306a36Sopenharmony_ci if (rc) 718862306a36Sopenharmony_ci pr_err("rollback unlock fail : %d\n", rc); 718962306a36Sopenharmony_ci 719062306a36Sopenharmony_ci list_del(&smb_lock->llist); 719162306a36Sopenharmony_ci spin_lock(&work->conn->llist_lock); 719262306a36Sopenharmony_ci if (!list_empty(&smb_lock->flist)) 719362306a36Sopenharmony_ci list_del(&smb_lock->flist); 719462306a36Sopenharmony_ci list_del(&smb_lock->clist); 719562306a36Sopenharmony_ci spin_unlock(&work->conn->llist_lock); 719662306a36Sopenharmony_ci 719762306a36Sopenharmony_ci locks_free_lock(smb_lock->fl); 719862306a36Sopenharmony_ci locks_free_lock(rlock); 719962306a36Sopenharmony_ci kfree(smb_lock); 720062306a36Sopenharmony_ci } 720162306a36Sopenharmony_ciout2: 720262306a36Sopenharmony_ci ksmbd_debug(SMB, "failed in taking lock(flags : %x), err : %d\n", flags, err); 720362306a36Sopenharmony_ci 720462306a36Sopenharmony_ci if (!rsp->hdr.Status) { 720562306a36Sopenharmony_ci if (err == -EINVAL) 720662306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_PARAMETER; 720762306a36Sopenharmony_ci else if (err == -ENOMEM) 720862306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; 720962306a36Sopenharmony_ci else if (err == -ENOENT) 721062306a36Sopenharmony_ci rsp->hdr.Status = STATUS_FILE_CLOSED; 721162306a36Sopenharmony_ci else 721262306a36Sopenharmony_ci rsp->hdr.Status = STATUS_LOCK_NOT_GRANTED; 721362306a36Sopenharmony_ci } 721462306a36Sopenharmony_ci 721562306a36Sopenharmony_ci smb2_set_err_rsp(work); 721662306a36Sopenharmony_ci ksmbd_fd_put(work, fp); 721762306a36Sopenharmony_ci return err; 721862306a36Sopenharmony_ci} 721962306a36Sopenharmony_ci 722062306a36Sopenharmony_cistatic int fsctl_copychunk(struct ksmbd_work *work, 722162306a36Sopenharmony_ci struct copychunk_ioctl_req *ci_req, 722262306a36Sopenharmony_ci unsigned int cnt_code, 722362306a36Sopenharmony_ci unsigned int input_count, 722462306a36Sopenharmony_ci unsigned long long volatile_id, 722562306a36Sopenharmony_ci unsigned long long persistent_id, 722662306a36Sopenharmony_ci struct smb2_ioctl_rsp *rsp) 722762306a36Sopenharmony_ci{ 722862306a36Sopenharmony_ci struct copychunk_ioctl_rsp *ci_rsp; 722962306a36Sopenharmony_ci struct ksmbd_file *src_fp = NULL, *dst_fp = NULL; 723062306a36Sopenharmony_ci struct srv_copychunk *chunks; 723162306a36Sopenharmony_ci unsigned int i, chunk_count, chunk_count_written = 0; 723262306a36Sopenharmony_ci unsigned int chunk_size_written = 0; 723362306a36Sopenharmony_ci loff_t total_size_written = 0; 723462306a36Sopenharmony_ci int ret = 0; 723562306a36Sopenharmony_ci 723662306a36Sopenharmony_ci ci_rsp = (struct copychunk_ioctl_rsp *)&rsp->Buffer[0]; 723762306a36Sopenharmony_ci 723862306a36Sopenharmony_ci rsp->VolatileFileId = volatile_id; 723962306a36Sopenharmony_ci rsp->PersistentFileId = persistent_id; 724062306a36Sopenharmony_ci ci_rsp->ChunksWritten = 724162306a36Sopenharmony_ci cpu_to_le32(ksmbd_server_side_copy_max_chunk_count()); 724262306a36Sopenharmony_ci ci_rsp->ChunkBytesWritten = 724362306a36Sopenharmony_ci cpu_to_le32(ksmbd_server_side_copy_max_chunk_size()); 724462306a36Sopenharmony_ci ci_rsp->TotalBytesWritten = 724562306a36Sopenharmony_ci cpu_to_le32(ksmbd_server_side_copy_max_total_size()); 724662306a36Sopenharmony_ci 724762306a36Sopenharmony_ci chunks = (struct srv_copychunk *)&ci_req->Chunks[0]; 724862306a36Sopenharmony_ci chunk_count = le32_to_cpu(ci_req->ChunkCount); 724962306a36Sopenharmony_ci if (chunk_count == 0) 725062306a36Sopenharmony_ci goto out; 725162306a36Sopenharmony_ci total_size_written = 0; 725262306a36Sopenharmony_ci 725362306a36Sopenharmony_ci /* verify the SRV_COPYCHUNK_COPY packet */ 725462306a36Sopenharmony_ci if (chunk_count > ksmbd_server_side_copy_max_chunk_count() || 725562306a36Sopenharmony_ci input_count < offsetof(struct copychunk_ioctl_req, Chunks) + 725662306a36Sopenharmony_ci chunk_count * sizeof(struct srv_copychunk)) { 725762306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_PARAMETER; 725862306a36Sopenharmony_ci return -EINVAL; 725962306a36Sopenharmony_ci } 726062306a36Sopenharmony_ci 726162306a36Sopenharmony_ci for (i = 0; i < chunk_count; i++) { 726262306a36Sopenharmony_ci if (le32_to_cpu(chunks[i].Length) == 0 || 726362306a36Sopenharmony_ci le32_to_cpu(chunks[i].Length) > ksmbd_server_side_copy_max_chunk_size()) 726462306a36Sopenharmony_ci break; 726562306a36Sopenharmony_ci total_size_written += le32_to_cpu(chunks[i].Length); 726662306a36Sopenharmony_ci } 726762306a36Sopenharmony_ci 726862306a36Sopenharmony_ci if (i < chunk_count || 726962306a36Sopenharmony_ci total_size_written > ksmbd_server_side_copy_max_total_size()) { 727062306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_PARAMETER; 727162306a36Sopenharmony_ci return -EINVAL; 727262306a36Sopenharmony_ci } 727362306a36Sopenharmony_ci 727462306a36Sopenharmony_ci src_fp = ksmbd_lookup_foreign_fd(work, 727562306a36Sopenharmony_ci le64_to_cpu(ci_req->ResumeKey[0])); 727662306a36Sopenharmony_ci dst_fp = ksmbd_lookup_fd_slow(work, volatile_id, persistent_id); 727762306a36Sopenharmony_ci ret = -EINVAL; 727862306a36Sopenharmony_ci if (!src_fp || 727962306a36Sopenharmony_ci src_fp->persistent_id != le64_to_cpu(ci_req->ResumeKey[1])) { 728062306a36Sopenharmony_ci rsp->hdr.Status = STATUS_OBJECT_NAME_NOT_FOUND; 728162306a36Sopenharmony_ci goto out; 728262306a36Sopenharmony_ci } 728362306a36Sopenharmony_ci 728462306a36Sopenharmony_ci if (!dst_fp) { 728562306a36Sopenharmony_ci rsp->hdr.Status = STATUS_FILE_CLOSED; 728662306a36Sopenharmony_ci goto out; 728762306a36Sopenharmony_ci } 728862306a36Sopenharmony_ci 728962306a36Sopenharmony_ci /* 729062306a36Sopenharmony_ci * FILE_READ_DATA should only be included in 729162306a36Sopenharmony_ci * the FSCTL_COPYCHUNK case 729262306a36Sopenharmony_ci */ 729362306a36Sopenharmony_ci if (cnt_code == FSCTL_COPYCHUNK && 729462306a36Sopenharmony_ci !(dst_fp->daccess & (FILE_READ_DATA_LE | FILE_GENERIC_READ_LE))) { 729562306a36Sopenharmony_ci rsp->hdr.Status = STATUS_ACCESS_DENIED; 729662306a36Sopenharmony_ci goto out; 729762306a36Sopenharmony_ci } 729862306a36Sopenharmony_ci 729962306a36Sopenharmony_ci ret = ksmbd_vfs_copy_file_ranges(work, src_fp, dst_fp, 730062306a36Sopenharmony_ci chunks, chunk_count, 730162306a36Sopenharmony_ci &chunk_count_written, 730262306a36Sopenharmony_ci &chunk_size_written, 730362306a36Sopenharmony_ci &total_size_written); 730462306a36Sopenharmony_ci if (ret < 0) { 730562306a36Sopenharmony_ci if (ret == -EACCES) 730662306a36Sopenharmony_ci rsp->hdr.Status = STATUS_ACCESS_DENIED; 730762306a36Sopenharmony_ci if (ret == -EAGAIN) 730862306a36Sopenharmony_ci rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT; 730962306a36Sopenharmony_ci else if (ret == -EBADF) 731062306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_HANDLE; 731162306a36Sopenharmony_ci else if (ret == -EFBIG || ret == -ENOSPC) 731262306a36Sopenharmony_ci rsp->hdr.Status = STATUS_DISK_FULL; 731362306a36Sopenharmony_ci else if (ret == -EINVAL) 731462306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_PARAMETER; 731562306a36Sopenharmony_ci else if (ret == -EISDIR) 731662306a36Sopenharmony_ci rsp->hdr.Status = STATUS_FILE_IS_A_DIRECTORY; 731762306a36Sopenharmony_ci else if (ret == -E2BIG) 731862306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_VIEW_SIZE; 731962306a36Sopenharmony_ci else 732062306a36Sopenharmony_ci rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; 732162306a36Sopenharmony_ci } 732262306a36Sopenharmony_ci 732362306a36Sopenharmony_ci ci_rsp->ChunksWritten = cpu_to_le32(chunk_count_written); 732462306a36Sopenharmony_ci ci_rsp->ChunkBytesWritten = cpu_to_le32(chunk_size_written); 732562306a36Sopenharmony_ci ci_rsp->TotalBytesWritten = cpu_to_le32(total_size_written); 732662306a36Sopenharmony_ciout: 732762306a36Sopenharmony_ci ksmbd_fd_put(work, src_fp); 732862306a36Sopenharmony_ci ksmbd_fd_put(work, dst_fp); 732962306a36Sopenharmony_ci return ret; 733062306a36Sopenharmony_ci} 733162306a36Sopenharmony_ci 733262306a36Sopenharmony_cistatic __be32 idev_ipv4_address(struct in_device *idev) 733362306a36Sopenharmony_ci{ 733462306a36Sopenharmony_ci __be32 addr = 0; 733562306a36Sopenharmony_ci 733662306a36Sopenharmony_ci struct in_ifaddr *ifa; 733762306a36Sopenharmony_ci 733862306a36Sopenharmony_ci rcu_read_lock(); 733962306a36Sopenharmony_ci in_dev_for_each_ifa_rcu(ifa, idev) { 734062306a36Sopenharmony_ci if (ifa->ifa_flags & IFA_F_SECONDARY) 734162306a36Sopenharmony_ci continue; 734262306a36Sopenharmony_ci 734362306a36Sopenharmony_ci addr = ifa->ifa_address; 734462306a36Sopenharmony_ci break; 734562306a36Sopenharmony_ci } 734662306a36Sopenharmony_ci rcu_read_unlock(); 734762306a36Sopenharmony_ci return addr; 734862306a36Sopenharmony_ci} 734962306a36Sopenharmony_ci 735062306a36Sopenharmony_cistatic int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, 735162306a36Sopenharmony_ci struct smb2_ioctl_rsp *rsp, 735262306a36Sopenharmony_ci unsigned int out_buf_len) 735362306a36Sopenharmony_ci{ 735462306a36Sopenharmony_ci struct network_interface_info_ioctl_rsp *nii_rsp = NULL; 735562306a36Sopenharmony_ci int nbytes = 0; 735662306a36Sopenharmony_ci struct net_device *netdev; 735762306a36Sopenharmony_ci struct sockaddr_storage_rsp *sockaddr_storage; 735862306a36Sopenharmony_ci unsigned int flags; 735962306a36Sopenharmony_ci unsigned long long speed; 736062306a36Sopenharmony_ci 736162306a36Sopenharmony_ci rtnl_lock(); 736262306a36Sopenharmony_ci for_each_netdev(&init_net, netdev) { 736362306a36Sopenharmony_ci bool ipv4_set = false; 736462306a36Sopenharmony_ci 736562306a36Sopenharmony_ci if (netdev->type == ARPHRD_LOOPBACK) 736662306a36Sopenharmony_ci continue; 736762306a36Sopenharmony_ci 736862306a36Sopenharmony_ci flags = dev_get_flags(netdev); 736962306a36Sopenharmony_ci if (!(flags & IFF_RUNNING)) 737062306a36Sopenharmony_ci continue; 737162306a36Sopenharmony_ciipv6_retry: 737262306a36Sopenharmony_ci if (out_buf_len < 737362306a36Sopenharmony_ci nbytes + sizeof(struct network_interface_info_ioctl_rsp)) { 737462306a36Sopenharmony_ci rtnl_unlock(); 737562306a36Sopenharmony_ci return -ENOSPC; 737662306a36Sopenharmony_ci } 737762306a36Sopenharmony_ci 737862306a36Sopenharmony_ci nii_rsp = (struct network_interface_info_ioctl_rsp *) 737962306a36Sopenharmony_ci &rsp->Buffer[nbytes]; 738062306a36Sopenharmony_ci nii_rsp->IfIndex = cpu_to_le32(netdev->ifindex); 738162306a36Sopenharmony_ci 738262306a36Sopenharmony_ci nii_rsp->Capability = 0; 738362306a36Sopenharmony_ci if (netdev->real_num_tx_queues > 1) 738462306a36Sopenharmony_ci nii_rsp->Capability |= cpu_to_le32(RSS_CAPABLE); 738562306a36Sopenharmony_ci if (ksmbd_rdma_capable_netdev(netdev)) 738662306a36Sopenharmony_ci nii_rsp->Capability |= cpu_to_le32(RDMA_CAPABLE); 738762306a36Sopenharmony_ci 738862306a36Sopenharmony_ci nii_rsp->Next = cpu_to_le32(152); 738962306a36Sopenharmony_ci nii_rsp->Reserved = 0; 739062306a36Sopenharmony_ci 739162306a36Sopenharmony_ci if (netdev->ethtool_ops->get_link_ksettings) { 739262306a36Sopenharmony_ci struct ethtool_link_ksettings cmd; 739362306a36Sopenharmony_ci 739462306a36Sopenharmony_ci netdev->ethtool_ops->get_link_ksettings(netdev, &cmd); 739562306a36Sopenharmony_ci speed = cmd.base.speed; 739662306a36Sopenharmony_ci } else { 739762306a36Sopenharmony_ci ksmbd_debug(SMB, "%s %s\n", netdev->name, 739862306a36Sopenharmony_ci "speed is unknown, defaulting to 1Gb/sec"); 739962306a36Sopenharmony_ci speed = SPEED_1000; 740062306a36Sopenharmony_ci } 740162306a36Sopenharmony_ci 740262306a36Sopenharmony_ci speed *= 1000000; 740362306a36Sopenharmony_ci nii_rsp->LinkSpeed = cpu_to_le64(speed); 740462306a36Sopenharmony_ci 740562306a36Sopenharmony_ci sockaddr_storage = (struct sockaddr_storage_rsp *) 740662306a36Sopenharmony_ci nii_rsp->SockAddr_Storage; 740762306a36Sopenharmony_ci memset(sockaddr_storage, 0, 128); 740862306a36Sopenharmony_ci 740962306a36Sopenharmony_ci if (!ipv4_set) { 741062306a36Sopenharmony_ci struct in_device *idev; 741162306a36Sopenharmony_ci 741262306a36Sopenharmony_ci sockaddr_storage->Family = cpu_to_le16(INTERNETWORK); 741362306a36Sopenharmony_ci sockaddr_storage->addr4.Port = 0; 741462306a36Sopenharmony_ci 741562306a36Sopenharmony_ci idev = __in_dev_get_rtnl(netdev); 741662306a36Sopenharmony_ci if (!idev) 741762306a36Sopenharmony_ci continue; 741862306a36Sopenharmony_ci sockaddr_storage->addr4.IPv4address = 741962306a36Sopenharmony_ci idev_ipv4_address(idev); 742062306a36Sopenharmony_ci nbytes += sizeof(struct network_interface_info_ioctl_rsp); 742162306a36Sopenharmony_ci ipv4_set = true; 742262306a36Sopenharmony_ci goto ipv6_retry; 742362306a36Sopenharmony_ci } else { 742462306a36Sopenharmony_ci struct inet6_dev *idev6; 742562306a36Sopenharmony_ci struct inet6_ifaddr *ifa; 742662306a36Sopenharmony_ci __u8 *ipv6_addr = sockaddr_storage->addr6.IPv6address; 742762306a36Sopenharmony_ci 742862306a36Sopenharmony_ci sockaddr_storage->Family = cpu_to_le16(INTERNETWORKV6); 742962306a36Sopenharmony_ci sockaddr_storage->addr6.Port = 0; 743062306a36Sopenharmony_ci sockaddr_storage->addr6.FlowInfo = 0; 743162306a36Sopenharmony_ci 743262306a36Sopenharmony_ci idev6 = __in6_dev_get(netdev); 743362306a36Sopenharmony_ci if (!idev6) 743462306a36Sopenharmony_ci continue; 743562306a36Sopenharmony_ci 743662306a36Sopenharmony_ci list_for_each_entry(ifa, &idev6->addr_list, if_list) { 743762306a36Sopenharmony_ci if (ifa->flags & (IFA_F_TENTATIVE | 743862306a36Sopenharmony_ci IFA_F_DEPRECATED)) 743962306a36Sopenharmony_ci continue; 744062306a36Sopenharmony_ci memcpy(ipv6_addr, ifa->addr.s6_addr, 16); 744162306a36Sopenharmony_ci break; 744262306a36Sopenharmony_ci } 744362306a36Sopenharmony_ci sockaddr_storage->addr6.ScopeId = 0; 744462306a36Sopenharmony_ci nbytes += sizeof(struct network_interface_info_ioctl_rsp); 744562306a36Sopenharmony_ci } 744662306a36Sopenharmony_ci } 744762306a36Sopenharmony_ci rtnl_unlock(); 744862306a36Sopenharmony_ci 744962306a36Sopenharmony_ci /* zero if this is last one */ 745062306a36Sopenharmony_ci if (nii_rsp) 745162306a36Sopenharmony_ci nii_rsp->Next = 0; 745262306a36Sopenharmony_ci 745362306a36Sopenharmony_ci rsp->PersistentFileId = SMB2_NO_FID; 745462306a36Sopenharmony_ci rsp->VolatileFileId = SMB2_NO_FID; 745562306a36Sopenharmony_ci return nbytes; 745662306a36Sopenharmony_ci} 745762306a36Sopenharmony_ci 745862306a36Sopenharmony_cistatic int fsctl_validate_negotiate_info(struct ksmbd_conn *conn, 745962306a36Sopenharmony_ci struct validate_negotiate_info_req *neg_req, 746062306a36Sopenharmony_ci struct validate_negotiate_info_rsp *neg_rsp, 746162306a36Sopenharmony_ci unsigned int in_buf_len) 746262306a36Sopenharmony_ci{ 746362306a36Sopenharmony_ci int ret = 0; 746462306a36Sopenharmony_ci int dialect; 746562306a36Sopenharmony_ci 746662306a36Sopenharmony_ci if (in_buf_len < offsetof(struct validate_negotiate_info_req, Dialects) + 746762306a36Sopenharmony_ci le16_to_cpu(neg_req->DialectCount) * sizeof(__le16)) 746862306a36Sopenharmony_ci return -EINVAL; 746962306a36Sopenharmony_ci 747062306a36Sopenharmony_ci dialect = ksmbd_lookup_dialect_by_id(neg_req->Dialects, 747162306a36Sopenharmony_ci neg_req->DialectCount); 747262306a36Sopenharmony_ci if (dialect == BAD_PROT_ID || dialect != conn->dialect) { 747362306a36Sopenharmony_ci ret = -EINVAL; 747462306a36Sopenharmony_ci goto err_out; 747562306a36Sopenharmony_ci } 747662306a36Sopenharmony_ci 747762306a36Sopenharmony_ci if (strncmp(neg_req->Guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE)) { 747862306a36Sopenharmony_ci ret = -EINVAL; 747962306a36Sopenharmony_ci goto err_out; 748062306a36Sopenharmony_ci } 748162306a36Sopenharmony_ci 748262306a36Sopenharmony_ci if (le16_to_cpu(neg_req->SecurityMode) != conn->cli_sec_mode) { 748362306a36Sopenharmony_ci ret = -EINVAL; 748462306a36Sopenharmony_ci goto err_out; 748562306a36Sopenharmony_ci } 748662306a36Sopenharmony_ci 748762306a36Sopenharmony_ci if (le32_to_cpu(neg_req->Capabilities) != conn->cli_cap) { 748862306a36Sopenharmony_ci ret = -EINVAL; 748962306a36Sopenharmony_ci goto err_out; 749062306a36Sopenharmony_ci } 749162306a36Sopenharmony_ci 749262306a36Sopenharmony_ci neg_rsp->Capabilities = cpu_to_le32(conn->vals->capabilities); 749362306a36Sopenharmony_ci memset(neg_rsp->Guid, 0, SMB2_CLIENT_GUID_SIZE); 749462306a36Sopenharmony_ci neg_rsp->SecurityMode = cpu_to_le16(conn->srv_sec_mode); 749562306a36Sopenharmony_ci neg_rsp->Dialect = cpu_to_le16(conn->dialect); 749662306a36Sopenharmony_cierr_out: 749762306a36Sopenharmony_ci return ret; 749862306a36Sopenharmony_ci} 749962306a36Sopenharmony_ci 750062306a36Sopenharmony_cistatic int fsctl_query_allocated_ranges(struct ksmbd_work *work, u64 id, 750162306a36Sopenharmony_ci struct file_allocated_range_buffer *qar_req, 750262306a36Sopenharmony_ci struct file_allocated_range_buffer *qar_rsp, 750362306a36Sopenharmony_ci unsigned int in_count, unsigned int *out_count) 750462306a36Sopenharmony_ci{ 750562306a36Sopenharmony_ci struct ksmbd_file *fp; 750662306a36Sopenharmony_ci loff_t start, length; 750762306a36Sopenharmony_ci int ret = 0; 750862306a36Sopenharmony_ci 750962306a36Sopenharmony_ci *out_count = 0; 751062306a36Sopenharmony_ci if (in_count == 0) 751162306a36Sopenharmony_ci return -EINVAL; 751262306a36Sopenharmony_ci 751362306a36Sopenharmony_ci start = le64_to_cpu(qar_req->file_offset); 751462306a36Sopenharmony_ci length = le64_to_cpu(qar_req->length); 751562306a36Sopenharmony_ci 751662306a36Sopenharmony_ci if (start < 0 || length < 0) 751762306a36Sopenharmony_ci return -EINVAL; 751862306a36Sopenharmony_ci 751962306a36Sopenharmony_ci fp = ksmbd_lookup_fd_fast(work, id); 752062306a36Sopenharmony_ci if (!fp) 752162306a36Sopenharmony_ci return -ENOENT; 752262306a36Sopenharmony_ci 752362306a36Sopenharmony_ci ret = ksmbd_vfs_fqar_lseek(fp, start, length, 752462306a36Sopenharmony_ci qar_rsp, in_count, out_count); 752562306a36Sopenharmony_ci if (ret && ret != -E2BIG) 752662306a36Sopenharmony_ci *out_count = 0; 752762306a36Sopenharmony_ci 752862306a36Sopenharmony_ci ksmbd_fd_put(work, fp); 752962306a36Sopenharmony_ci return ret; 753062306a36Sopenharmony_ci} 753162306a36Sopenharmony_ci 753262306a36Sopenharmony_cistatic int fsctl_pipe_transceive(struct ksmbd_work *work, u64 id, 753362306a36Sopenharmony_ci unsigned int out_buf_len, 753462306a36Sopenharmony_ci struct smb2_ioctl_req *req, 753562306a36Sopenharmony_ci struct smb2_ioctl_rsp *rsp) 753662306a36Sopenharmony_ci{ 753762306a36Sopenharmony_ci struct ksmbd_rpc_command *rpc_resp; 753862306a36Sopenharmony_ci char *data_buf = (char *)&req->Buffer[0]; 753962306a36Sopenharmony_ci int nbytes = 0; 754062306a36Sopenharmony_ci 754162306a36Sopenharmony_ci rpc_resp = ksmbd_rpc_ioctl(work->sess, id, data_buf, 754262306a36Sopenharmony_ci le32_to_cpu(req->InputCount)); 754362306a36Sopenharmony_ci if (rpc_resp) { 754462306a36Sopenharmony_ci if (rpc_resp->flags == KSMBD_RPC_SOME_NOT_MAPPED) { 754562306a36Sopenharmony_ci /* 754662306a36Sopenharmony_ci * set STATUS_SOME_NOT_MAPPED response 754762306a36Sopenharmony_ci * for unknown domain sid. 754862306a36Sopenharmony_ci */ 754962306a36Sopenharmony_ci rsp->hdr.Status = STATUS_SOME_NOT_MAPPED; 755062306a36Sopenharmony_ci } else if (rpc_resp->flags == KSMBD_RPC_ENOTIMPLEMENTED) { 755162306a36Sopenharmony_ci rsp->hdr.Status = STATUS_NOT_SUPPORTED; 755262306a36Sopenharmony_ci goto out; 755362306a36Sopenharmony_ci } else if (rpc_resp->flags != KSMBD_RPC_OK) { 755462306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_PARAMETER; 755562306a36Sopenharmony_ci goto out; 755662306a36Sopenharmony_ci } 755762306a36Sopenharmony_ci 755862306a36Sopenharmony_ci nbytes = rpc_resp->payload_sz; 755962306a36Sopenharmony_ci if (rpc_resp->payload_sz > out_buf_len) { 756062306a36Sopenharmony_ci rsp->hdr.Status = STATUS_BUFFER_OVERFLOW; 756162306a36Sopenharmony_ci nbytes = out_buf_len; 756262306a36Sopenharmony_ci } 756362306a36Sopenharmony_ci 756462306a36Sopenharmony_ci if (!rpc_resp->payload_sz) { 756562306a36Sopenharmony_ci rsp->hdr.Status = 756662306a36Sopenharmony_ci STATUS_UNEXPECTED_IO_ERROR; 756762306a36Sopenharmony_ci goto out; 756862306a36Sopenharmony_ci } 756962306a36Sopenharmony_ci 757062306a36Sopenharmony_ci memcpy((char *)rsp->Buffer, rpc_resp->payload, nbytes); 757162306a36Sopenharmony_ci } 757262306a36Sopenharmony_ciout: 757362306a36Sopenharmony_ci kvfree(rpc_resp); 757462306a36Sopenharmony_ci return nbytes; 757562306a36Sopenharmony_ci} 757662306a36Sopenharmony_ci 757762306a36Sopenharmony_cistatic inline int fsctl_set_sparse(struct ksmbd_work *work, u64 id, 757862306a36Sopenharmony_ci struct file_sparse *sparse) 757962306a36Sopenharmony_ci{ 758062306a36Sopenharmony_ci struct ksmbd_file *fp; 758162306a36Sopenharmony_ci struct mnt_idmap *idmap; 758262306a36Sopenharmony_ci int ret = 0; 758362306a36Sopenharmony_ci __le32 old_fattr; 758462306a36Sopenharmony_ci 758562306a36Sopenharmony_ci fp = ksmbd_lookup_fd_fast(work, id); 758662306a36Sopenharmony_ci if (!fp) 758762306a36Sopenharmony_ci return -ENOENT; 758862306a36Sopenharmony_ci idmap = file_mnt_idmap(fp->filp); 758962306a36Sopenharmony_ci 759062306a36Sopenharmony_ci old_fattr = fp->f_ci->m_fattr; 759162306a36Sopenharmony_ci if (sparse->SetSparse) 759262306a36Sopenharmony_ci fp->f_ci->m_fattr |= FILE_ATTRIBUTE_SPARSE_FILE_LE; 759362306a36Sopenharmony_ci else 759462306a36Sopenharmony_ci fp->f_ci->m_fattr &= ~FILE_ATTRIBUTE_SPARSE_FILE_LE; 759562306a36Sopenharmony_ci 759662306a36Sopenharmony_ci if (fp->f_ci->m_fattr != old_fattr && 759762306a36Sopenharmony_ci test_share_config_flag(work->tcon->share_conf, 759862306a36Sopenharmony_ci KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { 759962306a36Sopenharmony_ci struct xattr_dos_attrib da; 760062306a36Sopenharmony_ci 760162306a36Sopenharmony_ci ret = ksmbd_vfs_get_dos_attrib_xattr(idmap, 760262306a36Sopenharmony_ci fp->filp->f_path.dentry, &da); 760362306a36Sopenharmony_ci if (ret <= 0) 760462306a36Sopenharmony_ci goto out; 760562306a36Sopenharmony_ci 760662306a36Sopenharmony_ci da.attr = le32_to_cpu(fp->f_ci->m_fattr); 760762306a36Sopenharmony_ci ret = ksmbd_vfs_set_dos_attrib_xattr(idmap, 760862306a36Sopenharmony_ci &fp->filp->f_path, 760962306a36Sopenharmony_ci &da, true); 761062306a36Sopenharmony_ci if (ret) 761162306a36Sopenharmony_ci fp->f_ci->m_fattr = old_fattr; 761262306a36Sopenharmony_ci } 761362306a36Sopenharmony_ci 761462306a36Sopenharmony_ciout: 761562306a36Sopenharmony_ci ksmbd_fd_put(work, fp); 761662306a36Sopenharmony_ci return ret; 761762306a36Sopenharmony_ci} 761862306a36Sopenharmony_ci 761962306a36Sopenharmony_cistatic int fsctl_request_resume_key(struct ksmbd_work *work, 762062306a36Sopenharmony_ci struct smb2_ioctl_req *req, 762162306a36Sopenharmony_ci struct resume_key_ioctl_rsp *key_rsp) 762262306a36Sopenharmony_ci{ 762362306a36Sopenharmony_ci struct ksmbd_file *fp; 762462306a36Sopenharmony_ci 762562306a36Sopenharmony_ci fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); 762662306a36Sopenharmony_ci if (!fp) 762762306a36Sopenharmony_ci return -ENOENT; 762862306a36Sopenharmony_ci 762962306a36Sopenharmony_ci memset(key_rsp, 0, sizeof(*key_rsp)); 763062306a36Sopenharmony_ci key_rsp->ResumeKey[0] = req->VolatileFileId; 763162306a36Sopenharmony_ci key_rsp->ResumeKey[1] = req->PersistentFileId; 763262306a36Sopenharmony_ci ksmbd_fd_put(work, fp); 763362306a36Sopenharmony_ci 763462306a36Sopenharmony_ci return 0; 763562306a36Sopenharmony_ci} 763662306a36Sopenharmony_ci 763762306a36Sopenharmony_ci/** 763862306a36Sopenharmony_ci * smb2_ioctl() - handler for smb2 ioctl command 763962306a36Sopenharmony_ci * @work: smb work containing ioctl command buffer 764062306a36Sopenharmony_ci * 764162306a36Sopenharmony_ci * Return: 0 on success, otherwise error 764262306a36Sopenharmony_ci */ 764362306a36Sopenharmony_ciint smb2_ioctl(struct ksmbd_work *work) 764462306a36Sopenharmony_ci{ 764562306a36Sopenharmony_ci struct smb2_ioctl_req *req; 764662306a36Sopenharmony_ci struct smb2_ioctl_rsp *rsp; 764762306a36Sopenharmony_ci unsigned int cnt_code, nbytes = 0, out_buf_len, in_buf_len; 764862306a36Sopenharmony_ci u64 id = KSMBD_NO_FID; 764962306a36Sopenharmony_ci struct ksmbd_conn *conn = work->conn; 765062306a36Sopenharmony_ci int ret = 0; 765162306a36Sopenharmony_ci 765262306a36Sopenharmony_ci if (work->next_smb2_rcv_hdr_off) { 765362306a36Sopenharmony_ci req = ksmbd_req_buf_next(work); 765462306a36Sopenharmony_ci rsp = ksmbd_resp_buf_next(work); 765562306a36Sopenharmony_ci if (!has_file_id(req->VolatileFileId)) { 765662306a36Sopenharmony_ci ksmbd_debug(SMB, "Compound request set FID = %llu\n", 765762306a36Sopenharmony_ci work->compound_fid); 765862306a36Sopenharmony_ci id = work->compound_fid; 765962306a36Sopenharmony_ci } 766062306a36Sopenharmony_ci } else { 766162306a36Sopenharmony_ci req = smb2_get_msg(work->request_buf); 766262306a36Sopenharmony_ci rsp = smb2_get_msg(work->response_buf); 766362306a36Sopenharmony_ci } 766462306a36Sopenharmony_ci 766562306a36Sopenharmony_ci if (!has_file_id(id)) 766662306a36Sopenharmony_ci id = req->VolatileFileId; 766762306a36Sopenharmony_ci 766862306a36Sopenharmony_ci if (req->Flags != cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL)) { 766962306a36Sopenharmony_ci rsp->hdr.Status = STATUS_NOT_SUPPORTED; 767062306a36Sopenharmony_ci goto out; 767162306a36Sopenharmony_ci } 767262306a36Sopenharmony_ci 767362306a36Sopenharmony_ci cnt_code = le32_to_cpu(req->CtlCode); 767462306a36Sopenharmony_ci ret = smb2_calc_max_out_buf_len(work, 48, 767562306a36Sopenharmony_ci le32_to_cpu(req->MaxOutputResponse)); 767662306a36Sopenharmony_ci if (ret < 0) { 767762306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_PARAMETER; 767862306a36Sopenharmony_ci goto out; 767962306a36Sopenharmony_ci } 768062306a36Sopenharmony_ci out_buf_len = (unsigned int)ret; 768162306a36Sopenharmony_ci in_buf_len = le32_to_cpu(req->InputCount); 768262306a36Sopenharmony_ci 768362306a36Sopenharmony_ci switch (cnt_code) { 768462306a36Sopenharmony_ci case FSCTL_DFS_GET_REFERRALS: 768562306a36Sopenharmony_ci case FSCTL_DFS_GET_REFERRALS_EX: 768662306a36Sopenharmony_ci /* Not support DFS yet */ 768762306a36Sopenharmony_ci rsp->hdr.Status = STATUS_FS_DRIVER_REQUIRED; 768862306a36Sopenharmony_ci goto out; 768962306a36Sopenharmony_ci case FSCTL_CREATE_OR_GET_OBJECT_ID: 769062306a36Sopenharmony_ci { 769162306a36Sopenharmony_ci struct file_object_buf_type1_ioctl_rsp *obj_buf; 769262306a36Sopenharmony_ci 769362306a36Sopenharmony_ci nbytes = sizeof(struct file_object_buf_type1_ioctl_rsp); 769462306a36Sopenharmony_ci obj_buf = (struct file_object_buf_type1_ioctl_rsp *) 769562306a36Sopenharmony_ci &rsp->Buffer[0]; 769662306a36Sopenharmony_ci 769762306a36Sopenharmony_ci /* 769862306a36Sopenharmony_ci * TODO: This is dummy implementation to pass smbtorture 769962306a36Sopenharmony_ci * Need to check correct response later 770062306a36Sopenharmony_ci */ 770162306a36Sopenharmony_ci memset(obj_buf->ObjectId, 0x0, 16); 770262306a36Sopenharmony_ci memset(obj_buf->BirthVolumeId, 0x0, 16); 770362306a36Sopenharmony_ci memset(obj_buf->BirthObjectId, 0x0, 16); 770462306a36Sopenharmony_ci memset(obj_buf->DomainId, 0x0, 16); 770562306a36Sopenharmony_ci 770662306a36Sopenharmony_ci break; 770762306a36Sopenharmony_ci } 770862306a36Sopenharmony_ci case FSCTL_PIPE_TRANSCEIVE: 770962306a36Sopenharmony_ci out_buf_len = min_t(u32, KSMBD_IPC_MAX_PAYLOAD, out_buf_len); 771062306a36Sopenharmony_ci nbytes = fsctl_pipe_transceive(work, id, out_buf_len, req, rsp); 771162306a36Sopenharmony_ci break; 771262306a36Sopenharmony_ci case FSCTL_VALIDATE_NEGOTIATE_INFO: 771362306a36Sopenharmony_ci if (conn->dialect < SMB30_PROT_ID) { 771462306a36Sopenharmony_ci ret = -EOPNOTSUPP; 771562306a36Sopenharmony_ci goto out; 771662306a36Sopenharmony_ci } 771762306a36Sopenharmony_ci 771862306a36Sopenharmony_ci if (in_buf_len < offsetof(struct validate_negotiate_info_req, 771962306a36Sopenharmony_ci Dialects)) { 772062306a36Sopenharmony_ci ret = -EINVAL; 772162306a36Sopenharmony_ci goto out; 772262306a36Sopenharmony_ci } 772362306a36Sopenharmony_ci 772462306a36Sopenharmony_ci if (out_buf_len < sizeof(struct validate_negotiate_info_rsp)) { 772562306a36Sopenharmony_ci ret = -EINVAL; 772662306a36Sopenharmony_ci goto out; 772762306a36Sopenharmony_ci } 772862306a36Sopenharmony_ci 772962306a36Sopenharmony_ci ret = fsctl_validate_negotiate_info(conn, 773062306a36Sopenharmony_ci (struct validate_negotiate_info_req *)&req->Buffer[0], 773162306a36Sopenharmony_ci (struct validate_negotiate_info_rsp *)&rsp->Buffer[0], 773262306a36Sopenharmony_ci in_buf_len); 773362306a36Sopenharmony_ci if (ret < 0) 773462306a36Sopenharmony_ci goto out; 773562306a36Sopenharmony_ci 773662306a36Sopenharmony_ci nbytes = sizeof(struct validate_negotiate_info_rsp); 773762306a36Sopenharmony_ci rsp->PersistentFileId = SMB2_NO_FID; 773862306a36Sopenharmony_ci rsp->VolatileFileId = SMB2_NO_FID; 773962306a36Sopenharmony_ci break; 774062306a36Sopenharmony_ci case FSCTL_QUERY_NETWORK_INTERFACE_INFO: 774162306a36Sopenharmony_ci ret = fsctl_query_iface_info_ioctl(conn, rsp, out_buf_len); 774262306a36Sopenharmony_ci if (ret < 0) 774362306a36Sopenharmony_ci goto out; 774462306a36Sopenharmony_ci nbytes = ret; 774562306a36Sopenharmony_ci break; 774662306a36Sopenharmony_ci case FSCTL_REQUEST_RESUME_KEY: 774762306a36Sopenharmony_ci if (out_buf_len < sizeof(struct resume_key_ioctl_rsp)) { 774862306a36Sopenharmony_ci ret = -EINVAL; 774962306a36Sopenharmony_ci goto out; 775062306a36Sopenharmony_ci } 775162306a36Sopenharmony_ci 775262306a36Sopenharmony_ci ret = fsctl_request_resume_key(work, req, 775362306a36Sopenharmony_ci (struct resume_key_ioctl_rsp *)&rsp->Buffer[0]); 775462306a36Sopenharmony_ci if (ret < 0) 775562306a36Sopenharmony_ci goto out; 775662306a36Sopenharmony_ci rsp->PersistentFileId = req->PersistentFileId; 775762306a36Sopenharmony_ci rsp->VolatileFileId = req->VolatileFileId; 775862306a36Sopenharmony_ci nbytes = sizeof(struct resume_key_ioctl_rsp); 775962306a36Sopenharmony_ci break; 776062306a36Sopenharmony_ci case FSCTL_COPYCHUNK: 776162306a36Sopenharmony_ci case FSCTL_COPYCHUNK_WRITE: 776262306a36Sopenharmony_ci if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { 776362306a36Sopenharmony_ci ksmbd_debug(SMB, 776462306a36Sopenharmony_ci "User does not have write permission\n"); 776562306a36Sopenharmony_ci ret = -EACCES; 776662306a36Sopenharmony_ci goto out; 776762306a36Sopenharmony_ci } 776862306a36Sopenharmony_ci 776962306a36Sopenharmony_ci if (in_buf_len < sizeof(struct copychunk_ioctl_req)) { 777062306a36Sopenharmony_ci ret = -EINVAL; 777162306a36Sopenharmony_ci goto out; 777262306a36Sopenharmony_ci } 777362306a36Sopenharmony_ci 777462306a36Sopenharmony_ci if (out_buf_len < sizeof(struct copychunk_ioctl_rsp)) { 777562306a36Sopenharmony_ci ret = -EINVAL; 777662306a36Sopenharmony_ci goto out; 777762306a36Sopenharmony_ci } 777862306a36Sopenharmony_ci 777962306a36Sopenharmony_ci nbytes = sizeof(struct copychunk_ioctl_rsp); 778062306a36Sopenharmony_ci rsp->VolatileFileId = req->VolatileFileId; 778162306a36Sopenharmony_ci rsp->PersistentFileId = req->PersistentFileId; 778262306a36Sopenharmony_ci fsctl_copychunk(work, 778362306a36Sopenharmony_ci (struct copychunk_ioctl_req *)&req->Buffer[0], 778462306a36Sopenharmony_ci le32_to_cpu(req->CtlCode), 778562306a36Sopenharmony_ci le32_to_cpu(req->InputCount), 778662306a36Sopenharmony_ci req->VolatileFileId, 778762306a36Sopenharmony_ci req->PersistentFileId, 778862306a36Sopenharmony_ci rsp); 778962306a36Sopenharmony_ci break; 779062306a36Sopenharmony_ci case FSCTL_SET_SPARSE: 779162306a36Sopenharmony_ci if (in_buf_len < sizeof(struct file_sparse)) { 779262306a36Sopenharmony_ci ret = -EINVAL; 779362306a36Sopenharmony_ci goto out; 779462306a36Sopenharmony_ci } 779562306a36Sopenharmony_ci 779662306a36Sopenharmony_ci ret = fsctl_set_sparse(work, id, 779762306a36Sopenharmony_ci (struct file_sparse *)&req->Buffer[0]); 779862306a36Sopenharmony_ci if (ret < 0) 779962306a36Sopenharmony_ci goto out; 780062306a36Sopenharmony_ci break; 780162306a36Sopenharmony_ci case FSCTL_SET_ZERO_DATA: 780262306a36Sopenharmony_ci { 780362306a36Sopenharmony_ci struct file_zero_data_information *zero_data; 780462306a36Sopenharmony_ci struct ksmbd_file *fp; 780562306a36Sopenharmony_ci loff_t off, len, bfz; 780662306a36Sopenharmony_ci 780762306a36Sopenharmony_ci if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { 780862306a36Sopenharmony_ci ksmbd_debug(SMB, 780962306a36Sopenharmony_ci "User does not have write permission\n"); 781062306a36Sopenharmony_ci ret = -EACCES; 781162306a36Sopenharmony_ci goto out; 781262306a36Sopenharmony_ci } 781362306a36Sopenharmony_ci 781462306a36Sopenharmony_ci if (in_buf_len < sizeof(struct file_zero_data_information)) { 781562306a36Sopenharmony_ci ret = -EINVAL; 781662306a36Sopenharmony_ci goto out; 781762306a36Sopenharmony_ci } 781862306a36Sopenharmony_ci 781962306a36Sopenharmony_ci zero_data = 782062306a36Sopenharmony_ci (struct file_zero_data_information *)&req->Buffer[0]; 782162306a36Sopenharmony_ci 782262306a36Sopenharmony_ci off = le64_to_cpu(zero_data->FileOffset); 782362306a36Sopenharmony_ci bfz = le64_to_cpu(zero_data->BeyondFinalZero); 782462306a36Sopenharmony_ci if (off < 0 || bfz < 0 || off > bfz) { 782562306a36Sopenharmony_ci ret = -EINVAL; 782662306a36Sopenharmony_ci goto out; 782762306a36Sopenharmony_ci } 782862306a36Sopenharmony_ci 782962306a36Sopenharmony_ci len = bfz - off; 783062306a36Sopenharmony_ci if (len) { 783162306a36Sopenharmony_ci fp = ksmbd_lookup_fd_fast(work, id); 783262306a36Sopenharmony_ci if (!fp) { 783362306a36Sopenharmony_ci ret = -ENOENT; 783462306a36Sopenharmony_ci goto out; 783562306a36Sopenharmony_ci } 783662306a36Sopenharmony_ci 783762306a36Sopenharmony_ci ret = ksmbd_vfs_zero_data(work, fp, off, len); 783862306a36Sopenharmony_ci ksmbd_fd_put(work, fp); 783962306a36Sopenharmony_ci if (ret < 0) 784062306a36Sopenharmony_ci goto out; 784162306a36Sopenharmony_ci } 784262306a36Sopenharmony_ci break; 784362306a36Sopenharmony_ci } 784462306a36Sopenharmony_ci case FSCTL_QUERY_ALLOCATED_RANGES: 784562306a36Sopenharmony_ci if (in_buf_len < sizeof(struct file_allocated_range_buffer)) { 784662306a36Sopenharmony_ci ret = -EINVAL; 784762306a36Sopenharmony_ci goto out; 784862306a36Sopenharmony_ci } 784962306a36Sopenharmony_ci 785062306a36Sopenharmony_ci ret = fsctl_query_allocated_ranges(work, id, 785162306a36Sopenharmony_ci (struct file_allocated_range_buffer *)&req->Buffer[0], 785262306a36Sopenharmony_ci (struct file_allocated_range_buffer *)&rsp->Buffer[0], 785362306a36Sopenharmony_ci out_buf_len / 785462306a36Sopenharmony_ci sizeof(struct file_allocated_range_buffer), &nbytes); 785562306a36Sopenharmony_ci if (ret == -E2BIG) { 785662306a36Sopenharmony_ci rsp->hdr.Status = STATUS_BUFFER_OVERFLOW; 785762306a36Sopenharmony_ci } else if (ret < 0) { 785862306a36Sopenharmony_ci nbytes = 0; 785962306a36Sopenharmony_ci goto out; 786062306a36Sopenharmony_ci } 786162306a36Sopenharmony_ci 786262306a36Sopenharmony_ci nbytes *= sizeof(struct file_allocated_range_buffer); 786362306a36Sopenharmony_ci break; 786462306a36Sopenharmony_ci case FSCTL_GET_REPARSE_POINT: 786562306a36Sopenharmony_ci { 786662306a36Sopenharmony_ci struct reparse_data_buffer *reparse_ptr; 786762306a36Sopenharmony_ci struct ksmbd_file *fp; 786862306a36Sopenharmony_ci 786962306a36Sopenharmony_ci reparse_ptr = (struct reparse_data_buffer *)&rsp->Buffer[0]; 787062306a36Sopenharmony_ci fp = ksmbd_lookup_fd_fast(work, id); 787162306a36Sopenharmony_ci if (!fp) { 787262306a36Sopenharmony_ci pr_err("not found fp!!\n"); 787362306a36Sopenharmony_ci ret = -ENOENT; 787462306a36Sopenharmony_ci goto out; 787562306a36Sopenharmony_ci } 787662306a36Sopenharmony_ci 787762306a36Sopenharmony_ci reparse_ptr->ReparseTag = 787862306a36Sopenharmony_ci smb2_get_reparse_tag_special_file(file_inode(fp->filp)->i_mode); 787962306a36Sopenharmony_ci reparse_ptr->ReparseDataLength = 0; 788062306a36Sopenharmony_ci ksmbd_fd_put(work, fp); 788162306a36Sopenharmony_ci nbytes = sizeof(struct reparse_data_buffer); 788262306a36Sopenharmony_ci break; 788362306a36Sopenharmony_ci } 788462306a36Sopenharmony_ci case FSCTL_DUPLICATE_EXTENTS_TO_FILE: 788562306a36Sopenharmony_ci { 788662306a36Sopenharmony_ci struct ksmbd_file *fp_in, *fp_out = NULL; 788762306a36Sopenharmony_ci struct duplicate_extents_to_file *dup_ext; 788862306a36Sopenharmony_ci loff_t src_off, dst_off, length, cloned; 788962306a36Sopenharmony_ci 789062306a36Sopenharmony_ci if (in_buf_len < sizeof(struct duplicate_extents_to_file)) { 789162306a36Sopenharmony_ci ret = -EINVAL; 789262306a36Sopenharmony_ci goto out; 789362306a36Sopenharmony_ci } 789462306a36Sopenharmony_ci 789562306a36Sopenharmony_ci dup_ext = (struct duplicate_extents_to_file *)&req->Buffer[0]; 789662306a36Sopenharmony_ci 789762306a36Sopenharmony_ci fp_in = ksmbd_lookup_fd_slow(work, dup_ext->VolatileFileHandle, 789862306a36Sopenharmony_ci dup_ext->PersistentFileHandle); 789962306a36Sopenharmony_ci if (!fp_in) { 790062306a36Sopenharmony_ci pr_err("not found file handle in duplicate extent to file\n"); 790162306a36Sopenharmony_ci ret = -ENOENT; 790262306a36Sopenharmony_ci goto out; 790362306a36Sopenharmony_ci } 790462306a36Sopenharmony_ci 790562306a36Sopenharmony_ci fp_out = ksmbd_lookup_fd_fast(work, id); 790662306a36Sopenharmony_ci if (!fp_out) { 790762306a36Sopenharmony_ci pr_err("not found fp\n"); 790862306a36Sopenharmony_ci ret = -ENOENT; 790962306a36Sopenharmony_ci goto dup_ext_out; 791062306a36Sopenharmony_ci } 791162306a36Sopenharmony_ci 791262306a36Sopenharmony_ci src_off = le64_to_cpu(dup_ext->SourceFileOffset); 791362306a36Sopenharmony_ci dst_off = le64_to_cpu(dup_ext->TargetFileOffset); 791462306a36Sopenharmony_ci length = le64_to_cpu(dup_ext->ByteCount); 791562306a36Sopenharmony_ci /* 791662306a36Sopenharmony_ci * XXX: It is not clear if FSCTL_DUPLICATE_EXTENTS_TO_FILE 791762306a36Sopenharmony_ci * should fall back to vfs_copy_file_range(). This could be 791862306a36Sopenharmony_ci * beneficial when re-exporting nfs/smb mount, but note that 791962306a36Sopenharmony_ci * this can result in partial copy that returns an error status. 792062306a36Sopenharmony_ci * If/when FSCTL_DUPLICATE_EXTENTS_TO_FILE_EX is implemented, 792162306a36Sopenharmony_ci * fall back to vfs_copy_file_range(), should be avoided when 792262306a36Sopenharmony_ci * the flag DUPLICATE_EXTENTS_DATA_EX_SOURCE_ATOMIC is set. 792362306a36Sopenharmony_ci */ 792462306a36Sopenharmony_ci cloned = vfs_clone_file_range(fp_in->filp, src_off, 792562306a36Sopenharmony_ci fp_out->filp, dst_off, length, 0); 792662306a36Sopenharmony_ci if (cloned == -EXDEV || cloned == -EOPNOTSUPP) { 792762306a36Sopenharmony_ci ret = -EOPNOTSUPP; 792862306a36Sopenharmony_ci goto dup_ext_out; 792962306a36Sopenharmony_ci } else if (cloned != length) { 793062306a36Sopenharmony_ci cloned = vfs_copy_file_range(fp_in->filp, src_off, 793162306a36Sopenharmony_ci fp_out->filp, dst_off, 793262306a36Sopenharmony_ci length, 0); 793362306a36Sopenharmony_ci if (cloned != length) { 793462306a36Sopenharmony_ci if (cloned < 0) 793562306a36Sopenharmony_ci ret = cloned; 793662306a36Sopenharmony_ci else 793762306a36Sopenharmony_ci ret = -EINVAL; 793862306a36Sopenharmony_ci } 793962306a36Sopenharmony_ci } 794062306a36Sopenharmony_ci 794162306a36Sopenharmony_cidup_ext_out: 794262306a36Sopenharmony_ci ksmbd_fd_put(work, fp_in); 794362306a36Sopenharmony_ci ksmbd_fd_put(work, fp_out); 794462306a36Sopenharmony_ci if (ret < 0) 794562306a36Sopenharmony_ci goto out; 794662306a36Sopenharmony_ci break; 794762306a36Sopenharmony_ci } 794862306a36Sopenharmony_ci default: 794962306a36Sopenharmony_ci ksmbd_debug(SMB, "not implemented yet ioctl command 0x%x\n", 795062306a36Sopenharmony_ci cnt_code); 795162306a36Sopenharmony_ci ret = -EOPNOTSUPP; 795262306a36Sopenharmony_ci goto out; 795362306a36Sopenharmony_ci } 795462306a36Sopenharmony_ci 795562306a36Sopenharmony_ci rsp->CtlCode = cpu_to_le32(cnt_code); 795662306a36Sopenharmony_ci rsp->InputCount = cpu_to_le32(0); 795762306a36Sopenharmony_ci rsp->InputOffset = cpu_to_le32(112); 795862306a36Sopenharmony_ci rsp->OutputOffset = cpu_to_le32(112); 795962306a36Sopenharmony_ci rsp->OutputCount = cpu_to_le32(nbytes); 796062306a36Sopenharmony_ci rsp->StructureSize = cpu_to_le16(49); 796162306a36Sopenharmony_ci rsp->Reserved = cpu_to_le16(0); 796262306a36Sopenharmony_ci rsp->Flags = cpu_to_le32(0); 796362306a36Sopenharmony_ci rsp->Reserved2 = cpu_to_le32(0); 796462306a36Sopenharmony_ci ret = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_ioctl_rsp) + nbytes); 796562306a36Sopenharmony_ci if (!ret) 796662306a36Sopenharmony_ci return ret; 796762306a36Sopenharmony_ci 796862306a36Sopenharmony_ciout: 796962306a36Sopenharmony_ci if (ret == -EACCES) 797062306a36Sopenharmony_ci rsp->hdr.Status = STATUS_ACCESS_DENIED; 797162306a36Sopenharmony_ci else if (ret == -ENOENT) 797262306a36Sopenharmony_ci rsp->hdr.Status = STATUS_OBJECT_NAME_NOT_FOUND; 797362306a36Sopenharmony_ci else if (ret == -EOPNOTSUPP) 797462306a36Sopenharmony_ci rsp->hdr.Status = STATUS_NOT_SUPPORTED; 797562306a36Sopenharmony_ci else if (ret == -ENOSPC) 797662306a36Sopenharmony_ci rsp->hdr.Status = STATUS_BUFFER_TOO_SMALL; 797762306a36Sopenharmony_ci else if (ret < 0 || rsp->hdr.Status == 0) 797862306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_PARAMETER; 797962306a36Sopenharmony_ci smb2_set_err_rsp(work); 798062306a36Sopenharmony_ci return 0; 798162306a36Sopenharmony_ci} 798262306a36Sopenharmony_ci 798362306a36Sopenharmony_ci/** 798462306a36Sopenharmony_ci * smb20_oplock_break_ack() - handler for smb2.0 oplock break command 798562306a36Sopenharmony_ci * @work: smb work containing oplock break command buffer 798662306a36Sopenharmony_ci * 798762306a36Sopenharmony_ci * Return: 0 798862306a36Sopenharmony_ci */ 798962306a36Sopenharmony_cistatic void smb20_oplock_break_ack(struct ksmbd_work *work) 799062306a36Sopenharmony_ci{ 799162306a36Sopenharmony_ci struct smb2_oplock_break *req; 799262306a36Sopenharmony_ci struct smb2_oplock_break *rsp; 799362306a36Sopenharmony_ci struct ksmbd_file *fp; 799462306a36Sopenharmony_ci struct oplock_info *opinfo = NULL; 799562306a36Sopenharmony_ci __le32 err = 0; 799662306a36Sopenharmony_ci int ret = 0; 799762306a36Sopenharmony_ci u64 volatile_id, persistent_id; 799862306a36Sopenharmony_ci char req_oplevel = 0, rsp_oplevel = 0; 799962306a36Sopenharmony_ci unsigned int oplock_change_type; 800062306a36Sopenharmony_ci 800162306a36Sopenharmony_ci WORK_BUFFERS(work, req, rsp); 800262306a36Sopenharmony_ci 800362306a36Sopenharmony_ci volatile_id = req->VolatileFid; 800462306a36Sopenharmony_ci persistent_id = req->PersistentFid; 800562306a36Sopenharmony_ci req_oplevel = req->OplockLevel; 800662306a36Sopenharmony_ci ksmbd_debug(OPLOCK, "v_id %llu, p_id %llu request oplock level %d\n", 800762306a36Sopenharmony_ci volatile_id, persistent_id, req_oplevel); 800862306a36Sopenharmony_ci 800962306a36Sopenharmony_ci fp = ksmbd_lookup_fd_slow(work, volatile_id, persistent_id); 801062306a36Sopenharmony_ci if (!fp) { 801162306a36Sopenharmony_ci rsp->hdr.Status = STATUS_FILE_CLOSED; 801262306a36Sopenharmony_ci smb2_set_err_rsp(work); 801362306a36Sopenharmony_ci return; 801462306a36Sopenharmony_ci } 801562306a36Sopenharmony_ci 801662306a36Sopenharmony_ci opinfo = opinfo_get(fp); 801762306a36Sopenharmony_ci if (!opinfo) { 801862306a36Sopenharmony_ci pr_err("unexpected null oplock_info\n"); 801962306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_OPLOCK_PROTOCOL; 802062306a36Sopenharmony_ci smb2_set_err_rsp(work); 802162306a36Sopenharmony_ci ksmbd_fd_put(work, fp); 802262306a36Sopenharmony_ci return; 802362306a36Sopenharmony_ci } 802462306a36Sopenharmony_ci 802562306a36Sopenharmony_ci if (opinfo->level == SMB2_OPLOCK_LEVEL_NONE) { 802662306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_OPLOCK_PROTOCOL; 802762306a36Sopenharmony_ci goto err_out; 802862306a36Sopenharmony_ci } 802962306a36Sopenharmony_ci 803062306a36Sopenharmony_ci if (opinfo->op_state == OPLOCK_STATE_NONE) { 803162306a36Sopenharmony_ci ksmbd_debug(SMB, "unexpected oplock state 0x%x\n", opinfo->op_state); 803262306a36Sopenharmony_ci rsp->hdr.Status = STATUS_UNSUCCESSFUL; 803362306a36Sopenharmony_ci goto err_out; 803462306a36Sopenharmony_ci } 803562306a36Sopenharmony_ci 803662306a36Sopenharmony_ci if ((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE || 803762306a36Sopenharmony_ci opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) && 803862306a36Sopenharmony_ci (req_oplevel != SMB2_OPLOCK_LEVEL_II && 803962306a36Sopenharmony_ci req_oplevel != SMB2_OPLOCK_LEVEL_NONE)) { 804062306a36Sopenharmony_ci err = STATUS_INVALID_OPLOCK_PROTOCOL; 804162306a36Sopenharmony_ci oplock_change_type = OPLOCK_WRITE_TO_NONE; 804262306a36Sopenharmony_ci } else if (opinfo->level == SMB2_OPLOCK_LEVEL_II && 804362306a36Sopenharmony_ci req_oplevel != SMB2_OPLOCK_LEVEL_NONE) { 804462306a36Sopenharmony_ci err = STATUS_INVALID_OPLOCK_PROTOCOL; 804562306a36Sopenharmony_ci oplock_change_type = OPLOCK_READ_TO_NONE; 804662306a36Sopenharmony_ci } else if (req_oplevel == SMB2_OPLOCK_LEVEL_II || 804762306a36Sopenharmony_ci req_oplevel == SMB2_OPLOCK_LEVEL_NONE) { 804862306a36Sopenharmony_ci err = STATUS_INVALID_DEVICE_STATE; 804962306a36Sopenharmony_ci if ((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE || 805062306a36Sopenharmony_ci opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) && 805162306a36Sopenharmony_ci req_oplevel == SMB2_OPLOCK_LEVEL_II) { 805262306a36Sopenharmony_ci oplock_change_type = OPLOCK_WRITE_TO_READ; 805362306a36Sopenharmony_ci } else if ((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE || 805462306a36Sopenharmony_ci opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) && 805562306a36Sopenharmony_ci req_oplevel == SMB2_OPLOCK_LEVEL_NONE) { 805662306a36Sopenharmony_ci oplock_change_type = OPLOCK_WRITE_TO_NONE; 805762306a36Sopenharmony_ci } else if (opinfo->level == SMB2_OPLOCK_LEVEL_II && 805862306a36Sopenharmony_ci req_oplevel == SMB2_OPLOCK_LEVEL_NONE) { 805962306a36Sopenharmony_ci oplock_change_type = OPLOCK_READ_TO_NONE; 806062306a36Sopenharmony_ci } else { 806162306a36Sopenharmony_ci oplock_change_type = 0; 806262306a36Sopenharmony_ci } 806362306a36Sopenharmony_ci } else { 806462306a36Sopenharmony_ci oplock_change_type = 0; 806562306a36Sopenharmony_ci } 806662306a36Sopenharmony_ci 806762306a36Sopenharmony_ci switch (oplock_change_type) { 806862306a36Sopenharmony_ci case OPLOCK_WRITE_TO_READ: 806962306a36Sopenharmony_ci ret = opinfo_write_to_read(opinfo); 807062306a36Sopenharmony_ci rsp_oplevel = SMB2_OPLOCK_LEVEL_II; 807162306a36Sopenharmony_ci break; 807262306a36Sopenharmony_ci case OPLOCK_WRITE_TO_NONE: 807362306a36Sopenharmony_ci ret = opinfo_write_to_none(opinfo); 807462306a36Sopenharmony_ci rsp_oplevel = SMB2_OPLOCK_LEVEL_NONE; 807562306a36Sopenharmony_ci break; 807662306a36Sopenharmony_ci case OPLOCK_READ_TO_NONE: 807762306a36Sopenharmony_ci ret = opinfo_read_to_none(opinfo); 807862306a36Sopenharmony_ci rsp_oplevel = SMB2_OPLOCK_LEVEL_NONE; 807962306a36Sopenharmony_ci break; 808062306a36Sopenharmony_ci default: 808162306a36Sopenharmony_ci pr_err("unknown oplock change 0x%x -> 0x%x\n", 808262306a36Sopenharmony_ci opinfo->level, rsp_oplevel); 808362306a36Sopenharmony_ci } 808462306a36Sopenharmony_ci 808562306a36Sopenharmony_ci if (ret < 0) { 808662306a36Sopenharmony_ci rsp->hdr.Status = err; 808762306a36Sopenharmony_ci goto err_out; 808862306a36Sopenharmony_ci } 808962306a36Sopenharmony_ci 809062306a36Sopenharmony_ci opinfo->op_state = OPLOCK_STATE_NONE; 809162306a36Sopenharmony_ci wake_up_interruptible_all(&opinfo->oplock_q); 809262306a36Sopenharmony_ci opinfo_put(opinfo); 809362306a36Sopenharmony_ci ksmbd_fd_put(work, fp); 809462306a36Sopenharmony_ci 809562306a36Sopenharmony_ci rsp->StructureSize = cpu_to_le16(24); 809662306a36Sopenharmony_ci rsp->OplockLevel = rsp_oplevel; 809762306a36Sopenharmony_ci rsp->Reserved = 0; 809862306a36Sopenharmony_ci rsp->Reserved2 = 0; 809962306a36Sopenharmony_ci rsp->VolatileFid = volatile_id; 810062306a36Sopenharmony_ci rsp->PersistentFid = persistent_id; 810162306a36Sopenharmony_ci ret = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_oplock_break)); 810262306a36Sopenharmony_ci if (!ret) 810362306a36Sopenharmony_ci return; 810462306a36Sopenharmony_ci 810562306a36Sopenharmony_cierr_out: 810662306a36Sopenharmony_ci opinfo->op_state = OPLOCK_STATE_NONE; 810762306a36Sopenharmony_ci wake_up_interruptible_all(&opinfo->oplock_q); 810862306a36Sopenharmony_ci 810962306a36Sopenharmony_ci opinfo_put(opinfo); 811062306a36Sopenharmony_ci ksmbd_fd_put(work, fp); 811162306a36Sopenharmony_ci smb2_set_err_rsp(work); 811262306a36Sopenharmony_ci} 811362306a36Sopenharmony_ci 811462306a36Sopenharmony_cistatic int check_lease_state(struct lease *lease, __le32 req_state) 811562306a36Sopenharmony_ci{ 811662306a36Sopenharmony_ci if ((lease->new_state == 811762306a36Sopenharmony_ci (SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_HANDLE_CACHING_LE)) && 811862306a36Sopenharmony_ci !(req_state & SMB2_LEASE_WRITE_CACHING_LE)) { 811962306a36Sopenharmony_ci lease->new_state = req_state; 812062306a36Sopenharmony_ci return 0; 812162306a36Sopenharmony_ci } 812262306a36Sopenharmony_ci 812362306a36Sopenharmony_ci if (lease->new_state == req_state) 812462306a36Sopenharmony_ci return 0; 812562306a36Sopenharmony_ci 812662306a36Sopenharmony_ci return 1; 812762306a36Sopenharmony_ci} 812862306a36Sopenharmony_ci 812962306a36Sopenharmony_ci/** 813062306a36Sopenharmony_ci * smb21_lease_break_ack() - handler for smb2.1 lease break command 813162306a36Sopenharmony_ci * @work: smb work containing lease break command buffer 813262306a36Sopenharmony_ci * 813362306a36Sopenharmony_ci * Return: 0 813462306a36Sopenharmony_ci */ 813562306a36Sopenharmony_cistatic void smb21_lease_break_ack(struct ksmbd_work *work) 813662306a36Sopenharmony_ci{ 813762306a36Sopenharmony_ci struct ksmbd_conn *conn = work->conn; 813862306a36Sopenharmony_ci struct smb2_lease_ack *req; 813962306a36Sopenharmony_ci struct smb2_lease_ack *rsp; 814062306a36Sopenharmony_ci struct oplock_info *opinfo; 814162306a36Sopenharmony_ci __le32 err = 0; 814262306a36Sopenharmony_ci int ret = 0; 814362306a36Sopenharmony_ci unsigned int lease_change_type; 814462306a36Sopenharmony_ci __le32 lease_state; 814562306a36Sopenharmony_ci struct lease *lease; 814662306a36Sopenharmony_ci 814762306a36Sopenharmony_ci WORK_BUFFERS(work, req, rsp); 814862306a36Sopenharmony_ci 814962306a36Sopenharmony_ci ksmbd_debug(OPLOCK, "smb21 lease break, lease state(0x%x)\n", 815062306a36Sopenharmony_ci le32_to_cpu(req->LeaseState)); 815162306a36Sopenharmony_ci opinfo = lookup_lease_in_table(conn, req->LeaseKey); 815262306a36Sopenharmony_ci if (!opinfo) { 815362306a36Sopenharmony_ci ksmbd_debug(OPLOCK, "file not opened\n"); 815462306a36Sopenharmony_ci smb2_set_err_rsp(work); 815562306a36Sopenharmony_ci rsp->hdr.Status = STATUS_UNSUCCESSFUL; 815662306a36Sopenharmony_ci return; 815762306a36Sopenharmony_ci } 815862306a36Sopenharmony_ci lease = opinfo->o_lease; 815962306a36Sopenharmony_ci 816062306a36Sopenharmony_ci if (opinfo->op_state == OPLOCK_STATE_NONE) { 816162306a36Sopenharmony_ci pr_err("unexpected lease break state 0x%x\n", 816262306a36Sopenharmony_ci opinfo->op_state); 816362306a36Sopenharmony_ci rsp->hdr.Status = STATUS_UNSUCCESSFUL; 816462306a36Sopenharmony_ci goto err_out; 816562306a36Sopenharmony_ci } 816662306a36Sopenharmony_ci 816762306a36Sopenharmony_ci if (check_lease_state(lease, req->LeaseState)) { 816862306a36Sopenharmony_ci rsp->hdr.Status = STATUS_REQUEST_NOT_ACCEPTED; 816962306a36Sopenharmony_ci ksmbd_debug(OPLOCK, 817062306a36Sopenharmony_ci "req lease state: 0x%x, expected state: 0x%x\n", 817162306a36Sopenharmony_ci req->LeaseState, lease->new_state); 817262306a36Sopenharmony_ci goto err_out; 817362306a36Sopenharmony_ci } 817462306a36Sopenharmony_ci 817562306a36Sopenharmony_ci if (!atomic_read(&opinfo->breaking_cnt)) { 817662306a36Sopenharmony_ci rsp->hdr.Status = STATUS_UNSUCCESSFUL; 817762306a36Sopenharmony_ci goto err_out; 817862306a36Sopenharmony_ci } 817962306a36Sopenharmony_ci 818062306a36Sopenharmony_ci /* check for bad lease state */ 818162306a36Sopenharmony_ci if (req->LeaseState & 818262306a36Sopenharmony_ci (~(SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_HANDLE_CACHING_LE))) { 818362306a36Sopenharmony_ci err = STATUS_INVALID_OPLOCK_PROTOCOL; 818462306a36Sopenharmony_ci if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) 818562306a36Sopenharmony_ci lease_change_type = OPLOCK_WRITE_TO_NONE; 818662306a36Sopenharmony_ci else 818762306a36Sopenharmony_ci lease_change_type = OPLOCK_READ_TO_NONE; 818862306a36Sopenharmony_ci ksmbd_debug(OPLOCK, "handle bad lease state 0x%x -> 0x%x\n", 818962306a36Sopenharmony_ci le32_to_cpu(lease->state), 819062306a36Sopenharmony_ci le32_to_cpu(req->LeaseState)); 819162306a36Sopenharmony_ci } else if (lease->state == SMB2_LEASE_READ_CACHING_LE && 819262306a36Sopenharmony_ci req->LeaseState != SMB2_LEASE_NONE_LE) { 819362306a36Sopenharmony_ci err = STATUS_INVALID_OPLOCK_PROTOCOL; 819462306a36Sopenharmony_ci lease_change_type = OPLOCK_READ_TO_NONE; 819562306a36Sopenharmony_ci ksmbd_debug(OPLOCK, "handle bad lease state 0x%x -> 0x%x\n", 819662306a36Sopenharmony_ci le32_to_cpu(lease->state), 819762306a36Sopenharmony_ci le32_to_cpu(req->LeaseState)); 819862306a36Sopenharmony_ci } else { 819962306a36Sopenharmony_ci /* valid lease state changes */ 820062306a36Sopenharmony_ci err = STATUS_INVALID_DEVICE_STATE; 820162306a36Sopenharmony_ci if (req->LeaseState == SMB2_LEASE_NONE_LE) { 820262306a36Sopenharmony_ci if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) 820362306a36Sopenharmony_ci lease_change_type = OPLOCK_WRITE_TO_NONE; 820462306a36Sopenharmony_ci else 820562306a36Sopenharmony_ci lease_change_type = OPLOCK_READ_TO_NONE; 820662306a36Sopenharmony_ci } else if (req->LeaseState & SMB2_LEASE_READ_CACHING_LE) { 820762306a36Sopenharmony_ci if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) 820862306a36Sopenharmony_ci lease_change_type = OPLOCK_WRITE_TO_READ; 820962306a36Sopenharmony_ci else 821062306a36Sopenharmony_ci lease_change_type = OPLOCK_READ_HANDLE_TO_READ; 821162306a36Sopenharmony_ci } else { 821262306a36Sopenharmony_ci lease_change_type = 0; 821362306a36Sopenharmony_ci } 821462306a36Sopenharmony_ci } 821562306a36Sopenharmony_ci 821662306a36Sopenharmony_ci switch (lease_change_type) { 821762306a36Sopenharmony_ci case OPLOCK_WRITE_TO_READ: 821862306a36Sopenharmony_ci ret = opinfo_write_to_read(opinfo); 821962306a36Sopenharmony_ci break; 822062306a36Sopenharmony_ci case OPLOCK_READ_HANDLE_TO_READ: 822162306a36Sopenharmony_ci ret = opinfo_read_handle_to_read(opinfo); 822262306a36Sopenharmony_ci break; 822362306a36Sopenharmony_ci case OPLOCK_WRITE_TO_NONE: 822462306a36Sopenharmony_ci ret = opinfo_write_to_none(opinfo); 822562306a36Sopenharmony_ci break; 822662306a36Sopenharmony_ci case OPLOCK_READ_TO_NONE: 822762306a36Sopenharmony_ci ret = opinfo_read_to_none(opinfo); 822862306a36Sopenharmony_ci break; 822962306a36Sopenharmony_ci default: 823062306a36Sopenharmony_ci ksmbd_debug(OPLOCK, "unknown lease change 0x%x -> 0x%x\n", 823162306a36Sopenharmony_ci le32_to_cpu(lease->state), 823262306a36Sopenharmony_ci le32_to_cpu(req->LeaseState)); 823362306a36Sopenharmony_ci } 823462306a36Sopenharmony_ci 823562306a36Sopenharmony_ci if (ret < 0) { 823662306a36Sopenharmony_ci rsp->hdr.Status = err; 823762306a36Sopenharmony_ci goto err_out; 823862306a36Sopenharmony_ci } 823962306a36Sopenharmony_ci 824062306a36Sopenharmony_ci lease_state = lease->state; 824162306a36Sopenharmony_ci opinfo->op_state = OPLOCK_STATE_NONE; 824262306a36Sopenharmony_ci wake_up_interruptible_all(&opinfo->oplock_q); 824362306a36Sopenharmony_ci atomic_dec(&opinfo->breaking_cnt); 824462306a36Sopenharmony_ci wake_up_interruptible_all(&opinfo->oplock_brk); 824562306a36Sopenharmony_ci opinfo_put(opinfo); 824662306a36Sopenharmony_ci 824762306a36Sopenharmony_ci rsp->StructureSize = cpu_to_le16(36); 824862306a36Sopenharmony_ci rsp->Reserved = 0; 824962306a36Sopenharmony_ci rsp->Flags = 0; 825062306a36Sopenharmony_ci memcpy(rsp->LeaseKey, req->LeaseKey, 16); 825162306a36Sopenharmony_ci rsp->LeaseState = lease_state; 825262306a36Sopenharmony_ci rsp->LeaseDuration = 0; 825362306a36Sopenharmony_ci ret = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_lease_ack)); 825462306a36Sopenharmony_ci if (!ret) 825562306a36Sopenharmony_ci return; 825662306a36Sopenharmony_ci 825762306a36Sopenharmony_cierr_out: 825862306a36Sopenharmony_ci wake_up_interruptible_all(&opinfo->oplock_q); 825962306a36Sopenharmony_ci atomic_dec(&opinfo->breaking_cnt); 826062306a36Sopenharmony_ci wake_up_interruptible_all(&opinfo->oplock_brk); 826162306a36Sopenharmony_ci 826262306a36Sopenharmony_ci opinfo_put(opinfo); 826362306a36Sopenharmony_ci smb2_set_err_rsp(work); 826462306a36Sopenharmony_ci} 826562306a36Sopenharmony_ci 826662306a36Sopenharmony_ci/** 826762306a36Sopenharmony_ci * smb2_oplock_break() - dispatcher for smb2.0 and 2.1 oplock/lease break 826862306a36Sopenharmony_ci * @work: smb work containing oplock/lease break command buffer 826962306a36Sopenharmony_ci * 827062306a36Sopenharmony_ci * Return: 0 827162306a36Sopenharmony_ci */ 827262306a36Sopenharmony_ciint smb2_oplock_break(struct ksmbd_work *work) 827362306a36Sopenharmony_ci{ 827462306a36Sopenharmony_ci struct smb2_oplock_break *req; 827562306a36Sopenharmony_ci struct smb2_oplock_break *rsp; 827662306a36Sopenharmony_ci 827762306a36Sopenharmony_ci WORK_BUFFERS(work, req, rsp); 827862306a36Sopenharmony_ci 827962306a36Sopenharmony_ci switch (le16_to_cpu(req->StructureSize)) { 828062306a36Sopenharmony_ci case OP_BREAK_STRUCT_SIZE_20: 828162306a36Sopenharmony_ci smb20_oplock_break_ack(work); 828262306a36Sopenharmony_ci break; 828362306a36Sopenharmony_ci case OP_BREAK_STRUCT_SIZE_21: 828462306a36Sopenharmony_ci smb21_lease_break_ack(work); 828562306a36Sopenharmony_ci break; 828662306a36Sopenharmony_ci default: 828762306a36Sopenharmony_ci ksmbd_debug(OPLOCK, "invalid break cmd %d\n", 828862306a36Sopenharmony_ci le16_to_cpu(req->StructureSize)); 828962306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INVALID_PARAMETER; 829062306a36Sopenharmony_ci smb2_set_err_rsp(work); 829162306a36Sopenharmony_ci } 829262306a36Sopenharmony_ci 829362306a36Sopenharmony_ci return 0; 829462306a36Sopenharmony_ci} 829562306a36Sopenharmony_ci 829662306a36Sopenharmony_ci/** 829762306a36Sopenharmony_ci * smb2_notify() - handler for smb2 notify request 829862306a36Sopenharmony_ci * @work: smb work containing notify command buffer 829962306a36Sopenharmony_ci * 830062306a36Sopenharmony_ci * Return: 0 830162306a36Sopenharmony_ci */ 830262306a36Sopenharmony_ciint smb2_notify(struct ksmbd_work *work) 830362306a36Sopenharmony_ci{ 830462306a36Sopenharmony_ci struct smb2_change_notify_req *req; 830562306a36Sopenharmony_ci struct smb2_change_notify_rsp *rsp; 830662306a36Sopenharmony_ci 830762306a36Sopenharmony_ci WORK_BUFFERS(work, req, rsp); 830862306a36Sopenharmony_ci 830962306a36Sopenharmony_ci if (work->next_smb2_rcv_hdr_off && req->hdr.NextCommand) { 831062306a36Sopenharmony_ci rsp->hdr.Status = STATUS_INTERNAL_ERROR; 831162306a36Sopenharmony_ci smb2_set_err_rsp(work); 831262306a36Sopenharmony_ci return 0; 831362306a36Sopenharmony_ci } 831462306a36Sopenharmony_ci 831562306a36Sopenharmony_ci smb2_set_err_rsp(work); 831662306a36Sopenharmony_ci rsp->hdr.Status = STATUS_NOT_IMPLEMENTED; 831762306a36Sopenharmony_ci return 0; 831862306a36Sopenharmony_ci} 831962306a36Sopenharmony_ci 832062306a36Sopenharmony_ci/** 832162306a36Sopenharmony_ci * smb2_is_sign_req() - handler for checking packet signing status 832262306a36Sopenharmony_ci * @work: smb work containing notify command buffer 832362306a36Sopenharmony_ci * @command: SMB2 command id 832462306a36Sopenharmony_ci * 832562306a36Sopenharmony_ci * Return: true if packed is signed, false otherwise 832662306a36Sopenharmony_ci */ 832762306a36Sopenharmony_cibool smb2_is_sign_req(struct ksmbd_work *work, unsigned int command) 832862306a36Sopenharmony_ci{ 832962306a36Sopenharmony_ci struct smb2_hdr *rcv_hdr2 = smb2_get_msg(work->request_buf); 833062306a36Sopenharmony_ci 833162306a36Sopenharmony_ci if ((rcv_hdr2->Flags & SMB2_FLAGS_SIGNED) && 833262306a36Sopenharmony_ci command != SMB2_NEGOTIATE_HE && 833362306a36Sopenharmony_ci command != SMB2_SESSION_SETUP_HE && 833462306a36Sopenharmony_ci command != SMB2_OPLOCK_BREAK_HE) 833562306a36Sopenharmony_ci return true; 833662306a36Sopenharmony_ci 833762306a36Sopenharmony_ci return false; 833862306a36Sopenharmony_ci} 833962306a36Sopenharmony_ci 834062306a36Sopenharmony_ci/** 834162306a36Sopenharmony_ci * smb2_check_sign_req() - handler for req packet sign processing 834262306a36Sopenharmony_ci * @work: smb work containing notify command buffer 834362306a36Sopenharmony_ci * 834462306a36Sopenharmony_ci * Return: 1 on success, 0 otherwise 834562306a36Sopenharmony_ci */ 834662306a36Sopenharmony_ciint smb2_check_sign_req(struct ksmbd_work *work) 834762306a36Sopenharmony_ci{ 834862306a36Sopenharmony_ci struct smb2_hdr *hdr; 834962306a36Sopenharmony_ci char signature_req[SMB2_SIGNATURE_SIZE]; 835062306a36Sopenharmony_ci char signature[SMB2_HMACSHA256_SIZE]; 835162306a36Sopenharmony_ci struct kvec iov[1]; 835262306a36Sopenharmony_ci size_t len; 835362306a36Sopenharmony_ci 835462306a36Sopenharmony_ci hdr = smb2_get_msg(work->request_buf); 835562306a36Sopenharmony_ci if (work->next_smb2_rcv_hdr_off) 835662306a36Sopenharmony_ci hdr = ksmbd_req_buf_next(work); 835762306a36Sopenharmony_ci 835862306a36Sopenharmony_ci if (!hdr->NextCommand && !work->next_smb2_rcv_hdr_off) 835962306a36Sopenharmony_ci len = get_rfc1002_len(work->request_buf); 836062306a36Sopenharmony_ci else if (hdr->NextCommand) 836162306a36Sopenharmony_ci len = le32_to_cpu(hdr->NextCommand); 836262306a36Sopenharmony_ci else 836362306a36Sopenharmony_ci len = get_rfc1002_len(work->request_buf) - 836462306a36Sopenharmony_ci work->next_smb2_rcv_hdr_off; 836562306a36Sopenharmony_ci 836662306a36Sopenharmony_ci memcpy(signature_req, hdr->Signature, SMB2_SIGNATURE_SIZE); 836762306a36Sopenharmony_ci memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); 836862306a36Sopenharmony_ci 836962306a36Sopenharmony_ci iov[0].iov_base = (char *)&hdr->ProtocolId; 837062306a36Sopenharmony_ci iov[0].iov_len = len; 837162306a36Sopenharmony_ci 837262306a36Sopenharmony_ci if (ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, 1, 837362306a36Sopenharmony_ci signature)) 837462306a36Sopenharmony_ci return 0; 837562306a36Sopenharmony_ci 837662306a36Sopenharmony_ci if (memcmp(signature, signature_req, SMB2_SIGNATURE_SIZE)) { 837762306a36Sopenharmony_ci pr_err("bad smb2 signature\n"); 837862306a36Sopenharmony_ci return 0; 837962306a36Sopenharmony_ci } 838062306a36Sopenharmony_ci 838162306a36Sopenharmony_ci return 1; 838262306a36Sopenharmony_ci} 838362306a36Sopenharmony_ci 838462306a36Sopenharmony_ci/** 838562306a36Sopenharmony_ci * smb2_set_sign_rsp() - handler for rsp packet sign processing 838662306a36Sopenharmony_ci * @work: smb work containing notify command buffer 838762306a36Sopenharmony_ci * 838862306a36Sopenharmony_ci */ 838962306a36Sopenharmony_civoid smb2_set_sign_rsp(struct ksmbd_work *work) 839062306a36Sopenharmony_ci{ 839162306a36Sopenharmony_ci struct smb2_hdr *hdr; 839262306a36Sopenharmony_ci char signature[SMB2_HMACSHA256_SIZE]; 839362306a36Sopenharmony_ci struct kvec *iov; 839462306a36Sopenharmony_ci int n_vec = 1; 839562306a36Sopenharmony_ci 839662306a36Sopenharmony_ci hdr = ksmbd_resp_buf_curr(work); 839762306a36Sopenharmony_ci hdr->Flags |= SMB2_FLAGS_SIGNED; 839862306a36Sopenharmony_ci memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); 839962306a36Sopenharmony_ci 840062306a36Sopenharmony_ci if (hdr->Command == SMB2_READ) { 840162306a36Sopenharmony_ci iov = &work->iov[work->iov_idx - 1]; 840262306a36Sopenharmony_ci n_vec++; 840362306a36Sopenharmony_ci } else { 840462306a36Sopenharmony_ci iov = &work->iov[work->iov_idx]; 840562306a36Sopenharmony_ci } 840662306a36Sopenharmony_ci 840762306a36Sopenharmony_ci if (!ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, n_vec, 840862306a36Sopenharmony_ci signature)) 840962306a36Sopenharmony_ci memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE); 841062306a36Sopenharmony_ci} 841162306a36Sopenharmony_ci 841262306a36Sopenharmony_ci/** 841362306a36Sopenharmony_ci * smb3_check_sign_req() - handler for req packet sign processing 841462306a36Sopenharmony_ci * @work: smb work containing notify command buffer 841562306a36Sopenharmony_ci * 841662306a36Sopenharmony_ci * Return: 1 on success, 0 otherwise 841762306a36Sopenharmony_ci */ 841862306a36Sopenharmony_ciint smb3_check_sign_req(struct ksmbd_work *work) 841962306a36Sopenharmony_ci{ 842062306a36Sopenharmony_ci struct ksmbd_conn *conn = work->conn; 842162306a36Sopenharmony_ci char *signing_key; 842262306a36Sopenharmony_ci struct smb2_hdr *hdr; 842362306a36Sopenharmony_ci struct channel *chann; 842462306a36Sopenharmony_ci char signature_req[SMB2_SIGNATURE_SIZE]; 842562306a36Sopenharmony_ci char signature[SMB2_CMACAES_SIZE]; 842662306a36Sopenharmony_ci struct kvec iov[1]; 842762306a36Sopenharmony_ci size_t len; 842862306a36Sopenharmony_ci 842962306a36Sopenharmony_ci hdr = smb2_get_msg(work->request_buf); 843062306a36Sopenharmony_ci if (work->next_smb2_rcv_hdr_off) 843162306a36Sopenharmony_ci hdr = ksmbd_req_buf_next(work); 843262306a36Sopenharmony_ci 843362306a36Sopenharmony_ci if (!hdr->NextCommand && !work->next_smb2_rcv_hdr_off) 843462306a36Sopenharmony_ci len = get_rfc1002_len(work->request_buf); 843562306a36Sopenharmony_ci else if (hdr->NextCommand) 843662306a36Sopenharmony_ci len = le32_to_cpu(hdr->NextCommand); 843762306a36Sopenharmony_ci else 843862306a36Sopenharmony_ci len = get_rfc1002_len(work->request_buf) - 843962306a36Sopenharmony_ci work->next_smb2_rcv_hdr_off; 844062306a36Sopenharmony_ci 844162306a36Sopenharmony_ci if (le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) { 844262306a36Sopenharmony_ci signing_key = work->sess->smb3signingkey; 844362306a36Sopenharmony_ci } else { 844462306a36Sopenharmony_ci chann = lookup_chann_list(work->sess, conn); 844562306a36Sopenharmony_ci if (!chann) { 844662306a36Sopenharmony_ci return 0; 844762306a36Sopenharmony_ci } 844862306a36Sopenharmony_ci signing_key = chann->smb3signingkey; 844962306a36Sopenharmony_ci } 845062306a36Sopenharmony_ci 845162306a36Sopenharmony_ci if (!signing_key) { 845262306a36Sopenharmony_ci pr_err("SMB3 signing key is not generated\n"); 845362306a36Sopenharmony_ci return 0; 845462306a36Sopenharmony_ci } 845562306a36Sopenharmony_ci 845662306a36Sopenharmony_ci memcpy(signature_req, hdr->Signature, SMB2_SIGNATURE_SIZE); 845762306a36Sopenharmony_ci memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); 845862306a36Sopenharmony_ci iov[0].iov_base = (char *)&hdr->ProtocolId; 845962306a36Sopenharmony_ci iov[0].iov_len = len; 846062306a36Sopenharmony_ci 846162306a36Sopenharmony_ci if (ksmbd_sign_smb3_pdu(conn, signing_key, iov, 1, signature)) 846262306a36Sopenharmony_ci return 0; 846362306a36Sopenharmony_ci 846462306a36Sopenharmony_ci if (memcmp(signature, signature_req, SMB2_SIGNATURE_SIZE)) { 846562306a36Sopenharmony_ci pr_err("bad smb2 signature\n"); 846662306a36Sopenharmony_ci return 0; 846762306a36Sopenharmony_ci } 846862306a36Sopenharmony_ci 846962306a36Sopenharmony_ci return 1; 847062306a36Sopenharmony_ci} 847162306a36Sopenharmony_ci 847262306a36Sopenharmony_ci/** 847362306a36Sopenharmony_ci * smb3_set_sign_rsp() - handler for rsp packet sign processing 847462306a36Sopenharmony_ci * @work: smb work containing notify command buffer 847562306a36Sopenharmony_ci * 847662306a36Sopenharmony_ci */ 847762306a36Sopenharmony_civoid smb3_set_sign_rsp(struct ksmbd_work *work) 847862306a36Sopenharmony_ci{ 847962306a36Sopenharmony_ci struct ksmbd_conn *conn = work->conn; 848062306a36Sopenharmony_ci struct smb2_hdr *hdr; 848162306a36Sopenharmony_ci struct channel *chann; 848262306a36Sopenharmony_ci char signature[SMB2_CMACAES_SIZE]; 848362306a36Sopenharmony_ci struct kvec *iov; 848462306a36Sopenharmony_ci int n_vec = 1; 848562306a36Sopenharmony_ci char *signing_key; 848662306a36Sopenharmony_ci 848762306a36Sopenharmony_ci hdr = ksmbd_resp_buf_curr(work); 848862306a36Sopenharmony_ci 848962306a36Sopenharmony_ci if (conn->binding == false && 849062306a36Sopenharmony_ci le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) { 849162306a36Sopenharmony_ci signing_key = work->sess->smb3signingkey; 849262306a36Sopenharmony_ci } else { 849362306a36Sopenharmony_ci chann = lookup_chann_list(work->sess, work->conn); 849462306a36Sopenharmony_ci if (!chann) { 849562306a36Sopenharmony_ci return; 849662306a36Sopenharmony_ci } 849762306a36Sopenharmony_ci signing_key = chann->smb3signingkey; 849862306a36Sopenharmony_ci } 849962306a36Sopenharmony_ci 850062306a36Sopenharmony_ci if (!signing_key) 850162306a36Sopenharmony_ci return; 850262306a36Sopenharmony_ci 850362306a36Sopenharmony_ci hdr->Flags |= SMB2_FLAGS_SIGNED; 850462306a36Sopenharmony_ci memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); 850562306a36Sopenharmony_ci 850662306a36Sopenharmony_ci if (hdr->Command == SMB2_READ) { 850762306a36Sopenharmony_ci iov = &work->iov[work->iov_idx - 1]; 850862306a36Sopenharmony_ci n_vec++; 850962306a36Sopenharmony_ci } else { 851062306a36Sopenharmony_ci iov = &work->iov[work->iov_idx]; 851162306a36Sopenharmony_ci } 851262306a36Sopenharmony_ci 851362306a36Sopenharmony_ci if (!ksmbd_sign_smb3_pdu(conn, signing_key, iov, n_vec, 851462306a36Sopenharmony_ci signature)) 851562306a36Sopenharmony_ci memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE); 851662306a36Sopenharmony_ci} 851762306a36Sopenharmony_ci 851862306a36Sopenharmony_ci/** 851962306a36Sopenharmony_ci * smb3_preauth_hash_rsp() - handler for computing preauth hash on response 852062306a36Sopenharmony_ci * @work: smb work containing response buffer 852162306a36Sopenharmony_ci * 852262306a36Sopenharmony_ci */ 852362306a36Sopenharmony_civoid smb3_preauth_hash_rsp(struct ksmbd_work *work) 852462306a36Sopenharmony_ci{ 852562306a36Sopenharmony_ci struct ksmbd_conn *conn = work->conn; 852662306a36Sopenharmony_ci struct ksmbd_session *sess = work->sess; 852762306a36Sopenharmony_ci struct smb2_hdr *req, *rsp; 852862306a36Sopenharmony_ci 852962306a36Sopenharmony_ci if (conn->dialect != SMB311_PROT_ID) 853062306a36Sopenharmony_ci return; 853162306a36Sopenharmony_ci 853262306a36Sopenharmony_ci WORK_BUFFERS(work, req, rsp); 853362306a36Sopenharmony_ci 853462306a36Sopenharmony_ci if (le16_to_cpu(req->Command) == SMB2_NEGOTIATE_HE && 853562306a36Sopenharmony_ci conn->preauth_info) 853662306a36Sopenharmony_ci ksmbd_gen_preauth_integrity_hash(conn, work->response_buf, 853762306a36Sopenharmony_ci conn->preauth_info->Preauth_HashValue); 853862306a36Sopenharmony_ci 853962306a36Sopenharmony_ci if (le16_to_cpu(rsp->Command) == SMB2_SESSION_SETUP_HE && sess) { 854062306a36Sopenharmony_ci __u8 *hash_value; 854162306a36Sopenharmony_ci 854262306a36Sopenharmony_ci if (conn->binding) { 854362306a36Sopenharmony_ci struct preauth_session *preauth_sess; 854462306a36Sopenharmony_ci 854562306a36Sopenharmony_ci preauth_sess = ksmbd_preauth_session_lookup(conn, sess->id); 854662306a36Sopenharmony_ci if (!preauth_sess) 854762306a36Sopenharmony_ci return; 854862306a36Sopenharmony_ci hash_value = preauth_sess->Preauth_HashValue; 854962306a36Sopenharmony_ci } else { 855062306a36Sopenharmony_ci hash_value = sess->Preauth_HashValue; 855162306a36Sopenharmony_ci if (!hash_value) 855262306a36Sopenharmony_ci return; 855362306a36Sopenharmony_ci } 855462306a36Sopenharmony_ci ksmbd_gen_preauth_integrity_hash(conn, work->response_buf, 855562306a36Sopenharmony_ci hash_value); 855662306a36Sopenharmony_ci } 855762306a36Sopenharmony_ci} 855862306a36Sopenharmony_ci 855962306a36Sopenharmony_cistatic void fill_transform_hdr(void *tr_buf, char *old_buf, __le16 cipher_type) 856062306a36Sopenharmony_ci{ 856162306a36Sopenharmony_ci struct smb2_transform_hdr *tr_hdr = tr_buf + 4; 856262306a36Sopenharmony_ci struct smb2_hdr *hdr = smb2_get_msg(old_buf); 856362306a36Sopenharmony_ci unsigned int orig_len = get_rfc1002_len(old_buf); 856462306a36Sopenharmony_ci 856562306a36Sopenharmony_ci /* tr_buf must be cleared by the caller */ 856662306a36Sopenharmony_ci tr_hdr->ProtocolId = SMB2_TRANSFORM_PROTO_NUM; 856762306a36Sopenharmony_ci tr_hdr->OriginalMessageSize = cpu_to_le32(orig_len); 856862306a36Sopenharmony_ci tr_hdr->Flags = cpu_to_le16(TRANSFORM_FLAG_ENCRYPTED); 856962306a36Sopenharmony_ci if (cipher_type == SMB2_ENCRYPTION_AES128_GCM || 857062306a36Sopenharmony_ci cipher_type == SMB2_ENCRYPTION_AES256_GCM) 857162306a36Sopenharmony_ci get_random_bytes(&tr_hdr->Nonce, SMB3_AES_GCM_NONCE); 857262306a36Sopenharmony_ci else 857362306a36Sopenharmony_ci get_random_bytes(&tr_hdr->Nonce, SMB3_AES_CCM_NONCE); 857462306a36Sopenharmony_ci memcpy(&tr_hdr->SessionId, &hdr->SessionId, 8); 857562306a36Sopenharmony_ci inc_rfc1001_len(tr_buf, sizeof(struct smb2_transform_hdr)); 857662306a36Sopenharmony_ci inc_rfc1001_len(tr_buf, orig_len); 857762306a36Sopenharmony_ci} 857862306a36Sopenharmony_ci 857962306a36Sopenharmony_ciint smb3_encrypt_resp(struct ksmbd_work *work) 858062306a36Sopenharmony_ci{ 858162306a36Sopenharmony_ci struct kvec *iov = work->iov; 858262306a36Sopenharmony_ci int rc = -ENOMEM; 858362306a36Sopenharmony_ci void *tr_buf; 858462306a36Sopenharmony_ci 858562306a36Sopenharmony_ci tr_buf = kzalloc(sizeof(struct smb2_transform_hdr) + 4, GFP_KERNEL); 858662306a36Sopenharmony_ci if (!tr_buf) 858762306a36Sopenharmony_ci return rc; 858862306a36Sopenharmony_ci 858962306a36Sopenharmony_ci /* fill transform header */ 859062306a36Sopenharmony_ci fill_transform_hdr(tr_buf, work->response_buf, work->conn->cipher_type); 859162306a36Sopenharmony_ci 859262306a36Sopenharmony_ci iov[0].iov_base = tr_buf; 859362306a36Sopenharmony_ci iov[0].iov_len = sizeof(struct smb2_transform_hdr) + 4; 859462306a36Sopenharmony_ci work->tr_buf = tr_buf; 859562306a36Sopenharmony_ci 859662306a36Sopenharmony_ci return ksmbd_crypt_message(work, iov, work->iov_idx + 1, 1); 859762306a36Sopenharmony_ci} 859862306a36Sopenharmony_ci 859962306a36Sopenharmony_cibool smb3_is_transform_hdr(void *buf) 860062306a36Sopenharmony_ci{ 860162306a36Sopenharmony_ci struct smb2_transform_hdr *trhdr = smb2_get_msg(buf); 860262306a36Sopenharmony_ci 860362306a36Sopenharmony_ci return trhdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM; 860462306a36Sopenharmony_ci} 860562306a36Sopenharmony_ci 860662306a36Sopenharmony_ciint smb3_decrypt_req(struct ksmbd_work *work) 860762306a36Sopenharmony_ci{ 860862306a36Sopenharmony_ci struct ksmbd_session *sess; 860962306a36Sopenharmony_ci char *buf = work->request_buf; 861062306a36Sopenharmony_ci unsigned int pdu_length = get_rfc1002_len(buf); 861162306a36Sopenharmony_ci struct kvec iov[2]; 861262306a36Sopenharmony_ci int buf_data_size = pdu_length - sizeof(struct smb2_transform_hdr); 861362306a36Sopenharmony_ci struct smb2_transform_hdr *tr_hdr = smb2_get_msg(buf); 861462306a36Sopenharmony_ci int rc = 0; 861562306a36Sopenharmony_ci 861662306a36Sopenharmony_ci if (pdu_length < sizeof(struct smb2_transform_hdr) || 861762306a36Sopenharmony_ci buf_data_size < sizeof(struct smb2_hdr)) { 861862306a36Sopenharmony_ci pr_err("Transform message is too small (%u)\n", 861962306a36Sopenharmony_ci pdu_length); 862062306a36Sopenharmony_ci return -ECONNABORTED; 862162306a36Sopenharmony_ci } 862262306a36Sopenharmony_ci 862362306a36Sopenharmony_ci if (buf_data_size < le32_to_cpu(tr_hdr->OriginalMessageSize)) { 862462306a36Sopenharmony_ci pr_err("Transform message is broken\n"); 862562306a36Sopenharmony_ci return -ECONNABORTED; 862662306a36Sopenharmony_ci } 862762306a36Sopenharmony_ci 862862306a36Sopenharmony_ci sess = ksmbd_session_lookup_all(work->conn, le64_to_cpu(tr_hdr->SessionId)); 862962306a36Sopenharmony_ci if (!sess) { 863062306a36Sopenharmony_ci pr_err("invalid session id(%llx) in transform header\n", 863162306a36Sopenharmony_ci le64_to_cpu(tr_hdr->SessionId)); 863262306a36Sopenharmony_ci return -ECONNABORTED; 863362306a36Sopenharmony_ci } 863462306a36Sopenharmony_ci 863562306a36Sopenharmony_ci iov[0].iov_base = buf; 863662306a36Sopenharmony_ci iov[0].iov_len = sizeof(struct smb2_transform_hdr) + 4; 863762306a36Sopenharmony_ci iov[1].iov_base = buf + sizeof(struct smb2_transform_hdr) + 4; 863862306a36Sopenharmony_ci iov[1].iov_len = buf_data_size; 863962306a36Sopenharmony_ci rc = ksmbd_crypt_message(work, iov, 2, 0); 864062306a36Sopenharmony_ci if (rc) 864162306a36Sopenharmony_ci return rc; 864262306a36Sopenharmony_ci 864362306a36Sopenharmony_ci memmove(buf + 4, iov[1].iov_base, buf_data_size); 864462306a36Sopenharmony_ci *(__be32 *)buf = cpu_to_be32(buf_data_size); 864562306a36Sopenharmony_ci 864662306a36Sopenharmony_ci return rc; 864762306a36Sopenharmony_ci} 864862306a36Sopenharmony_ci 864962306a36Sopenharmony_cibool smb3_11_final_sess_setup_resp(struct ksmbd_work *work) 865062306a36Sopenharmony_ci{ 865162306a36Sopenharmony_ci struct ksmbd_conn *conn = work->conn; 865262306a36Sopenharmony_ci struct ksmbd_session *sess = work->sess; 865362306a36Sopenharmony_ci struct smb2_hdr *rsp = smb2_get_msg(work->response_buf); 865462306a36Sopenharmony_ci 865562306a36Sopenharmony_ci if (conn->dialect < SMB30_PROT_ID) 865662306a36Sopenharmony_ci return false; 865762306a36Sopenharmony_ci 865862306a36Sopenharmony_ci if (work->next_smb2_rcv_hdr_off) 865962306a36Sopenharmony_ci rsp = ksmbd_resp_buf_next(work); 866062306a36Sopenharmony_ci 866162306a36Sopenharmony_ci if (le16_to_cpu(rsp->Command) == SMB2_SESSION_SETUP_HE && 866262306a36Sopenharmony_ci sess->user && !user_guest(sess->user) && 866362306a36Sopenharmony_ci rsp->Status == STATUS_SUCCESS) 866462306a36Sopenharmony_ci return true; 866562306a36Sopenharmony_ci return false; 866662306a36Sopenharmony_ci} 8667