diff options
Diffstat (limited to 'fs/smb/server/smb2pdu.c')
| -rw-r--r-- | fs/smb/server/smb2pdu.c | 849 |
1 files changed, 512 insertions, 337 deletions
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 840c71c66b30..f3184b217575 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -23,6 +23,7 @@ #include "asn1.h" #include "connection.h" #include "transport_ipc.h" +#include "../common/smbdirect/smbdirect.h" #include "transport_rdma.h" #include "vfs.h" #include "vfs_cache.h" @@ -30,7 +31,7 @@ #include "server.h" #include "smb_common.h" -#include "smbstatus.h" +#include "../common/smb2status.h" #include "ksmbd_work.h" #include "mgmt/user_config.h" #include "mgmt/share_config.h" @@ -38,6 +39,7 @@ #include "mgmt/user_session.h" #include "mgmt/ksmbd_ida.h" #include "ndr.h" +#include "transport_tcp.h" static void __wbuf(struct ksmbd_work *work, void **req, void **rsp) { @@ -67,8 +69,10 @@ static inline bool check_session_id(struct ksmbd_conn *conn, u64 id) return false; sess = ksmbd_session_lookup_all(conn, id); - if (sess) + if (sess) { + ksmbd_user_session_put(sess); return true; + } pr_err("Invalid user session id: %llu\n", id); return false; } @@ -278,7 +282,7 @@ int init_smb2_neg_rsp(struct ksmbd_work *work) /* Not setting conn guid rsp->ServerGUID, as it * not used by client for identifying connection */ - rsp->Capabilities = cpu_to_le32(conn->vals->capabilities); + rsp->Capabilities = cpu_to_le32(conn->vals->req_capabilities); /* Default Max Message Size till SMB2.0, 64K*/ rsp->MaxTransactSize = cpu_to_le32(conn->vals->max_trans_size); rsp->MaxReadSize = cpu_to_le32(conn->vals->max_read_size); @@ -519,7 +523,7 @@ int init_smb2_rsp_hdr(struct ksmbd_work *work) * smb2_allocate_rsp_buf() - allocate smb2 response buffer * @work: smb work containing smb request buffer * - * Return: 0 on success, otherwise -ENOMEM + * Return: 0 on success, otherwise error */ int smb2_allocate_rsp_buf(struct ksmbd_work *work) { @@ -551,7 +555,7 @@ int smb2_allocate_rsp_buf(struct ksmbd_work *work) if (le32_to_cpu(hdr->NextCommand) > 0) sz = large_sz; - work->response_buf = kvzalloc(sz, GFP_KERNEL); + work->response_buf = kvzalloc(sz, KSMBD_DEFAULT_GFP); if (!work->response_buf) return -ENOMEM; @@ -630,6 +634,11 @@ smb2_get_name(const char *src, const int maxlen, struct nls_table *local_nls) return name; } + if (*name == '\0') { + kfree(name); + return ERR_PTR(-EINVAL); + } + if (*name == '\\') { pr_err("not allow directory name included leading slash\n"); kfree(name); @@ -693,6 +702,9 @@ void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status) struct smb2_hdr *rsp_hdr; struct ksmbd_work *in_work = ksmbd_alloc_work_struct(); + if (!in_work) + return; + if (allocate_interim_rsp_buf(in_work)) { pr_err("smb_allocate_rsp_buf failed!\n"); ksmbd_free_work_struct(in_work); @@ -884,7 +896,7 @@ static __le32 decode_preauth_ctxt(struct ksmbd_conn *conn, return STATUS_INVALID_PARAMETER; if (pneg_ctxt->HashAlgorithms != SMB2_PREAUTH_INTEGRITY_SHA512) - return STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP; + return STATUS_SMB_NO_PREAUTH_INTEGRITY_HASH_OVERLAP; conn->preauth_info->Preauth_HashId = SMB2_PREAUTH_INTEGRITY_SHA512; return STATUS_SUCCESS; @@ -944,7 +956,7 @@ bool smb3_encryption_negotiated(struct ksmbd_conn *conn) * SMB 3.0 and 3.0.2 dialects use the SMB2_GLOBAL_CAP_ENCRYPTION flag. * SMB 3.1.1 uses the cipher_type field. */ - return (conn->vals->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION) || + return (conn->vals->req_capabilities & SMB2_GLOBAL_CAP_ENCRYPTION) || conn->cipher_type; } @@ -1095,6 +1107,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work) return rc; } + ksmbd_conn_lock(conn); smb2_buf_len = get_rfc1002_len(work->request_buf); smb2_neg_size = offsetof(struct smb2_negotiate_req, Dialects); if (smb2_neg_size > smb2_buf_len) { @@ -1145,7 +1158,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work) case SMB311_PROT_ID: conn->preauth_info = kzalloc(sizeof(struct preauth_integrity_info), - GFP_KERNEL); + KSMBD_DEFAULT_GFP); if (!conn->preauth_info) { rc = -ENOMEM; rsp->hdr.Status = STATUS_INVALID_PARAMETER; @@ -1197,7 +1210,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work) rc = -EINVAL; goto err_out; } - rsp->Capabilities = cpu_to_le32(conn->vals->capabilities); + rsp->Capabilities = cpu_to_le32(conn->vals->req_capabilities); /* For stats */ conn->connection_type = conn->dialect; @@ -1242,9 +1255,10 @@ int smb2_handle_negotiate(struct ksmbd_work *work) } conn->srv_sec_mode = le16_to_cpu(rsp->SecurityMode); - ksmbd_conn_set_need_negotiate(conn); + ksmbd_conn_set_need_setup(conn); err_out: + ksmbd_conn_unlock(conn); if (rc) rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; @@ -1263,8 +1277,11 @@ static int alloc_preauth_hash(struct ksmbd_session *sess, if (sess->Preauth_HashValue) return 0; + if (!conn->preauth_info) + return -ENOMEM; + sess->Preauth_HashValue = kmemdup(conn->preauth_info->Preauth_HashValue, - PREAUTH_HASHVALUE_SIZE, GFP_KERNEL); + PREAUTH_HASHVALUE_SIZE, KSMBD_DEFAULT_GFP); if (!sess->Preauth_HashValue) return -ENOMEM; @@ -1335,8 +1352,7 @@ static int ntlm_negotiate(struct ksmbd_work *work, return rc; sz = le16_to_cpu(rsp->SecurityBufferOffset); - chgblob = - (struct challenge_message *)((char *)&rsp->hdr.ProtocolId + sz); + chgblob = (struct challenge_message *)rsp->Buffer; memset(chgblob, 0, sizeof(struct challenge_message)); if (!work->conn->use_spnego) { @@ -1351,7 +1367,7 @@ static int ntlm_negotiate(struct ksmbd_work *work, sz = sizeof(struct challenge_message); sz += (strlen(ksmbd_netbios_name()) * 2 + 1 + 4) * 6; - neg_blob = kzalloc(sz, GFP_KERNEL); + neg_blob = kzalloc(sz, KSMBD_DEFAULT_GFP); if (!neg_blob) return -ENOMEM; @@ -1369,8 +1385,7 @@ static int ntlm_negotiate(struct ksmbd_work *work, goto out; } - sz = le16_to_cpu(rsp->SecurityBufferOffset); - memcpy((char *)&rsp->hdr.ProtocolId + sz, spnego_blob, spnego_blob_len); + memcpy(rsp->Buffer, spnego_blob, spnego_blob_len); rsp->SecurityBufferLength = cpu_to_le16(spnego_blob_len); out: @@ -1436,7 +1451,7 @@ static int ntlm_authenticate(struct ksmbd_work *work, { struct ksmbd_conn *conn = work->conn; struct ksmbd_session *sess = work->sess; - struct channel *chann = NULL; + struct channel *chann = NULL, *old; struct ksmbd_user *user; u64 prev_id; int sz, rc; @@ -1452,8 +1467,7 @@ static int ntlm_authenticate(struct ksmbd_work *work, if (rc) return -ENOMEM; - sz = le16_to_cpu(rsp->SecurityBufferOffset); - memcpy((char *)&rsp->hdr.ProtocolId + sz, spnego_blob, spnego_blob_len); + memcpy(rsp->Buffer, spnego_blob, spnego_blob_len); rsp->SecurityBufferLength = cpu_to_le16(spnego_blob_len); kfree(spnego_blob); } @@ -1524,12 +1538,7 @@ static int ntlm_authenticate(struct ksmbd_work *work, if (smb3_encryption_negotiated(conn) && !(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) { - rc = conn->ops->generate_encryptionkey(conn, sess); - if (rc) { - ksmbd_debug(SMB, - "SMB3 encryption key generation failed\n"); - return -EINVAL; - } + conn->ops->generate_encryptionkey(conn, sess); sess->enc = true; if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION) rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE; @@ -1544,12 +1553,17 @@ binding_session: if (conn->dialect >= SMB30_PROT_ID) { chann = lookup_chann_list(sess, conn); if (!chann) { - chann = kmalloc(sizeof(struct channel), GFP_KERNEL); + chann = kmalloc(sizeof(struct channel), KSMBD_DEFAULT_GFP); if (!chann) return -ENOMEM; chann->conn = conn; - xa_store(&sess->ksmbd_chann_list, (long)conn, chann, GFP_KERNEL); + old = xa_store(&sess->ksmbd_chann_list, (long)conn, chann, + KSMBD_DEFAULT_GFP); + if (xa_is_err(old)) { + kfree(chann); + return xa_err(old); + } } } @@ -1576,7 +1590,7 @@ static int krb5_authenticate(struct ksmbd_work *work, struct ksmbd_conn *conn = work->conn; struct ksmbd_session *sess = work->sess; char *in_blob, *out_blob; - struct channel *chann = NULL; + struct channel *chann = NULL, *old; u64 prev_sess_id; int in_len, out_len; int retval; @@ -1589,48 +1603,60 @@ static int krb5_authenticate(struct ksmbd_work *work, out_len = work->response_sz - (le16_to_cpu(rsp->SecurityBufferOffset) + 4); - /* Check previous session */ - prev_sess_id = le64_to_cpu(req->PreviousSessionId); - if (prev_sess_id && prev_sess_id != sess->id) - destroy_previous_session(conn, sess->user, prev_sess_id); - - if (sess->state == SMB2_SESSION_VALID) - ksmbd_free_user(sess->user); - retval = ksmbd_krb5_authenticate(sess, in_blob, in_len, out_blob, &out_len); if (retval) { ksmbd_debug(SMB, "krb5 authentication failed\n"); return -EINVAL; } + + /* Check previous session */ + prev_sess_id = le64_to_cpu(req->PreviousSessionId); + if (prev_sess_id && prev_sess_id != sess->id) + destroy_previous_session(conn, sess->user, prev_sess_id); + rsp->SecurityBufferLength = cpu_to_le16(out_len); - if ((conn->sign || server_conf.enforced_signing) || + /* + * If session state is SMB2_SESSION_VALID, We can assume + * that it is reauthentication. And the user/password + * has been verified, so return it here. + */ + if (sess->state == SMB2_SESSION_VALID) { + if (conn->binding) + goto binding_session; + return 0; + } + + if ((rsp->SessionFlags != SMB2_SESSION_FLAG_IS_GUEST_LE && + (conn->sign || server_conf.enforced_signing)) || (req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED)) sess->sign = true; - if (smb3_encryption_negotiated(conn)) { - retval = conn->ops->generate_encryptionkey(conn, sess); - if (retval) { - ksmbd_debug(SMB, - "SMB3 encryption key generation failed\n"); - return -EINVAL; - } + if (smb3_encryption_negotiated(conn) && + !(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) { + conn->ops->generate_encryptionkey(conn, sess); sess->enc = true; if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION) rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE; sess->sign = false; } +binding_session: if (conn->dialect >= SMB30_PROT_ID) { chann = lookup_chann_list(sess, conn); if (!chann) { - chann = kmalloc(sizeof(struct channel), GFP_KERNEL); + chann = kmalloc(sizeof(struct channel), KSMBD_DEFAULT_GFP); if (!chann) return -ENOMEM; chann->conn = conn; - xa_store(&sess->ksmbd_chann_list, (long)conn, chann, GFP_KERNEL); + old = xa_store(&sess->ksmbd_chann_list, (long)conn, + chann, KSMBD_DEFAULT_GFP); + if (xa_is_err(old)) { + kfree(chann); + return xa_err(old); + } } } @@ -1667,7 +1693,12 @@ int smb2_sess_setup(struct ksmbd_work *work) unsigned int negblob_len, negblob_off; int rc = 0; - ksmbd_debug(SMB, "Received request for session setup\n"); + ksmbd_debug(SMB, "Received smb2 session setup request\n"); + + if (!ksmbd_conn_need_setup(conn) && !ksmbd_conn_good(conn)) { + work->send_no_response = 1; + return rc; + } WORK_BUFFERS(work, req, rsp); @@ -1687,6 +1718,8 @@ int smb2_sess_setup(struct ksmbd_work *work) rc = ksmbd_session_register(conn, sess); if (rc) goto out_err; + + conn->binding = false; } else if (conn->dialect >= SMB30_PROT_ID && (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) && req->Flags & SMB2_SESSION_REQ_FLAG_BINDING) { @@ -1726,11 +1759,12 @@ int smb2_sess_setup(struct ksmbd_work *work) if (ksmbd_conn_need_reconnect(conn)) { rc = -EFAULT; + ksmbd_user_session_put(sess); sess = NULL; goto out_err; } - if (ksmbd_session_lookup(conn, sess_id)) { + if (is_ksmbd_session_in_connection(conn, sess_id)) { rc = -EACCES; goto out_err; } @@ -1762,9 +1796,12 @@ int smb2_sess_setup(struct ksmbd_work *work) if (ksmbd_conn_need_reconnect(conn)) { rc = -EFAULT; + ksmbd_user_session_put(sess); sess = NULL; goto out_err; } + + conn->binding = false; } work->sess = sess; @@ -1807,8 +1844,6 @@ int smb2_sess_setup(struct ksmbd_work *work) ksmbd_conn_set_good(conn); sess->state = SMB2_SESSION_VALID; } - kfree(sess->Preauth_HashValue); - sess->Preauth_HashValue = NULL; } else if (conn->preferred_auth_mech == KSMBD_AUTH_NTLMSSP) { if (negblob->MessageType == NtLmNegotiate) { rc = ntlm_negotiate(work, negblob, negblob_len, rsp); @@ -1835,8 +1870,6 @@ int smb2_sess_setup(struct ksmbd_work *work) kfree(preauth_sess); } } - kfree(sess->Preauth_HashValue); - sess->Preauth_HashValue = NULL; } else { pr_info_ratelimited("Unknown NTLMSSP message type : 0x%x\n", le32_to_cpu(negblob->MessageType)); @@ -1894,10 +1927,12 @@ out_err: sess->last_active = jiffies; sess->state = SMB2_SESSION_EXPIRED; + ksmbd_user_session_put(sess); + work->sess = NULL; if (try_delay) { ksmbd_conn_set_need_reconnect(conn); ssleep(5); - ksmbd_conn_set_need_negotiate(conn); + ksmbd_conn_set_need_setup(conn); } } smb2_set_err_rsp(work); @@ -1935,6 +1970,8 @@ int smb2_tree_connect(struct ksmbd_work *work) struct ksmbd_share_config *share = NULL; int rc = -EINVAL; + ksmbd_debug(SMB, "Received smb2 tree connect request\n"); + WORK_BUFFERS(work, req, rsp); treename = smb_strndup_from_utf16((char *)req + le16_to_cpu(req->PathOffset), @@ -1955,7 +1992,7 @@ int smb2_tree_connect(struct ksmbd_work *work) ksmbd_debug(SMB, "tree connect request for tree %s treename %s\n", name, treename); - status = ksmbd_tree_conn_connect(conn, sess, name); + status = ksmbd_tree_conn_connect(work, name); if (status.ret == KSMBD_TREE_CONN_STATUS_OK) rsp->hdr.Id.SyncId.TreeId = cpu_to_le32(status.tree_conn->id); else @@ -2051,18 +2088,20 @@ out_err1: * @access: file access flags * @disposition: file disposition flags * @may_flags: set with MAY_ flags - * @is_dir: is creating open flags for directory + * @coptions: file creation options + * @mode: file mode * * Return: file open flags */ static int smb2_create_open_flags(bool file_present, __le32 access, __le32 disposition, int *may_flags, - bool is_dir) + __le32 coptions, + umode_t mode) { int oflags = O_NONBLOCK | O_LARGEFILE; - if (is_dir) { + if (coptions & FILE_DIRECTORY_FILE_LE || S_ISDIR(mode)) { access &= ~FILE_WRITE_DESIRE_ACCESS_LE; ksmbd_debug(SMB, "Discard write access to a directory\n"); } @@ -2079,7 +2118,7 @@ static int smb2_create_open_flags(bool file_present, __le32 access, *may_flags = MAY_OPEN | MAY_READ; } - if (access == FILE_READ_ATTRIBUTES_LE) + if (access == FILE_READ_ATTRIBUTES_LE || S_ISBLK(mode) || S_ISCHR(mode)) oflags |= O_PATH; if (file_present) { @@ -2119,7 +2158,7 @@ static int smb2_create_open_flags(bool file_present, __le32 access, * smb2_tree_disconnect() - handler for smb tree connect request * @work: smb work containing request buffer * - * Return: 0 + * Return: 0 on success, otherwise error */ int smb2_tree_disconnect(struct ksmbd_work *work) { @@ -2129,9 +2168,9 @@ int smb2_tree_disconnect(struct ksmbd_work *work) struct ksmbd_tree_connect *tcon = work->tcon; int err; - WORK_BUFFERS(work, req, rsp); + ksmbd_debug(SMB, "Received smb2 tree disconnect request\n"); - ksmbd_debug(SMB, "request\n"); + WORK_BUFFERS(work, req, rsp); if (!tcon) { ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); @@ -2151,7 +2190,6 @@ int smb2_tree_disconnect(struct ksmbd_work *work) goto err_out; } - WARN_ON_ONCE(atomic_dec_and_test(&tcon->refcount)); tcon->t_state = TREE_DISCONNECTED; write_unlock(&sess->tree_conns_lock); @@ -2161,8 +2199,6 @@ int smb2_tree_disconnect(struct ksmbd_work *work) goto err_out; } - work->tcon = NULL; - rsp->StructureSize = cpu_to_le16(4); err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_tree_disconnect_rsp)); @@ -2183,20 +2219,20 @@ err_out: * smb2_session_logoff() - handler for session log off request * @work: smb work containing request buffer * - * Return: 0 + * Return: 0 on success, otherwise error */ int smb2_session_logoff(struct ksmbd_work *work) { struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = work->sess; struct smb2_logoff_req *req; struct smb2_logoff_rsp *rsp; - struct ksmbd_session *sess; u64 sess_id; int err; WORK_BUFFERS(work, req, rsp); - ksmbd_debug(SMB, "request\n"); + ksmbd_debug(SMB, "Received smb2 session logoff request\n"); ksmbd_conn_lock(conn); if (!ksmbd_conn_good(conn)) { @@ -2210,13 +2246,8 @@ int smb2_session_logoff(struct ksmbd_work *work) ksmbd_conn_unlock(conn); ksmbd_close_session_fds(work); - ksmbd_conn_wait_idle(conn, sess_id); + ksmbd_conn_wait_idle(conn); - /* - * Re-lookup session to validate if session is deleted - * while waiting request complete - */ - sess = ksmbd_session_lookup_all(conn, sess_id); if (ksmbd_tree_conn_session_logoff(sess)) { ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; @@ -2224,12 +2255,11 @@ int smb2_session_logoff(struct ksmbd_work *work) return -ENOENT; } - ksmbd_destroy_file_table(&sess->file_table); + down_write(&conn->session_lock); sess->state = SMB2_SESSION_EXPIRED; + up_write(&conn->session_lock); - ksmbd_free_user(sess->user); - sess->user = NULL; - ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_NEGOTIATE); + ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_SETUP); rsp->StructureSize = cpu_to_le16(4); err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_logoff_rsp)); @@ -2337,7 +2367,7 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len, le16_to_cpu(eabuf->EaValueLength)) return -EINVAL; - attr_name = kmalloc(XATTR_NAME_MAX + 1, GFP_KERNEL); + attr_name = kmalloc(XATTR_NAME_MAX + 1, KSMBD_DEFAULT_GFP); if (!attr_name) return -ENOMEM; @@ -2555,7 +2585,7 @@ static void smb2_update_xattrs(struct ksmbd_tree_connect *tcon, } } -static int smb2_creat(struct ksmbd_work *work, struct path *parent_path, +static int smb2_creat(struct ksmbd_work *work, struct path *path, char *name, int open_flags, umode_t posix_mode, bool is_dir) { @@ -2584,7 +2614,7 @@ static int smb2_creat(struct ksmbd_work *work, struct path *parent_path, return rc; } - rc = ksmbd_vfs_kern_path_locked(work, name, 0, parent_path, path, 0); + rc = ksmbd_vfs_kern_path(work, name, 0, path, 0); if (rc) { pr_err("cannot get linux path (%s), err = %d\n", name, rc); @@ -2683,7 +2713,7 @@ static int parse_durable_handle_context(struct ksmbd_work *work, switch (dh_idx) { case DURABLE_RECONN_V2: { - struct create_durable_reconn_v2_req *recon_v2; + struct create_durable_handle_reconnect_v2 *recon_v2; if (dh_info->type == DURABLE_RECONN || dh_info->type == DURABLE_REQ_V2) { @@ -2691,8 +2721,15 @@ static int parse_durable_handle_context(struct ksmbd_work *work, goto out; } - recon_v2 = (struct create_durable_reconn_v2_req *)context; - persistent_id = recon_v2->Fid.PersistentFileId; + if (le16_to_cpu(context->DataOffset) + + le32_to_cpu(context->DataLength) < + sizeof(struct create_durable_handle_reconnect_v2)) { + err = -EINVAL; + goto out; + } + + recon_v2 = (struct create_durable_handle_reconnect_v2 *)context; + persistent_id = recon_v2->dcontext.Fid.PersistentFileId; dh_info->fp = ksmbd_lookup_durable_fd(persistent_id); if (!dh_info->fp) { ksmbd_debug(SMB, "Failed to get durable handle state\n"); @@ -2700,7 +2737,7 @@ static int parse_durable_handle_context(struct ksmbd_work *work, goto out; } - if (memcmp(dh_info->fp->create_guid, recon_v2->CreateGuid, + if (memcmp(dh_info->fp->create_guid, recon_v2->dcontext.CreateGuid, SMB2_CREATE_GUID_SIZE)) { err = -EBADF; ksmbd_put_durable_fd(dh_info->fp); @@ -2716,7 +2753,7 @@ static int parse_durable_handle_context(struct ksmbd_work *work, } case DURABLE_RECONN: { - struct create_durable_reconn_req *recon; + create_durable_reconn_t *recon; if (dh_info->type == DURABLE_RECONN_V2 || dh_info->type == DURABLE_REQ_V2) { @@ -2724,7 +2761,14 @@ static int parse_durable_handle_context(struct ksmbd_work *work, goto out; } - recon = (struct create_durable_reconn_req *)context; + if (le16_to_cpu(context->DataOffset) + + le32_to_cpu(context->DataLength) < + sizeof(create_durable_reconn_t)) { + err = -EINVAL; + goto out; + } + + recon = (create_durable_reconn_t *)context; persistent_id = recon->Data.Fid.PersistentFileId; dh_info->fp = ksmbd_lookup_durable_fd(persistent_id); if (!dh_info->fp) { @@ -2749,10 +2793,17 @@ static int parse_durable_handle_context(struct ksmbd_work *work, goto out; } + if (le16_to_cpu(context->DataOffset) + + le32_to_cpu(context->DataLength) < + sizeof(struct create_durable_req_v2)) { + err = -EINVAL; + goto out; + } + durable_v2_blob = (struct create_durable_req_v2 *)context; ksmbd_debug(SMB, "Request for durable v2 open\n"); - dh_info->fp = ksmbd_lookup_fd_cguid(durable_v2_blob->CreateGuid); + dh_info->fp = ksmbd_lookup_fd_cguid(durable_v2_blob->dcontext.CreateGuid); if (dh_info->fp) { if (!memcmp(conn->ClientGUID, dh_info->fp->client_guid, SMB2_CLIENT_GUID_SIZE)) { @@ -2767,14 +2818,14 @@ static int parse_durable_handle_context(struct ksmbd_work *work, } } - if (((lc && (lc->req_state & SMB2_LEASE_HANDLE_CACHING_LE)) || - req_op_level == SMB2_OPLOCK_LEVEL_BATCH)) { + if ((lc && (lc->req_state & SMB2_LEASE_HANDLE_CACHING_LE)) || + req_op_level == SMB2_OPLOCK_LEVEL_BATCH) { dh_info->CreateGuid = - durable_v2_blob->CreateGuid; + durable_v2_blob->dcontext.CreateGuid; dh_info->persistent = - le32_to_cpu(durable_v2_blob->Flags); + le32_to_cpu(durable_v2_blob->dcontext.Flags); dh_info->timeout = - le32_to_cpu(durable_v2_blob->Timeout); + le32_to_cpu(durable_v2_blob->dcontext.Timeout); dh_info->type = dh_idx; } break; @@ -2788,8 +2839,8 @@ static int parse_durable_handle_context(struct ksmbd_work *work, goto out; } - if (((lc && (lc->req_state & SMB2_LEASE_HANDLE_CACHING_LE)) || - req_op_level == SMB2_OPLOCK_LEVEL_BATCH)) { + if ((lc && (lc->req_state & SMB2_LEASE_HANDLE_CACHING_LE)) || + req_op_level == SMB2_OPLOCK_LEVEL_BATCH) { ksmbd_debug(SMB, "Request for durable open\n"); dh_info->type = dh_idx; } @@ -2813,7 +2864,7 @@ int smb2_open(struct ksmbd_work *work) struct ksmbd_tree_connect *tcon = work->tcon; struct smb2_create_req *req; struct smb2_create_rsp *rsp; - struct path path, parent_path; + struct path path; struct ksmbd_share_config *share = tcon->share_conf; struct ksmbd_file *fp = NULL; struct file *filp = NULL; @@ -2828,7 +2879,7 @@ int smb2_open(struct ksmbd_work *work) int req_op_level = 0, open_flags = 0, may_flags = 0, file_info = 0; int rc = 0; int contxt_cnt = 0, query_disk_id = 0; - int maximal_access_ctxt = 0, posix_ctxt = 0; + bool maximal_access_ctxt = false, posix_ctxt = false; int s_type = 0; int next_off = 0; char *name = NULL; @@ -2840,6 +2891,8 @@ int smb2_open(struct ksmbd_work *work) __le32 daccess, maximal_access = 0; int iov_len = 0; + ksmbd_debug(SMB, "Received smb2 create request\n"); + WORK_BUFFERS(work, req, rsp); if (req->hdr.NextCommand && !work->next_smb2_rcv_hdr_off && @@ -2855,6 +2908,27 @@ int smb2_open(struct ksmbd_work *work) return create_smb2_pipe(work); } + if (req->CreateContextsOffset && tcon->posix_extensions) { + context = smb2_find_context_vals(req, SMB2_CREATE_TAG_POSIX, 16); + if (IS_ERR(context)) { + rc = PTR_ERR(context); + goto err_out2; + } else if (context) { + struct create_posix *posix = (struct create_posix *)context; + + if (le16_to_cpu(context->DataOffset) + + le32_to_cpu(context->DataLength) < + sizeof(struct create_posix) - 4) { + rc = -EINVAL; + goto err_out2; + } + ksmbd_debug(SMB, "get posix context\n"); + + posix_mode = le32_to_cpu(posix->Mode); + posix_ctxt = true; + } + } + if (req->NameLength) { name = smb2_get_name((char *)req + le16_to_cpu(req->NameOffset), le16_to_cpu(req->NameLength), @@ -2866,21 +2940,24 @@ int smb2_open(struct ksmbd_work *work) } ksmbd_debug(SMB, "converted name = %s\n", name); - if (strchr(name, ':')) { - if (!test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_STREAMS)) { - rc = -EBADF; - goto err_out2; + + if (posix_ctxt == false) { + if (strchr(name, ':')) { + if (!test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_STREAMS)) { + rc = -EBADF; + goto err_out2; + } + rc = parse_stream_name(name, &stream_name, &s_type); + if (rc < 0) + goto err_out2; } - rc = parse_stream_name(name, &stream_name, &s_type); + + rc = ksmbd_validate_filename(name); if (rc < 0) goto err_out2; } - rc = ksmbd_validate_filename(name); - if (rc < 0) - goto err_out2; - if (ksmbd_share_veto_filename(share, name)) { rc = -ENOENT; ksmbd_debug(SMB, "Reject open(), vetoed file: %s\n", @@ -2888,7 +2965,7 @@ int smb2_open(struct ksmbd_work *work) goto err_out2; } } else { - name = kstrdup("", GFP_KERNEL); + name = kstrdup("", KSMBD_DEFAULT_GFP); if (!name) { rc = -ENOMEM; goto err_out2; @@ -3037,28 +3114,6 @@ int smb2_open(struct ksmbd_work *work) rc = -EBADF; goto err_out2; } - - if (tcon->posix_extensions) { - context = smb2_find_context_vals(req, - SMB2_CREATE_TAG_POSIX, 16); - if (IS_ERR(context)) { - rc = PTR_ERR(context); - goto err_out2; - } else if (context) { - struct create_posix *posix = - (struct create_posix *)context; - if (le16_to_cpu(context->DataOffset) + - le32_to_cpu(context->DataLength) < - sizeof(struct create_posix) - 4) { - rc = -EINVAL; - goto err_out2; - } - ksmbd_debug(SMB, "get posix context\n"); - - posix_mode = le32_to_cpu(posix->Mode); - posix_ctxt = 1; - } - } } if (ksmbd_override_fsids(work)) { @@ -3066,8 +3121,8 @@ int smb2_open(struct ksmbd_work *work) goto err_out2; } - rc = ksmbd_vfs_kern_path_locked(work, name, LOOKUP_NO_SYMLINKS, - &parent_path, &path, 1); + rc = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, + &path, 1); if (!rc) { file_present = true; @@ -3093,7 +3148,6 @@ int smb2_open(struct ksmbd_work *work) goto err_out; } - file_present = true; idmap = mnt_idmap(path.mnt); } else { if (rc != -ENOENT) @@ -3175,8 +3229,8 @@ int smb2_open(struct ksmbd_work *work) open_flags = smb2_create_open_flags(file_present, daccess, req->CreateDisposition, &may_flags, - req->CreateOptions & FILE_DIRECTORY_FILE_LE || - (file_present && S_ISDIR(d_inode(path.dentry)->i_mode))); + req->CreateOptions, + file_present ? d_inode(path.dentry)->i_mode : 0); if (!test_tree_conn_flag(tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { if (open_flags & (O_CREAT | O_TRUNC)) { @@ -3189,7 +3243,7 @@ int smb2_open(struct ksmbd_work *work) /*create file if not present */ if (!file_present) { - rc = smb2_creat(work, &parent_path, &path, name, open_flags, + rc = smb2_creat(work, &path, name, open_flags, posix_mode, req->CreateOptions & FILE_DIRECTORY_FILE_LE); if (rc) { @@ -3330,7 +3384,7 @@ int smb2_open(struct ksmbd_work *work) sizeof(struct smb_sid) * 3 + sizeof(struct smb_acl) + sizeof(struct smb_ace) * ace_num * 2, - GFP_KERNEL); + KSMBD_DEFAULT_GFP); if (!pntsd) { posix_acl_release(fattr.cf_acls); posix_acl_release(fattr.cf_dacls); @@ -3379,6 +3433,8 @@ int smb2_open(struct ksmbd_work *work) fp->attrib_only = !(req->DesiredAccess & ~(FILE_READ_ATTRIBUTES_LE | FILE_WRITE_ATTRIBUTES_LE | FILE_SYNCHRONIZE_LE)); + fp->is_posix_ctxt = posix_ctxt; + /* fp should be searchable through ksmbd_inode.m_fp_list * after daccess, saccess, attrib_only, and stream are * initialized. @@ -3394,7 +3450,7 @@ int smb2_open(struct ksmbd_work *work) } if (file_present || created) - ksmbd_vfs_kern_path_unlock(&parent_path, &path); + path_put(&path); if (!S_ISDIR(file_inode(filp)->i_mode) && open_flags & O_TRUNC && !fp->attrib_only && !stream_name) { @@ -3405,13 +3461,13 @@ int smb2_open(struct ksmbd_work *work) share_ret = ksmbd_smb_check_shared_mode(fp->filp, fp); if (!test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_OPLOCKS) || (req_op_level == SMB2_OPLOCK_LEVEL_LEASE && - !(conn->vals->capabilities & SMB2_GLOBAL_CAP_LEASING))) { + !(conn->vals->req_capabilities & SMB2_GLOBAL_CAP_LEASING))) { if (share_ret < 0 && !S_ISDIR(file_inode(fp->filp)->i_mode)) { rc = share_ret; goto err_out1; } } else { - if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) { + if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE && lc) { if (S_ISDIR(file_inode(filp)->i_mode)) { lc->req_state &= ~SMB2_LEASE_WRITE_CACHING_LE; lc->is_dir = true; @@ -3492,6 +3548,15 @@ int smb2_open(struct ksmbd_work *work) ksmbd_debug(SMB, "get query on disk id context\n"); query_disk_id = 1; } + + if (conn->is_aapl == false) { + context = smb2_find_context_vals(req, SMB2_CREATE_AAPL, 4); + if (IS_ERR(context)) { + rc = PTR_ERR(context); + goto err_out1; + } else if (context) + conn->is_aapl = true; + } } rc = ksmbd_vfs_getattr(&path, &stat); @@ -3525,8 +3590,9 @@ int smb2_open(struct ksmbd_work *work) memcpy(fp->create_guid, dh_info.CreateGuid, SMB2_CREATE_GUID_SIZE); if (dh_info.timeout) - fp->durable_timeout = min(dh_info.timeout, - 300000); + fp->durable_timeout = + min_t(unsigned int, dh_info.timeout, + DURABLE_HANDLE_MAX_TIMEOUT); else fp->durable_timeout = 60; } @@ -3665,7 +3731,7 @@ reconnected_fp: err_out: if (rc && (file_present || created)) - ksmbd_vfs_kern_path_unlock(&parent_path, &path); + path_put(&path); err_out1: ksmbd_revert_fsids(work); @@ -3710,22 +3776,22 @@ err_out2: kfree(name); kfree(lc); - return 0; + return rc; } static int readdir_info_level_struct_sz(int info_level) { switch (info_level) { case FILE_FULL_DIRECTORY_INFORMATION: - return sizeof(struct file_full_directory_info); + return sizeof(FILE_FULL_DIRECTORY_INFO); case FILE_BOTH_DIRECTORY_INFORMATION: - return sizeof(struct file_both_directory_info); + return sizeof(FILE_BOTH_DIRECTORY_INFO); case FILE_DIRECTORY_INFORMATION: - return sizeof(struct file_directory_info); + return sizeof(FILE_DIRECTORY_INFO); case FILE_NAMES_INFORMATION: return sizeof(struct file_names_info); case FILEID_FULL_DIRECTORY_INFORMATION: - return sizeof(struct file_id_full_dir_info); + return sizeof(FILE_ID_FULL_DIR_INFO); case FILEID_BOTH_DIRECTORY_INFORMATION: return sizeof(struct file_id_both_directory_info); case SMB_FIND_FILE_POSIX_INFO: @@ -3740,9 +3806,9 @@ static int dentry_name(struct ksmbd_dir_info *d_info, int info_level) switch (info_level) { case FILE_FULL_DIRECTORY_INFORMATION: { - struct file_full_directory_info *ffdinfo; + FILE_FULL_DIRECTORY_INFO *ffdinfo; - ffdinfo = (struct file_full_directory_info *)d_info->rptr; + ffdinfo = (FILE_FULL_DIRECTORY_INFO *)d_info->rptr; d_info->rptr += le32_to_cpu(ffdinfo->NextEntryOffset); d_info->name = ffdinfo->FileName; d_info->name_len = le32_to_cpu(ffdinfo->FileNameLength); @@ -3750,9 +3816,9 @@ static int dentry_name(struct ksmbd_dir_info *d_info, int info_level) } case FILE_BOTH_DIRECTORY_INFORMATION: { - struct file_both_directory_info *fbdinfo; + FILE_BOTH_DIRECTORY_INFO *fbdinfo; - fbdinfo = (struct file_both_directory_info *)d_info->rptr; + fbdinfo = (FILE_BOTH_DIRECTORY_INFO *)d_info->rptr; d_info->rptr += le32_to_cpu(fbdinfo->NextEntryOffset); d_info->name = fbdinfo->FileName; d_info->name_len = le32_to_cpu(fbdinfo->FileNameLength); @@ -3760,9 +3826,9 @@ static int dentry_name(struct ksmbd_dir_info *d_info, int info_level) } case FILE_DIRECTORY_INFORMATION: { - struct file_directory_info *fdinfo; + FILE_DIRECTORY_INFO *fdinfo; - fdinfo = (struct file_directory_info *)d_info->rptr; + fdinfo = (FILE_DIRECTORY_INFO *)d_info->rptr; d_info->rptr += le32_to_cpu(fdinfo->NextEntryOffset); d_info->name = fdinfo->FileName; d_info->name_len = le32_to_cpu(fdinfo->FileNameLength); @@ -3780,9 +3846,9 @@ static int dentry_name(struct ksmbd_dir_info *d_info, int info_level) } case FILEID_FULL_DIRECTORY_INFORMATION: { - struct file_id_full_dir_info *dinfo; + FILE_ID_FULL_DIR_INFO *dinfo; - dinfo = (struct file_id_full_dir_info *)d_info->rptr; + dinfo = (FILE_ID_FULL_DIR_INFO *)d_info->rptr; d_info->rptr += le32_to_cpu(dinfo->NextEntryOffset); d_info->name = dinfo->FileName; d_info->name_len = le32_to_cpu(dinfo->FileNameLength); @@ -3865,9 +3931,9 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, switch (info_level) { case FILE_FULL_DIRECTORY_INFORMATION: { - struct file_full_directory_info *ffdinfo; + FILE_FULL_DIRECTORY_INFO *ffdinfo; - ffdinfo = (struct file_full_directory_info *)kstat; + ffdinfo = (FILE_FULL_DIRECTORY_INFO *)kstat; ffdinfo->FileNameLength = cpu_to_le32(conv_len); ffdinfo->EaSize = smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); @@ -3881,9 +3947,9 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, } case FILE_BOTH_DIRECTORY_INFORMATION: { - struct file_both_directory_info *fbdinfo; + FILE_BOTH_DIRECTORY_INFO *fbdinfo; - fbdinfo = (struct file_both_directory_info *)kstat; + fbdinfo = (FILE_BOTH_DIRECTORY_INFO *)kstat; fbdinfo->FileNameLength = cpu_to_le32(conv_len); fbdinfo->EaSize = smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); @@ -3899,9 +3965,9 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, } case FILE_DIRECTORY_INFORMATION: { - struct file_directory_info *fdinfo; + FILE_DIRECTORY_INFO *fdinfo; - fdinfo = (struct file_directory_info *)kstat; + fdinfo = (FILE_DIRECTORY_INFO *)kstat; fdinfo->FileNameLength = cpu_to_le32(conv_len); if (d_info->hide_dot_file && d_info->name[0] == '.') fdinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; @@ -3921,16 +3987,19 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, } case FILEID_FULL_DIRECTORY_INFORMATION: { - struct file_id_full_dir_info *dinfo; + FILE_ID_FULL_DIR_INFO *dinfo; - dinfo = (struct file_id_full_dir_info *)kstat; + dinfo = (FILE_ID_FULL_DIR_INFO *)kstat; dinfo->FileNameLength = cpu_to_le32(conv_len); dinfo->EaSize = smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); if (dinfo->EaSize) dinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE; dinfo->Reserved = 0; - dinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino); + if (conn->is_aapl) + dinfo->UniqueId = 0; + else + dinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino); if (d_info->hide_dot_file && d_info->name[0] == '.') dinfo->ExtFileAttributes |= FILE_ATTRIBUTE_HIDDEN_LE; memcpy(dinfo->FileName, conv_name, conv_len); @@ -3947,7 +4016,10 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); if (fibdinfo->EaSize) fibdinfo->ExtFileAttributes = FILE_ATTRIBUTE_REPARSE_POINT_LE; - fibdinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino); + if (conn->is_aapl) + fibdinfo->UniqueId = 0; + else + fibdinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino); fibdinfo->ShortNameLength = 0; fibdinfo->Reserved = 0; fibdinfo->Reserved2 = cpu_to_le16(0); @@ -3976,6 +4048,26 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, posix_info->DeviceId = cpu_to_le32(ksmbd_kstat->kstat->rdev); posix_info->HardLinks = cpu_to_le32(ksmbd_kstat->kstat->nlink); posix_info->Mode = cpu_to_le32(ksmbd_kstat->kstat->mode & 0777); + switch (ksmbd_kstat->kstat->mode & S_IFMT) { + case S_IFDIR: + posix_info->Mode |= cpu_to_le32(POSIX_TYPE_DIR << POSIX_FILETYPE_SHIFT); + break; + case S_IFLNK: + posix_info->Mode |= cpu_to_le32(POSIX_TYPE_SYMLINK << POSIX_FILETYPE_SHIFT); + break; + case S_IFCHR: + posix_info->Mode |= cpu_to_le32(POSIX_TYPE_CHARDEV << POSIX_FILETYPE_SHIFT); + break; + case S_IFBLK: + posix_info->Mode |= cpu_to_le32(POSIX_TYPE_BLKDEV << POSIX_FILETYPE_SHIFT); + break; + case S_IFIFO: + posix_info->Mode |= cpu_to_le32(POSIX_TYPE_FIFO << POSIX_FILETYPE_SHIFT); + break; + case S_IFSOCK: + posix_info->Mode |= cpu_to_le32(POSIX_TYPE_SOCKET << POSIX_FILETYPE_SHIFT); + } + posix_info->Inode = cpu_to_le64(ksmbd_kstat->kstat->ino); posix_info->DosAttributes = S_ISDIR(ksmbd_kstat->kstat->mode) ? @@ -4023,20 +4115,6 @@ struct smb2_query_dir_private { int info_level; }; -static void lock_dir(struct ksmbd_file *dir_fp) -{ - struct dentry *dir = dir_fp->filp->f_path.dentry; - - inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); -} - -static void unlock_dir(struct ksmbd_file *dir_fp) -{ - struct dentry *dir = dir_fp->filp->f_path.dentry; - - inode_unlock(d_inode(dir)); -} - static int process_query_dir_entries(struct smb2_query_dir_private *priv) { struct mnt_idmap *idmap = file_mnt_idmap(priv->dir_fp->filp); @@ -4051,11 +4129,10 @@ static int process_query_dir_entries(struct smb2_query_dir_private *priv) if (dentry_name(priv->d_info, priv->info_level)) return -EINVAL; - lock_dir(priv->dir_fp); - dent = lookup_one(idmap, priv->d_info->name, - priv->dir_fp->filp->f_path.dentry, - priv->d_info->name_len); - unlock_dir(priv->dir_fp); + dent = lookup_one_unlocked(idmap, + &QSTR_LEN(priv->d_info->name, + priv->d_info->name_len), + priv->dir_fp->filp->f_path.dentry); if (IS_ERR(dent)) { ksmbd_debug(SMB, "Cannot lookup `%s' [%ld]\n", @@ -4116,9 +4193,9 @@ static int reserve_populate_dentry(struct ksmbd_dir_info *d_info, switch (info_level) { case FILE_FULL_DIRECTORY_INFORMATION: { - struct file_full_directory_info *ffdinfo; + FILE_FULL_DIRECTORY_INFO *ffdinfo; - ffdinfo = (struct file_full_directory_info *)d_info->wptr; + ffdinfo = (FILE_FULL_DIRECTORY_INFO *)d_info->wptr; memcpy(ffdinfo->FileName, d_info->name, d_info->name_len); ffdinfo->FileName[d_info->name_len] = 0x00; ffdinfo->FileNameLength = cpu_to_le32(d_info->name_len); @@ -4127,9 +4204,9 @@ static int reserve_populate_dentry(struct ksmbd_dir_info *d_info, } case FILE_BOTH_DIRECTORY_INFORMATION: { - struct file_both_directory_info *fbdinfo; + FILE_BOTH_DIRECTORY_INFO *fbdinfo; - fbdinfo = (struct file_both_directory_info *)d_info->wptr; + fbdinfo = (FILE_BOTH_DIRECTORY_INFO *)d_info->wptr; memcpy(fbdinfo->FileName, d_info->name, d_info->name_len); fbdinfo->FileName[d_info->name_len] = 0x00; fbdinfo->FileNameLength = cpu_to_le32(d_info->name_len); @@ -4138,9 +4215,9 @@ static int reserve_populate_dentry(struct ksmbd_dir_info *d_info, } case FILE_DIRECTORY_INFORMATION: { - struct file_directory_info *fdinfo; + FILE_DIRECTORY_INFO *fdinfo; - fdinfo = (struct file_directory_info *)d_info->wptr; + fdinfo = (FILE_DIRECTORY_INFO *)d_info->wptr; memcpy(fdinfo->FileName, d_info->name, d_info->name_len); fdinfo->FileName[d_info->name_len] = 0x00; fdinfo->FileNameLength = cpu_to_le32(d_info->name_len); @@ -4160,9 +4237,9 @@ static int reserve_populate_dentry(struct ksmbd_dir_info *d_info, } case FILEID_FULL_DIRECTORY_INFORMATION: { - struct file_id_full_dir_info *dinfo; + FILE_ID_FULL_DIR_INFO *dinfo; - dinfo = (struct file_id_full_dir_info *)d_info->wptr; + dinfo = (FILE_ID_FULL_DIR_INFO *)d_info->wptr; memcpy(dinfo->FileName, d_info->name, d_info->name_len); dinfo->FileName[d_info->name_len] = 0x00; dinfo->FileNameLength = cpu_to_le32(d_info->name_len); @@ -4215,6 +4292,7 @@ static bool __query_dir(struct dir_context *ctx, const char *name, int namlen, /* dot and dotdot entries are already reserved */ if (!strcmp(".", name) || !strcmp("..", name)) return true; + d_info->num_scan++; if (ksmbd_share_veto_filename(priv->work->tcon->share_conf, name)) return true; if (!match_pattern(name, namlen, priv->search_pattern)) @@ -4287,6 +4365,8 @@ int smb2_query_dir(struct ksmbd_work *work) int buffer_sz; struct smb2_query_dir_private query_dir_private = {NULL, }; + ksmbd_debug(SMB, "Received smb2 query directory request\n"); + WORK_BUFFERS(work, req, rsp); if (ksmbd_override_fsids(work)) { @@ -4375,9 +4455,18 @@ int smb2_query_dir(struct ksmbd_work *work) query_dir_private.info_level = req->FileInformationClass; dir_fp->readdir_data.private = &query_dir_private; set_ctx_actor(&dir_fp->readdir_data.ctx, __query_dir); - +again: + d_info.num_scan = 0; rc = iterate_dir(dir_fp->filp, &dir_fp->readdir_data.ctx); /* + * num_entry can be 0 if the directory iteration stops before reaching + * the end of the directory and no file is matched with the search + * pattern. + */ + if (rc >= 0 && !d_info.num_entry && d_info.num_scan && + d_info.out_buf_len > 0) + goto again; + /* * req->OutputBufferLength is too small to contain even one entry. * In this case, it immediately returns OutputBufferLength 0 to client. */ @@ -4406,12 +4495,13 @@ int smb2_query_dir(struct ksmbd_work *work) rsp->OutputBufferLength = cpu_to_le32(0); rsp->Buffer[0] = 0; rc = ksmbd_iov_pin_rsp(work, (void *)rsp, - sizeof(struct smb2_query_directory_rsp)); + offsetof(struct smb2_query_directory_rsp, Buffer) + + 1); if (rc) goto err_out; } else { no_buf_len: - ((struct file_directory_info *) + ((FILE_DIRECTORY_INFO *) ((char *)rsp->Buffer + d_info.last_entry_offset)) ->NextEntryOffset = 0; if (d_info.data_count >= d_info.last_entry_off_align) @@ -4457,7 +4547,7 @@ err_out2: smb2_set_err_rsp(work); ksmbd_fd_put(work, dir_fp); ksmbd_revert_fsids(work); - return 0; + return rc; } /** @@ -4523,8 +4613,15 @@ static int smb2_get_info_file_pipe(struct ksmbd_session *sess, * pipe without opening it, checking error condition here */ id = req->VolatileFileId; - if (!ksmbd_session_rpc_method(sess, id)) + + lockdep_assert_not_held(&sess->rpc_lock); + + down_read(&sess->rpc_lock); + if (!ksmbd_session_rpc_method(sess, id)) { + up_read(&sess->rpc_lock); return -ENOENT; + } + up_read(&sess->rpc_lock); ksmbd_debug(SMB, "FileInfoClass %u, FileId 0x%llx\n", req->FileInfoClass, req->VolatileFileId); @@ -4579,7 +4676,7 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp, path = &fp->filp->f_path; /* single EA entry is requested with given user.* name */ if (req->InputBufferLength) { - if (le32_to_cpu(req->InputBufferLength) < + if (le32_to_cpu(req->InputBufferLength) <= sizeof(struct smb2_ea_info_req)) return -EINVAL; @@ -4773,8 +4870,13 @@ static int get_file_standard_info(struct smb2_query_info_rsp *rsp, sinfo = (struct smb2_file_standard_info *)rsp->Buffer; delete_pending = ksmbd_inode_pending_delete(fp); - sinfo->AllocationSize = cpu_to_le64(stat.blocks << 9); - sinfo->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); + if (ksmbd_stream_fd(fp) == false) { + sinfo->AllocationSize = cpu_to_le64(stat.blocks << 9); + sinfo->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); + } else { + sinfo->AllocationSize = cpu_to_le64(fp->stream.size); + sinfo->EndOfFile = cpu_to_le64(fp->stream.size); + } sinfo->NumberOfLinks = cpu_to_le32(get_nlink(&stat) - delete_pending); sinfo->DeletePending = delete_pending; sinfo->Directory = S_ISDIR(stat.mode) ? 1 : 0; @@ -4837,9 +4939,14 @@ static int get_file_all_info(struct ksmbd_work *work, file_info->ChangeTime = cpu_to_le64(time); file_info->Attributes = fp->f_ci->m_fattr; file_info->Pad1 = 0; - file_info->AllocationSize = - cpu_to_le64(stat.blocks << 9); - file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); + if (ksmbd_stream_fd(fp) == false) { + file_info->AllocationSize = + cpu_to_le64(stat.blocks << 9); + file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); + } else { + file_info->AllocationSize = cpu_to_le64(fp->stream.size); + file_info->EndOfFile = cpu_to_le64(fp->stream.size); + } file_info->NumberOfLinks = cpu_to_le32(get_nlink(&stat) - delete_pending); file_info->DeletePending = delete_pending; @@ -4848,7 +4955,10 @@ static int get_file_all_info(struct ksmbd_work *work, file_info->IndexNumber = cpu_to_le64(stat.ino); file_info->EASize = 0; file_info->AccessFlags = fp->daccess; - file_info->CurrentByteOffset = cpu_to_le64(fp->filp->f_pos); + if (ksmbd_stream_fd(fp) == false) + file_info->CurrentByteOffset = cpu_to_le64(fp->filp->f_pos); + else + file_info->CurrentByteOffset = cpu_to_le64(fp->stream.pos); file_info->Mode = fp->coption; file_info->AlignmentRequirement = 0; conv_len = smbConvertToUTF16((__le16 *)file_info->FileName, filename, @@ -4879,7 +4989,7 @@ static void get_file_alternate_info(struct ksmbd_work *work, spin_unlock(&dentry->d_lock); file_info->FileNameLength = cpu_to_le32(conv_len); rsp->OutputBufferLength = - cpu_to_le32(sizeof(struct smb2_file_alt_name_info) + conv_len); + cpu_to_le32(struct_size(file_info, FileName, conv_len)); } static int get_file_stream_info(struct ksmbd_work *work, @@ -4936,7 +5046,7 @@ static int get_file_stream_info(struct ksmbd_work *work, /* plus : size */ streamlen += 1; - stream_buf = kmalloc(streamlen + 1, GFP_KERNEL); + stream_buf = kmalloc(streamlen + 1, KSMBD_DEFAULT_GFP); if (!stream_buf) break; @@ -5010,7 +5120,7 @@ static int get_file_internal_info(struct smb2_query_info_rsp *rsp, static int get_file_network_open_info(struct smb2_query_info_rsp *rsp, struct ksmbd_file *fp, void *rsp_org) { - struct smb2_file_ntwrk_info *file_info; + struct smb2_file_network_open_info *file_info; struct kstat stat; u64 time; int ret; @@ -5026,7 +5136,7 @@ static int get_file_network_open_info(struct smb2_query_info_rsp *rsp, if (ret) return ret; - file_info = (struct smb2_file_ntwrk_info *)rsp->Buffer; + file_info = (struct smb2_file_network_open_info *)rsp->Buffer; file_info->CreationTime = cpu_to_le64(fp->create_time); time = ksmbd_UnixTimeToNT(stat.atime); @@ -5036,11 +5146,16 @@ static int get_file_network_open_info(struct smb2_query_info_rsp *rsp, time = ksmbd_UnixTimeToNT(stat.ctime); file_info->ChangeTime = cpu_to_le64(time); file_info->Attributes = fp->f_ci->m_fattr; - file_info->AllocationSize = cpu_to_le64(stat.blocks << 9); - file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); + if (ksmbd_stream_fd(fp) == false) { + file_info->AllocationSize = cpu_to_le64(stat.blocks << 9); + file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); + } else { + file_info->AllocationSize = cpu_to_le64(fp->stream.size); + file_info->EndOfFile = cpu_to_le64(fp->stream.size); + } file_info->Reserved = cpu_to_le32(0); rsp->OutputBufferLength = - cpu_to_le32(sizeof(struct smb2_file_ntwrk_info)); + cpu_to_le32(sizeof(struct smb2_file_network_open_info)); return 0; } @@ -5060,7 +5175,11 @@ static void get_file_position_info(struct smb2_query_info_rsp *rsp, struct smb2_file_pos_info *file_info; file_info = (struct smb2_file_pos_info *)rsp->Buffer; - file_info->CurrentByteOffset = cpu_to_le64(fp->filp->f_pos); + if (ksmbd_stream_fd(fp) == false) + file_info->CurrentByteOffset = cpu_to_le64(fp->filp->f_pos); + else + file_info->CurrentByteOffset = cpu_to_le64(fp->stream.pos); + rsp->OutputBufferLength = cpu_to_le32(sizeof(struct smb2_file_pos_info)); } @@ -5149,10 +5268,35 @@ static int find_file_posix_info(struct smb2_query_info_rsp *rsp, file_info->ChangeTime = cpu_to_le64(time); file_info->DosAttributes = fp->f_ci->m_fattr; file_info->Inode = cpu_to_le64(stat.ino); - file_info->EndOfFile = cpu_to_le64(stat.size); - file_info->AllocationSize = cpu_to_le64(stat.blocks << 9); + if (ksmbd_stream_fd(fp) == false) { + file_info->EndOfFile = cpu_to_le64(stat.size); + file_info->AllocationSize = cpu_to_le64(stat.blocks << 9); + } else { + file_info->EndOfFile = cpu_to_le64(fp->stream.size); + file_info->AllocationSize = cpu_to_le64(fp->stream.size); + } file_info->HardLinks = cpu_to_le32(stat.nlink); file_info->Mode = cpu_to_le32(stat.mode & 0777); + switch (stat.mode & S_IFMT) { + case S_IFDIR: + file_info->Mode |= cpu_to_le32(POSIX_TYPE_DIR << POSIX_FILETYPE_SHIFT); + break; + case S_IFLNK: + file_info->Mode |= cpu_to_le32(POSIX_TYPE_SYMLINK << POSIX_FILETYPE_SHIFT); + break; + case S_IFCHR: + file_info->Mode |= cpu_to_le32(POSIX_TYPE_CHARDEV << POSIX_FILETYPE_SHIFT); + break; + case S_IFBLK: + file_info->Mode |= cpu_to_le32(POSIX_TYPE_BLKDEV << POSIX_FILETYPE_SHIFT); + break; + case S_IFIFO: + file_info->Mode |= cpu_to_le32(POSIX_TYPE_FIFO << POSIX_FILETYPE_SHIFT); + break; + case S_IFSOCK: + file_info->Mode |= cpu_to_le32(POSIX_TYPE_SOCKET << POSIX_FILETYPE_SHIFT); + } + file_info->DeviceId = cpu_to_le32(stat.rdev); /* @@ -5319,9 +5463,9 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, switch (fsinfoclass) { case FS_DEVICE_INFORMATION: { - struct filesystem_device_info *info; + FILE_SYSTEM_DEVICE_INFO *info; - info = (struct filesystem_device_info *)rsp->Buffer; + info = (FILE_SYSTEM_DEVICE_INFO *)rsp->Buffer; info->DeviceType = cpu_to_le32(FILE_DEVICE_DISK); info->DeviceCharacteristics = @@ -5335,10 +5479,10 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, } case FS_ATTRIBUTE_INFORMATION: { - struct filesystem_attribute_info *info; + FILE_SYSTEM_ATTRIBUTE_INFO *info; size_t sz; - info = (struct filesystem_attribute_info *)rsp->Buffer; + info = (FILE_SYSTEM_ATTRIBUTE_INFO *)rsp->Buffer; info->Attributes = cpu_to_le32(FILE_SUPPORTS_OBJECT_IDS | FILE_PERSISTENT_ACLS | FILE_UNICODE_ON_DISK | @@ -5357,7 +5501,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, "NTFS", PATH_MAX, conn->local_nls, 0); len = len * 2; info->FileSystemNameLen = cpu_to_le32(len); - sz = sizeof(struct filesystem_attribute_info) - 2 + len; + sz = sizeof(FILE_SYSTEM_ATTRIBUTE_INFO) + len; rsp->OutputBufferLength = cpu_to_le32(sz); break; } @@ -5383,17 +5527,17 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, len = len * 2; info->VolumeLabelSize = cpu_to_le32(len); info->Reserved = 0; - sz = sizeof(struct filesystem_vol_info) - 2 + len; + sz = sizeof(struct filesystem_vol_info) + len; rsp->OutputBufferLength = cpu_to_le32(sz); break; } case FS_SIZE_INFORMATION: { - struct filesystem_info *info; + FILE_SYSTEM_SIZE_INFO *info; - info = (struct filesystem_info *)(rsp->Buffer); + info = (FILE_SYSTEM_SIZE_INFO *)(rsp->Buffer); info->TotalAllocationUnits = cpu_to_le64(stfs.f_blocks); - info->FreeAllocationUnits = cpu_to_le64(stfs.f_bfree); + info->AvailableAllocationUnits = cpu_to_le64(stfs.f_bfree); info->SectorsPerAllocationUnit = cpu_to_le32(1); info->BytesPerSector = cpu_to_le32(stfs.f_bsize); rsp->OutputBufferLength = cpu_to_le32(24); @@ -5476,13 +5620,14 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, } case FS_POSIX_INFORMATION: { - struct filesystem_posix_info *info; + FILE_SYSTEM_POSIX_INFO *info; if (!work->tcon->posix_extensions) { pr_err("client doesn't negotiate with SMB3.1.1 POSIX Extensions\n"); - rc = -EOPNOTSUPP; + path_put(&path); + return -EOPNOTSUPP; } else { - info = (struct filesystem_posix_info *)(rsp->Buffer); + info = (FILE_SYSTEM_POSIX_INFO *)(rsp->Buffer); info->OptimalTransferSize = cpu_to_le32(stfs.f_bsize); info->BlockSize = cpu_to_le32(stfs.f_bsize); info->TotalBlocks = cpu_to_le64(stfs.f_blocks); @@ -5592,9 +5737,14 @@ int smb2_query_info(struct ksmbd_work *work) struct smb2_query_info_rsp *rsp; int rc = 0; + ksmbd_debug(SMB, "Received request smb2 query info request\n"); + WORK_BUFFERS(work, req, rsp); - ksmbd_debug(SMB, "GOT query info request\n"); + if (ksmbd_override_fsids(work)) { + rc = -ENOMEM; + goto err_out; + } switch (req->InfoType) { case SMB2_O_INFO_FILE: @@ -5614,6 +5764,7 @@ int smb2_query_info(struct ksmbd_work *work) req->InfoType); rc = -EOPNOTSUPP; } + ksmbd_revert_fsids(work); if (!rc) { rsp->StructureSize = cpu_to_le16(9); @@ -5623,6 +5774,7 @@ int smb2_query_info(struct ksmbd_work *work) le32_to_cpu(rsp->OutputBufferLength)); } +err_out: if (rc < 0) { if (rc == -EACCES) rsp->hdr.Status = STATUS_ACCESS_DENIED; @@ -5679,7 +5831,7 @@ static noinline int smb2_close_pipe(struct ksmbd_work *work) * smb2_close() - handler for smb2 close file command * @work: smb work containing close request buffer * - * Return: 0 + * Return: 0 on success, otherwise error */ int smb2_close(struct ksmbd_work *work) { @@ -5692,6 +5844,8 @@ int smb2_close(struct ksmbd_work *work) u64 time; int err = 0; + ksmbd_debug(SMB, "Received smb2 close request\n"); + WORK_BUFFERS(work, req, rsp); if (test_share_config_flag(work->tcon->share_conf, @@ -5802,12 +5956,14 @@ out: * smb2_echo() - handler for smb2 echo(ping) command * @work: smb work containing echo request buffer * - * Return: 0 + * Return: 0 on success, otherwise error */ int smb2_echo(struct ksmbd_work *work) { struct smb2_echo_rsp *rsp = smb2_get_msg(work->response_buf); + ksmbd_debug(SMB, "Received smb2 echo request\n"); + if (work->next_smb2_rcv_hdr_off) rsp = ksmbd_resp_buf_next(work); @@ -5832,7 +5988,7 @@ static int smb2_rename(struct ksmbd_work *work, if (IS_ERR(new_name)) return PTR_ERR(new_name); - if (strchr(new_name, ':')) { + if (fp->is_posix_ctxt == false && strchr(new_name, ':')) { int s_type; char *xattr_stream_name, *stream_name = NULL; size_t xattr_stream_size; @@ -5895,8 +6051,7 @@ static int smb2_create_link(struct ksmbd_work *work, struct nls_table *local_nls) { char *link_name = NULL, *target_name = NULL, *pathname = NULL; - struct path path, parent_path; - bool file_present = false; + struct path path; int rc; if (buf_len < (u64)sizeof(struct smb2_file_link_info) + @@ -5904,7 +6059,7 @@ static int smb2_create_link(struct ksmbd_work *work, return -EINVAL; ksmbd_debug(SMB, "setting FILE_LINK_INFORMATION\n"); - pathname = kmalloc(PATH_MAX, GFP_KERNEL); + pathname = kmalloc(PATH_MAX, KSMBD_DEFAULT_GFP); if (!pathname) return -ENOMEM; @@ -5924,16 +6079,13 @@ static int smb2_create_link(struct ksmbd_work *work, } ksmbd_debug(SMB, "target name is %s\n", target_name); - rc = ksmbd_vfs_kern_path_locked(work, link_name, LOOKUP_NO_SYMLINKS, - &parent_path, &path, 0); + rc = ksmbd_vfs_kern_path_start_removing(work, link_name, LOOKUP_NO_SYMLINKS, + &path, 0); if (rc) { if (rc != -ENOENT) goto out; - } else - file_present = true; - - if (file_info->ReplaceIfExists) { - if (file_present) { + } else { + if (file_info->ReplaceIfExists) { rc = ksmbd_vfs_remove_file(work, &path); if (rc) { rc = -EINVAL; @@ -5941,21 +6093,17 @@ static int smb2_create_link(struct ksmbd_work *work, link_name); goto out; } - } - } else { - if (file_present) { + } else { rc = -EEXIST; ksmbd_debug(SMB, "link already exists\n"); goto out; } + ksmbd_vfs_kern_path_end_removing(&path); } - rc = ksmbd_vfs_link(work, target_name, link_name); if (rc) rc = -EINVAL; out: - if (file_present) - ksmbd_vfs_kern_path_unlock(&parent_path, &path); if (!IS_ERR(link_name)) kfree(link_name); @@ -5989,15 +6137,13 @@ static int set_file_basic_info(struct ksmbd_file *fp, attrs.ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET); } - attrs.ia_valid |= ATTR_CTIME; if (file_info->ChangeTime) - attrs.ia_ctime = ksmbd_NTtimeToUnix(file_info->ChangeTime); - else - attrs.ia_ctime = inode_get_ctime(inode); + inode_set_ctime_to_ts(inode, + ksmbd_NTtimeToUnix(file_info->ChangeTime)); if (file_info->LastWriteTime) { attrs.ia_mtime = ksmbd_NTtimeToUnix(file_info->LastWriteTime); - attrs.ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET); + attrs.ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET | ATTR_CTIME); } if (file_info->Attributes) { @@ -6039,8 +6185,6 @@ static int set_file_basic_info(struct ksmbd_file *fp, return -EACCES; inode_lock(inode); - inode_set_ctime_to_ts(inode, attrs.ia_ctime); - attrs.ia_valid &= ~ATTR_CTIME; rc = notify_change(idmap, dentry, &attrs, NULL); inode_unlock(inode); } @@ -6065,6 +6209,9 @@ static int set_file_allocation_info(struct ksmbd_work *work, if (!(fp->daccess & FILE_WRITE_DATA_LE)) return -EACCES; + if (ksmbd_stream_fd(fp) == true) + return 0; + rc = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS, AT_STATX_SYNC_AS_STAT); if (rc) @@ -6123,7 +6270,8 @@ static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp, * truncate of some filesystem like FAT32 fill zero data in * truncated range. */ - if (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC) { + if (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC && + ksmbd_stream_fd(fp) == false) { ksmbd_debug(SMB, "truncated to newsize %lld\n", newsize); rc = ksmbd_vfs_truncate(work, fp, newsize); if (rc) { @@ -6196,7 +6344,13 @@ static int set_file_position_info(struct ksmbd_file *fp, return -EINVAL; } - fp->filp->f_pos = current_byte_offset; + if (ksmbd_stream_fd(fp) == false) + fp->filp->f_pos = current_byte_offset; + else { + if (current_byte_offset > XATTR_SIZE_MAX) + current_byte_offset = XATTR_SIZE_MAX; + fp->stream.pos = current_byte_offset; + } return 0; } @@ -6229,7 +6383,6 @@ static int set_file_mode_info(struct ksmbd_file *fp, * @share: ksmbd_share_config pointer * * Return: 0 on success, otherwise error - * TODO: need to implement an error handling for STATUS_INFO_LENGTH_MISMATCH */ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, struct smb2_set_info_req *req, @@ -6242,14 +6395,14 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, case FILE_BASIC_INFORMATION: { if (buf_len < sizeof(struct smb2_file_basic_info)) - return -EINVAL; + return -EMSGSIZE; return set_file_basic_info(fp, (struct smb2_file_basic_info *)buffer, share); } case FILE_ALLOCATION_INFORMATION: { if (buf_len < sizeof(struct smb2_file_alloc_info)) - return -EINVAL; + return -EMSGSIZE; return set_file_allocation_info(work, fp, (struct smb2_file_alloc_info *)buffer); @@ -6257,7 +6410,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, case FILE_END_OF_FILE_INFORMATION: { if (buf_len < sizeof(struct smb2_file_eof_info)) - return -EINVAL; + return -EMSGSIZE; return set_end_of_file_info(work, fp, (struct smb2_file_eof_info *)buffer); @@ -6265,7 +6418,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, case FILE_RENAME_INFORMATION: { if (buf_len < sizeof(struct smb2_file_rename_info)) - return -EINVAL; + return -EMSGSIZE; return set_rename_info(work, fp, (struct smb2_file_rename_info *)buffer, @@ -6274,7 +6427,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, case FILE_LINK_INFORMATION: { if (buf_len < sizeof(struct smb2_file_link_info)) - return -EINVAL; + return -EMSGSIZE; return smb2_create_link(work, work->tcon->share_conf, (struct smb2_file_link_info *)buffer, @@ -6284,7 +6437,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, case FILE_DISPOSITION_INFORMATION: { if (buf_len < sizeof(struct smb2_file_disposition_info)) - return -EINVAL; + return -EMSGSIZE; return set_file_disposition_info(fp, (struct smb2_file_disposition_info *)buffer); @@ -6298,7 +6451,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, } if (buf_len < sizeof(struct smb2_ea_info)) - return -EINVAL; + return -EMSGSIZE; return smb2_set_ea((struct smb2_ea_info *)buffer, buf_len, &fp->filp->f_path, true); @@ -6306,14 +6459,14 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, case FILE_POSITION_INFORMATION: { if (buf_len < sizeof(struct smb2_file_pos_info)) - return -EINVAL; + return -EMSGSIZE; return set_file_position_info(fp, (struct smb2_file_pos_info *)buffer); } case FILE_MODE_INFORMATION: { if (buf_len < sizeof(struct smb2_file_mode_info)) - return -EINVAL; + return -EMSGSIZE; return set_file_mode_info(fp, (struct smb2_file_mode_info *)buffer); } @@ -6348,7 +6501,7 @@ int smb2_set_info(struct ksmbd_work *work) int rc = 0; unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; - ksmbd_debug(SMB, "Received set info request\n"); + ksmbd_debug(SMB, "Received smb2 set info request\n"); if (work->next_smb2_rcv_hdr_off) { req = ksmbd_req_buf_next(work); @@ -6420,6 +6573,8 @@ err_out: rsp->hdr.Status = STATUS_ACCESS_DENIED; else if (rc == -EINVAL) rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else if (rc == -EMSGSIZE) + rsp->hdr.Status = STATUS_INFO_LENGTH_MISMATCH; else if (rc == -ESHARE) rsp->hdr.Status = STATUS_SHARING_VIOLATION; else if (rc == -ENOENT) @@ -6468,7 +6623,7 @@ static noinline int smb2_read_pipe(struct ksmbd_work *work) } aux_payload_buf = - kvmalloc(rpc_resp->payload_sz, GFP_KERNEL); + kvmalloc(rpc_resp->payload_sz, KSMBD_DEFAULT_GFP); if (!aux_payload_buf) { err = -ENOMEM; goto out; @@ -6508,7 +6663,7 @@ out: } static int smb2_set_remote_key_for_rdma(struct ksmbd_work *work, - struct smb2_buffer_desc_v1 *desc, + struct smbdirect_buffer_descriptor_v1 *desc, __le32 Channel, __le16 ChannelInfoLength) { @@ -6544,7 +6699,7 @@ static ssize_t smb2_read_rdma_channel(struct ksmbd_work *work, int err; err = ksmbd_conn_rdma_write(work->conn, data_buf, length, - (struct smb2_buffer_desc_v1 *) + (struct smbdirect_buffer_descriptor_v1 *) ((char *)req + le16_to_cpu(req->ReadChannelInfoOffset)), le16_to_cpu(req->ReadChannelInfoLength)); if (err) @@ -6574,6 +6729,8 @@ int smb2_read(struct ksmbd_work *work) unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; void *aux_payload_buf; + ksmbd_debug(SMB, "Received smb2 read request\n"); + if (test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_PIPE)) { ksmbd_debug(SMB, "IPC pipe read request\n"); @@ -6602,7 +6759,11 @@ int smb2_read(struct ksmbd_work *work) if (req->Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE || req->Channel == SMB2_CHANNEL_RDMA_V1) { is_rdma_channel = true; - max_read_size = get_smbd_max_read_write_size(); + max_read_size = get_smbd_max_read_write_size(work->conn->transport); + if (max_read_size == 0) { + err = -EINVAL; + goto out; + } } if (is_rdma_channel == true) { @@ -6613,7 +6774,7 @@ int smb2_read(struct ksmbd_work *work) goto out; } err = smb2_set_remote_key_for_rdma(work, - (struct smb2_buffer_desc_v1 *) + (struct smbdirect_buffer_descriptor_v1 *) ((char *)req + ch_offset), req->Channel, req->ReadChannelInfoLength); @@ -6634,6 +6795,10 @@ int smb2_read(struct ksmbd_work *work) } offset = le64_to_cpu(req->Offset); + if (offset < 0) { + err = -EINVAL; + goto out; + } length = le32_to_cpu(req->Length); mincount = le32_to_cpu(req->MinimumCount); @@ -6647,7 +6812,7 @@ int smb2_read(struct ksmbd_work *work) ksmbd_debug(SMB, "filename %pD, offset %lld, len %zu\n", fp->filp, offset, length); - aux_payload_buf = kvzalloc(length, GFP_KERNEL); + aux_payload_buf = kvzalloc(ALIGN(length, 8), KSMBD_DEFAULT_GFP); if (!aux_payload_buf) { err = -ENOMEM; goto out; @@ -6655,6 +6820,7 @@ int smb2_read(struct ksmbd_work *work) nbytes = ksmbd_vfs_read(work, fp, length, &offset, aux_payload_buf); if (nbytes < 0) { + kvfree(aux_payload_buf); err = nbytes; goto out; } @@ -6664,7 +6830,7 @@ int smb2_read(struct ksmbd_work *work) rsp->hdr.Status = STATUS_END_OF_FILE; smb2_set_err_rsp(work); ksmbd_fd_put(work, fp); - return 0; + return -ENODATA; } ksmbd_debug(SMB, "nbytes %zu, offset %lld mincount %zu\n", @@ -6799,12 +6965,12 @@ static ssize_t smb2_write_rdma_channel(struct ksmbd_work *work, int ret; ssize_t nbytes; - data_buf = kvzalloc(length, GFP_KERNEL); + data_buf = kvzalloc(length, KSMBD_DEFAULT_GFP); if (!data_buf) return -ENOMEM; ret = ksmbd_conn_rdma_read(work->conn, data_buf, length, - (struct smb2_buffer_desc_v1 *) + (struct smbdirect_buffer_descriptor_v1 *) ((char *)req + le16_to_cpu(req->WriteChannelInfoOffset)), le16_to_cpu(req->WriteChannelInfoLength)); if (ret < 0) { @@ -6839,6 +7005,8 @@ int smb2_write(struct ksmbd_work *work) int err = 0; unsigned int max_write_size = work->conn->vals->max_write_size; + ksmbd_debug(SMB, "Received smb2 write request\n"); + WORK_BUFFERS(work, req, rsp); if (test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_PIPE)) { @@ -6847,12 +7015,18 @@ int smb2_write(struct ksmbd_work *work) } offset = le64_to_cpu(req->Offset); + if (offset < 0) + return -EINVAL; length = le32_to_cpu(req->Length); if (req->Channel == SMB2_CHANNEL_RDMA_V1 || req->Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE) { is_rdma_channel = true; - max_write_size = get_smbd_max_read_write_size(); + max_write_size = get_smbd_max_read_write_size(work->conn->transport); + if (max_write_size == 0) { + err = -EINVAL; + goto out; + } length = le32_to_cpu(req->RemainingBytes); } @@ -6865,7 +7039,7 @@ int smb2_write(struct ksmbd_work *work) goto out; } err = smb2_set_remote_key_for_rdma(work, - (struct smb2_buffer_desc_v1 *) + (struct smbdirect_buffer_descriptor_v1 *) ((char *)req + ch_offset), req->Channel, req->WriteChannelInfoLength); @@ -6977,7 +7151,7 @@ int smb2_flush(struct ksmbd_work *work) WORK_BUFFERS(work, req, rsp); - ksmbd_debug(SMB, "SMB2_FLUSH called for fid %llu\n", req->VolatileFileId); + ksmbd_debug(SMB, "Received smb2 flush request(fid : %llu)\n", req->VolatileFileId); err = ksmbd_vfs_fsync(work, req->VolatileFileId, req->PersistentFileId); if (err) @@ -7128,7 +7302,7 @@ static struct ksmbd_lock *smb2_lock_init(struct file_lock *flock, { struct ksmbd_lock *lock; - lock = kzalloc(sizeof(struct ksmbd_lock), GFP_KERNEL); + lock = kzalloc(sizeof(struct ksmbd_lock), KSMBD_DEFAULT_GFP); if (!lock) return NULL; @@ -7185,11 +7359,11 @@ int smb2_lock(struct ksmbd_work *work) int nolock = 0; LIST_HEAD(lock_list); LIST_HEAD(rollback_list); - int prior_lock = 0; + int prior_lock = 0, bkt; WORK_BUFFERS(work, req, rsp); - ksmbd_debug(SMB, "Received lock request\n"); + ksmbd_debug(SMB, "Received smb2 lock request\n"); fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); if (!fp) { ksmbd_debug(SMB, "Invalid file id for lock : %llu\n", req->VolatileFileId); @@ -7295,7 +7469,7 @@ int smb2_lock(struct ksmbd_work *work) nolock = 1; /* check locks in connection list */ down_read(&conn_list_lock); - list_for_each_entry(conn, &conn_list, conns_list) { + hash_for_each(conn_list, bkt, conn, hlist) { spin_lock(&conn->llist_lock); list_for_each_entry_safe(cmp_lock, tmp2, &conn->lock_list, clist) { if (file_inode(cmp_lock->fl->c.flc_file) != @@ -7369,17 +7543,17 @@ out_check_cl: } no_check_cl: + flock = smb_lock->fl; + list_del(&smb_lock->llist); + if (smb_lock->zero_len) { err = 0; goto skip; } - - flock = smb_lock->fl; - list_del(&smb_lock->llist); retry: rc = vfs_lock_file(filp, smb_lock->cmd, flock, NULL); skip: - if (flags & SMB2_LOCKFLAG_UNLOCK) { + if (smb_lock->flags & SMB2_LOCKFLAG_UNLOCK) { if (!rc) { ksmbd_debug(SMB, "File unlocked\n"); } else if (rc == -ENOENT) { @@ -7396,7 +7570,7 @@ skip: "would have to wait for getting lock\n"); list_add(&smb_lock->llist, &rollback_list); - argv = kmalloc(sizeof(void *), GFP_KERNEL); + argv = kmalloc(sizeof(void *), KSMBD_DEFAULT_GFP); if (!argv) { err = -ENOMEM; goto out; @@ -7551,7 +7725,6 @@ static int fsctl_copychunk(struct ksmbd_work *work, ci_rsp->TotalBytesWritten = cpu_to_le32(ksmbd_server_side_copy_max_total_size()); - chunks = (struct srv_copychunk *)&ci_req->Chunks[0]; chunk_count = le32_to_cpu(ci_req->ChunkCount); if (chunk_count == 0) goto out; @@ -7559,12 +7732,12 @@ static int fsctl_copychunk(struct ksmbd_work *work, /* verify the SRV_COPYCHUNK_COPY packet */ if (chunk_count > ksmbd_server_side_copy_max_chunk_count() || - input_count < offsetof(struct copychunk_ioctl_req, Chunks) + - chunk_count * sizeof(struct srv_copychunk)) { + input_count < struct_size(ci_req, Chunks, chunk_count)) { rsp->hdr.Status = STATUS_INVALID_PARAMETER; return -EINVAL; } + chunks = &ci_req->Chunks[0]; for (i = 0; i < chunk_count; i++) { if (le32_to_cpu(chunks[i].Length) == 0 || le32_to_cpu(chunks[i].Length) > ksmbd_server_side_copy_max_chunk_size()) @@ -7579,11 +7752,11 @@ static int fsctl_copychunk(struct ksmbd_work *work, } src_fp = ksmbd_lookup_foreign_fd(work, - le64_to_cpu(ci_req->ResumeKey[0])); + le64_to_cpu(ci_req->SourceKeyU64[0])); dst_fp = ksmbd_lookup_fd_slow(work, volatile_id, persistent_id); ret = -EINVAL; if (!src_fp || - src_fp->persistent_id != le64_to_cpu(ci_req->ResumeKey[1])) { + src_fp->persistent_id != le64_to_cpu(ci_req->SourceKeyU64[1])) { rsp->hdr.Status = STATUS_OBJECT_NAME_NOT_FOUND; goto out; } @@ -7672,7 +7845,10 @@ static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, if (netdev->type == ARPHRD_LOOPBACK) continue; - flags = dev_get_flags(netdev); + if (!ksmbd_find_netdev_name_iface_list(netdev->name)) + continue; + + flags = netif_get_flags(netdev); if (!(flags & IFF_RUNNING)) continue; ipv6_retry: @@ -7688,9 +7864,9 @@ ipv6_retry: nii_rsp->Capability = 0; if (netdev->real_num_tx_queues > 1) - nii_rsp->Capability |= cpu_to_le32(RSS_CAPABLE); + nii_rsp->Capability |= RSS_CAPABLE; if (ksmbd_rdma_capable_netdev(netdev)) - nii_rsp->Capability |= cpu_to_le32(RDMA_CAPABLE); + nii_rsp->Capability |= RDMA_CAPABLE; nii_rsp->Next = cpu_to_le32(152); nii_rsp->Reserved = 0; @@ -7716,13 +7892,13 @@ ipv6_retry: if (!ipv4_set) { struct in_device *idev; - sockaddr_storage->Family = cpu_to_le16(INTERNETWORK); + sockaddr_storage->Family = INTERNETWORK; sockaddr_storage->addr4.Port = 0; idev = __in_dev_get_rtnl(netdev); if (!idev) continue; - sockaddr_storage->addr4.IPv4address = + sockaddr_storage->addr4.IPv4Address = idev_ipv4_address(idev); nbytes += sizeof(struct network_interface_info_ioctl_rsp); ipv4_set = true; @@ -7730,9 +7906,9 @@ ipv6_retry: } else { struct inet6_dev *idev6; struct inet6_ifaddr *ifa; - __u8 *ipv6_addr = sockaddr_storage->addr6.IPv6address; + __u8 *ipv6_addr = sockaddr_storage->addr6.IPv6Address; - sockaddr_storage->Family = cpu_to_le16(INTERNETWORKV6); + sockaddr_storage->Family = INTERNETWORKV6; sockaddr_storage->addr6.Port = 0; sockaddr_storage->addr6.FlowInfo = 0; @@ -7796,7 +7972,7 @@ static int fsctl_validate_negotiate_info(struct ksmbd_conn *conn, goto err_out; } - neg_rsp->Capabilities = cpu_to_le32(conn->vals->capabilities); + neg_rsp->Capabilities = cpu_to_le32(conn->vals->req_capabilities); memset(neg_rsp->Guid, 0, SMB2_CLIENT_GUID_SIZE); neg_rsp->SecurityMode = cpu_to_le16(conn->srv_sec_mode); neg_rsp->Dialect = cpu_to_le16(conn->dialect); @@ -7934,8 +8110,8 @@ static int fsctl_request_resume_key(struct ksmbd_work *work, return -ENOENT; memset(key_rsp, 0, sizeof(*key_rsp)); - key_rsp->ResumeKey[0] = req->VolatileFileId; - key_rsp->ResumeKey[1] = req->PersistentFileId; + key_rsp->ResumeKeyU64[0] = req->VolatileFileId; + key_rsp->ResumeKeyU64[1] = req->PersistentFileId; ksmbd_fd_put(work, fp); return 0; @@ -7957,6 +8133,8 @@ int smb2_ioctl(struct ksmbd_work *work) int ret = 0; char *buffer; + ksmbd_debug(SMB, "Received smb2 ioctl request\n"); + if (work->next_smb2_rcv_hdr_off) { req = ksmbd_req_buf_next(work); rsp = ksmbd_resp_buf_next(work); @@ -7974,7 +8152,7 @@ int smb2_ioctl(struct ksmbd_work *work) id = req->VolatileFileId; if (req->Flags != cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL)) { - rsp->hdr.Status = STATUS_NOT_SUPPORTED; + ret = -EOPNOTSUPP; goto out; } @@ -7994,8 +8172,9 @@ int smb2_ioctl(struct ksmbd_work *work) case FSCTL_DFS_GET_REFERRALS: case FSCTL_DFS_GET_REFERRALS_EX: /* Not support DFS yet */ + ret = -EOPNOTSUPP; rsp->hdr.Status = STATUS_FS_DRIVER_REQUIRED; - goto out; + goto out2; case FSCTL_CREATE_OR_GET_OBJECT_ID: { struct file_object_buf_type1_ioctl_rsp *obj_buf; @@ -8076,7 +8255,7 @@ int smb2_ioctl(struct ksmbd_work *work) goto out; } - if (in_buf_len < sizeof(struct copychunk_ioctl_req)) { + if (in_buf_len <= sizeof(struct copychunk_ioctl_req)) { ret = -EINVAL; goto out; } @@ -8285,8 +8464,10 @@ out: rsp->hdr.Status = STATUS_BUFFER_TOO_SMALL; else if (ret < 0 || rsp->hdr.Status == 0) rsp->hdr.Status = STATUS_INVALID_PARAMETER; + +out2: smb2_set_err_rsp(work); - return 0; + return ret; } /** @@ -8396,11 +8577,6 @@ static void smb20_oplock_break_ack(struct ksmbd_work *work) goto err_out; } - opinfo->op_state = OPLOCK_STATE_NONE; - wake_up_interruptible_all(&opinfo->oplock_q); - opinfo_put(opinfo); - ksmbd_fd_put(work, fp); - rsp->StructureSize = cpu_to_le16(24); rsp->OplockLevel = rsp_oplevel; rsp->Reserved = 0; @@ -8408,16 +8584,15 @@ static void smb20_oplock_break_ack(struct ksmbd_work *work) rsp->VolatileFid = volatile_id; rsp->PersistentFid = persistent_id; ret = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_oplock_break)); - if (!ret) - return; - + if (ret) { err_out: + smb2_set_err_rsp(work); + } + opinfo->op_state = OPLOCK_STATE_NONE; wake_up_interruptible_all(&opinfo->oplock_q); - opinfo_put(opinfo); ksmbd_fd_put(work, fp); - smb2_set_err_rsp(work); } static int check_lease_state(struct lease *lease, __le32 req_state) @@ -8547,11 +8722,6 @@ static void smb21_lease_break_ack(struct ksmbd_work *work) } lease_state = lease->state; - opinfo->op_state = OPLOCK_STATE_NONE; - wake_up_interruptible_all(&opinfo->oplock_q); - atomic_dec(&opinfo->breaking_cnt); - wake_up_interruptible_all(&opinfo->oplock_brk); - opinfo_put(opinfo); rsp->StructureSize = cpu_to_le16(36); rsp->Reserved = 0; @@ -8560,29 +8730,31 @@ static void smb21_lease_break_ack(struct ksmbd_work *work) rsp->LeaseState = lease_state; rsp->LeaseDuration = 0; ret = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_lease_ack)); - if (!ret) - return; - + if (ret) { err_out: + smb2_set_err_rsp(work); + } + + opinfo->op_state = OPLOCK_STATE_NONE; wake_up_interruptible_all(&opinfo->oplock_q); atomic_dec(&opinfo->breaking_cnt); wake_up_interruptible_all(&opinfo->oplock_brk); - opinfo_put(opinfo); - smb2_set_err_rsp(work); } /** * smb2_oplock_break() - dispatcher for smb2.0 and 2.1 oplock/lease break * @work: smb work containing oplock/lease break command buffer * - * Return: 0 + * Return: 0 on success, otherwise error */ int smb2_oplock_break(struct ksmbd_work *work) { struct smb2_oplock_break *req; struct smb2_oplock_break *rsp; + ksmbd_debug(SMB, "Received smb2 oplock break acknowledgment request\n"); + WORK_BUFFERS(work, req, rsp); switch (le16_to_cpu(req->StructureSize)) { @@ -8597,6 +8769,7 @@ int smb2_oplock_break(struct ksmbd_work *work) le16_to_cpu(req->StructureSize)); rsp->hdr.Status = STATUS_INVALID_PARAMETER; smb2_set_err_rsp(work); + return -EINVAL; } return 0; @@ -8606,24 +8779,26 @@ int smb2_oplock_break(struct ksmbd_work *work) * smb2_notify() - handler for smb2 notify request * @work: smb work containing notify command buffer * - * Return: 0 + * Return: 0 on success, otherwise error */ int smb2_notify(struct ksmbd_work *work) { struct smb2_change_notify_req *req; struct smb2_change_notify_rsp *rsp; + ksmbd_debug(SMB, "Received smb2 notify\n"); + WORK_BUFFERS(work, req, rsp); if (work->next_smb2_rcv_hdr_off && req->hdr.NextCommand) { rsp->hdr.Status = STATUS_INTERNAL_ERROR; smb2_set_err_rsp(work); - return 0; + return -EIO; } smb2_set_err_rsp(work); rsp->hdr.Status = STATUS_NOT_IMPLEMENTED; - return 0; + return -EOPNOTSUPP; } /** @@ -8678,9 +8853,8 @@ int smb2_check_sign_req(struct ksmbd_work *work) iov[0].iov_base = (char *)&hdr->ProtocolId; iov[0].iov_len = len; - if (ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, 1, - signature)) - return 0; + ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, 1, + signature); if (memcmp(signature, signature_req, SMB2_SIGNATURE_SIZE)) { pr_err("bad smb2 signature\n"); @@ -8713,9 +8887,9 @@ void smb2_set_sign_rsp(struct ksmbd_work *work) iov = &work->iov[work->iov_idx]; } - if (!ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, n_vec, - signature)) - memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE); + ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, n_vec, + signature); + memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE); } /** @@ -8891,7 +9065,7 @@ int smb3_encrypt_resp(struct ksmbd_work *work) int rc = -ENOMEM; void *tr_buf; - tr_buf = kzalloc(sizeof(struct smb2_transform_hdr) + 4, GFP_KERNEL); + tr_buf = kzalloc(sizeof(struct smb2_transform_hdr) + 4, KSMBD_DEFAULT_GFP); if (!tr_buf) return rc; @@ -8940,6 +9114,7 @@ int smb3_decrypt_req(struct ksmbd_work *work) le64_to_cpu(tr_hdr->SessionId)); return -ECONNABORTED; } + ksmbd_user_session_put(sess); iov[0].iov_base = buf; iov[0].iov_len = sizeof(struct smb2_transform_hdr) + 4; |
